数据结构 05 栈和队列
1 栈只在一端(栈顶)进行运算,并非两端
栈是只允许在表的一端(栈顶)进行插入和删除操作的线性表;队列是只允许在表的一端(队尾)进行插入操作,在另一端(队头)进行删除操作的线性表。
所以栈只在一端(栈顶)进行运算,并非两端。
2 循环队列不是以链表指针的方式来实现头尾相接
循环队列的典型实现是基于数组的,通过对数组下标进行特定的运算(如取模)来实现队列的头尾相接,从而重复利用数组空间。
虽然从广义的 “指针” 概念(比如数组下标也可看作一种地址标识),但通常我们所说的用 “指针” 实现,更多是指像链表那样用指针变量来链接节点。
而循环队列一般不用这种链表指针的方式来实现头尾相接,所以这一说法错误,答案选 “×”。
3 队列只允许在一端(队尾)进行插入(增加)操作,在另一端(队头)进行删除(减少)操作
队列是一种 “先进先出”(FIFO)的线性表,只允许在一端(队尾)进行插入(增加)操作,在另一端(队头)进行删除(减少)操作,并非下端和上端既能增加又能减少。
4 当有 n 个元素依次进栈时,合法的出栈序列数目恰好是第 n 个卡特兰数。
5 在单链表表示的链式队列里,队头位于链表的链头位置
队列是 “先进先出” 的线性结构,链式队列中,队头元素是最先进入队列的,在单链表表示的链式队列里,队头位于链表的链头位置,这样出队(删除队头元素)操作可以直接从链头进行,方便高效;而队尾位于链尾,用于入队操作。所以用单链表表示的链式队列的队头在链表的链头位置。
6 只有尾指针的循环链队列的入队和出队
以下是用 C 语言实现只有尾指针的循环链队列的入队和出队操作:
#include <stdio.h>
#include <stdlib.h>// 定义队列节点结构
typedef struct Node {int data;struct Node *next;
} Node;// 入队操作
void enQueue(Node **rear, int value) {// 创建新节点Node *newNode = (Node *)malloc(sizeof(Node));if (newNode == NULL) {printf("内存分配失败\n");exit(1);}newNode->data = value;// 如果队列为空if (*rear == NULL) {*rear = newNode;newNode->next = newNode; // 自己指向自己,形成循环} else {// 新节点的 next 指向原队头(尾指针的 next 指向队头)newNode->next = (*rear)->next;// 尾节点的 next 指向新节点(*rear)->next = newNode;// 尾指针后移到新节点*rear = newNode;}
}// 出队操作
int deQueue(Node **rear) {// 队列为空的情况if (*rear == NULL) {printf("队列为空,无法出队\n");exit(1);}Node *front = (*rear)->next; // 队头节点int data = front->data;// 如果队列中只有一个节点if (front == *rear) {*rear = NULL;} else {// 尾指针的 next 指向原队头的下一个节点,即新的队头(*rear)->next = front->next;}free(front);return data;
}// 测试函数
int main() {Node *rear = NULL;// 入队enQueue(&rear, 1);enQueue(&rear, 2);enQueue(&rear, 3);// 出队并打印printf("出队元素: %d\n", deQueue(&rear));printf("出队元素: %d\n", deQueue(&rear));printf("出队元素: %d\n", deQueue(&rear));// 再次入队测试enQueue(&rear, 4);printf("出队元素: %d\n", deQueue(&rear));return 0;
}
代码说明
- 入队操作(
enQueue
):- 首先创建新节点存储要入队的数据。
- 如果队列为空(尾指针
rear
为NULL
),则新节点的next
指向自身,尾指针指向新节点,形成只有一个节点的循环队列。 - 如果队列不为空,新节点的
next
指向原队头(尾指针的next
指向队头),然后尾节点的next
指向新节点,最后尾指针后移到新节点。
- 出队操作(
deQueue
):- 先判断队列是否为空,为空则无法出队。
- 找到队头节点(尾指针的
next
指向队头),保存队头节点的数据。 - 如果队列中只有一个节点,出队后队列为空,尾指针置为
NULL
。 - 否则,尾指针的
next
指向原队头的下一个节点(即新的队头),然后释放原队头节点的内存,返回其数据。
- 测试部分:在
main
函数中进行入队和出队操作的测试,验证代码的正确性。
7 实现两个共享存储区、栈顶相向增长的顺序栈的入栈和出栈
1. 定义栈结构
#define maxsize 100 // 假设最大容量为 100
typedef struct {int data[maxsize]; // 共享的存储数组int top1; // 栈 S1 的栈顶指针,初始为 -1int top2; // 栈 S2 的栈顶指针,初始为 maxsize
} DoubleStack;
2. 入栈操作
// 入栈操作,stack 为双栈,x 为入栈元素,stackNum 为栈编号(1 表示 S1,2 表示 S2)
int push(DoubleStack *stack, int x, int stackNum) {// 检查是否栈满(两个栈顶指针相邻,说明空间用尽)if (stack->top1 + 1 == stack->top2) {printf("栈满,无法入栈\n");return 0;}if (stackNum == 1) {// S1 入栈,栈顶指针上移,元素放入对应位置stack->data[++(stack->top1)] = x;} else if (stackNum == 2) {// S2 入栈,栈顶指针下移,元素放入对应位置stack->data[--(stack->top2)] = x;} else {printf("栈编号错误\n");return 0;}return 1;
}
3. 出栈操作
// 出栈操作,stack 为双栈,x 用于存储出栈元素,stackNum 为栈编号(1 表示 S1,2 表示 S2)
int pop(DoubleStack *stack, int *x, int stackNum) {if (stackNum == 1) {// 检查 S1 是否为空if (stack->top1 == -1) {printf("S1 栈空,无法出栈\n");return 0;}// S1 出栈,栈顶指针下移,取出元素*x = stack->data[stack->top1--];} else if (stackNum == 2) {// 检查 S2 是否为空if (stack->top2 == maxsize) {printf("S2 栈空,无法出栈\n");return 0;}// S2 出栈,栈顶指针上移,取出元素*x = stack->data[stack->top2++];} else {printf("栈编号错误\n");return 0;}return 1;
}
简单解释
- 入栈:两个栈
S1
和S2
栈顶相向,S1
栈顶从存储区起始位置(top1
初始为 -1)向上增长,S2
栈顶从存储区末尾位置(top2
初始为maxsize
)向下增长。入栈时先判断是否栈满(top1 + 1 == top2
表示空间用尽),若不满则根据栈编号,调整对应栈顶指针并放入元素。 - 出栈:出栈时先判断对应栈是否为空,若不为空则根据栈编号,调整对应栈顶指针并取出元素。