栈和队列
栈
栈(Stack)是限定仅在表尾进行插入或删除的线性表。因此对站来说,表尾端有其特殊含义,称为栈顶(Top)。不含元素的空表称为空栈。
- 核心规则:
后进先出(LIFO,Last In First Out),仅允许在「栈顶」进行插入(入栈)和删除(出栈)操作。
栈是限制插入和删除操作只能在一个位置进行的表,该位置是表的末端,叫栈顶(top)。对栈的基本操作有进栈(push)和出栈(pop),前者相当于插入,后者则是删除最后插入的元素。
- 实现方式:
- 数组实现:用数组存储数据,维护
top指针指向栈顶,入栈 / 出栈仅需移动指针(时间复杂度 O (1)),但有容量限制; - 链表实现:用单链表头插 / 头删模拟栈顶操作,无容量限制,灵活度更高。
- 数组实现:用数组存储数据,维护
- 典型应用:表达式求值、函数递归调用、括号匹配、撤销操作。
栈的顺序结构实现
定义
#define MAXSIZE 100
typedef int ElemType;
typedef struct {
ElemType data[MAXSIZE];
int top;
}Stack;
初始化
void initStack(Stack* s) {
s->top = -1;//栈顶指针置为-1,标识空栈
}
判断栈是否为空
int isEmpty(Stack* s) {
if (s->top == -1) {
printf("空的\n");
return 1;
}
else {
return 0;
}
}
进栈/压栈
- 入栈:
top++,数组 [top] = 元素;
int push(Stack* s, ElemType e) {
if (s->top >= MAXSIZE - 1) {
printf("满了\n");
return 0;
}
s->top++;
s->data[s->top] = e;
return 1;
}
出栈
- 出栈:返回数组 [top],
top--;
ElemType pop(Stack* s, ElemType* e) {
if (s->top == -1) {
printf("空的\n");
return 0;
}
*e = s->data[s->top];
s->top--;
return 1;
}
获取栈顶元素
int getTop(Stack* s, ElemType* e) {
if (s->top == -1) {
printf("空的\n");
return 0;
}
*e = s->data[s->top];
return 1;
}
完整代码:
#include<stdio.h>
#define MAXSIZE 100
typedef int ElemType;
typedef struct {
ElemType data[MAXSIZE];
int top;
}Stack;
// 初始化
void initStack(Stack* s) {
s->top = -1;//栈顶指针置为-1,标识空栈
}
// 判断栈是否为空
int isEmpty(Stack* s) {
if (s->top == -1) {
printf("空的\n");
return 1;
}
else {
return 0;
}
}
//进栈
int push(Stack* s, ElemType e) {
if (s->top >= MAXSIZE - 1) {
printf("满了\n");
return 0;
}
s->top++;
s->data[s->top] = e;
return 1;
}
//出栈
ElemType pop(Stack* s, ElemType* e) {
if (s->top == -1) {
printf("空的\n");
return 0;
}
*e = s->data[s->top];
s->top--;
return 1;
}
// 获取栈顶元素
int getTop(Stack* s, ElemType* e) {
if (s->top == -1) {
printf("空的\n");
return 0;
}
*e = s->data[s->top];
return 1;
}
int main() {
Stack s;
initStack(&s);
push(&s, 10);
push(&s, 20);
push(&s, 30);
ElemType e;
pop(&s, &e);
printf("%d\n", e);//30
getTop(&s, &e);
printf("%d\n", e);//20
return 0;
}
动态内存分配
#define MAXSIZE 100
typedef int ElemType;
typedef struct {
ElemType *data;
int top;
}Stack;
Stack* initStack() {
Stack *s = (Stack*)malloc(sizeof(Stack));
s->data = (ElemType*)malloc(sizeof(ElemType));
s->top = -1;
return s;
}
完整代码:
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100
typedef int ElemType;
typedef struct {
ElemType *data;
int top;
}Stack;
// 判断栈是否为空
int isEmpty(Stack* s) {
if (s->top == -1) {
printf("空的\n");
return 1;
}
else {
return 0;
}
}
//进栈
int push(Stack* s, ElemType e) {
if (s->top >= MAXSIZE - 1) {
printf("满了\n");
return 0;
}
s->top++;
s->data[s->top] = e;
return 1;
}
//出栈
ElemType pop(Stack* s, ElemType* e) {
if (s->top == -1) {
printf("空的\n");
return 0;
}
*e = s->data[s->top];
s->top--;
return 1;
}
// 获取栈顶元素
int getTop(Stack* s, ElemType* e) {
if (s->top == -1) {
printf("空的\n");
return 0;
}
*e = s->data[s->top];
return 1;
}
Stack* initStack() {
Stack *s = (Stack*)malloc(sizeof(Stack));
s->data = (ElemType*)malloc(sizeof(ElemType));
s->top = -1;
return s;
}
int main() {
Stack* s = initStack();
push(s, 10);
push(s, 20);
push(s, 30);
ElemType e;
pop(s, &e);
printf("%d\n", e);//30
getTop(s, &e);
printf("%d\n",e);//20
return 0;
}
栈的链表结构实现–与链表类似
| 普通单链表 | 链式栈 |
|---|---|
| 头节点(Head) | 栈顶节点(Top) |
| 节点(Node) | 栈元素节点 |
| 尾节点(Tail) | 栈底节点 |
| 链表长度(Size) | 栈的大小(Size) |
定义
typedef struct stack {
ElemType data;
struct stack * next;
}Stack;
初始化
Stack* initStack() {
Stack* s = (Stack*)malloc(sizeof(Stack));
s->data = 0;
s->next = NULL;
return s;
}
判断栈是否为空
int isEmpty(Stack* s) {
if (s->next == NULL) {
printf("空的\n");
return 1;
}
else {
return 0;
}
}
进栈/压栈—头插法
int push(Stack* s, ElemType e) {
Stack* p = (Stack*)malloc(sizeof(Stack));
p->data = e;
p->next = s->next;
s->next = p;
return 1;
}
出栈–删除头节点后的有效节点
int pop(Stack* s, ElemType *e) {
if (s->next == NULL) {
printf("空的\n");
return 0;
}
*e = s->next->data;
Stack* p = s->next;
s->next = p->next;
free(p);
return 1;
}
获取栈顶元素—获取第一个有效节点的值
int getTop(Stack* s, ElemType* e) {
if (s->next == NULL) {
return 0;
}
*e = s->next->data;
return 1;
}
完整代码:
#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
typedef struct stack {
ElemType data;
struct stack * next;
}Stack;
Stack* initStack() {
Stack* s = (Stack*)malloc(sizeof(Stack));
s->data = 0;
s->next = NULL;
return s;
}
int isEmpty(Stack* s) {
if (s->next == NULL) {
printf("空的\n");
return 1;
}
else {
return 0;
}
}
//进栈--头插法
int push(Stack* s, ElemType e) {
Stack* p = (Stack*)malloc(sizeof(Stack));
p->data = e;
p->next = s->next;
s->next = p;
return 1;
}
//出栈---获取第一个有效节点的值
int pop(Stack* s, ElemType *e) {
if (s->next == NULL) {
printf("空的\n");
return 0;
}
*e = s->next->data;
Stack* p = s->next;
s->next = p->next;
free(p);
return 1;
}
//获取栈顶元素
int getTop(Stack* s, ElemType* e) {
if (s->next == NULL) {
return 0;
}
*e = s->next->data;
return 1;
}
int main() {
Stack *s=initStack();
push(s, 10);
push(s, 20);
push(s, 30);
ElemType e;
pop(s, &e);
printf("%d\n", e);//30
getTop(s, &e);
printf("%d\n", e);//20
return 0;
}
队列
队列(Queue)是一种 “先进先出(FIFO, First In First Out)” 的线性表。它只允许在表的一端进行插入,而在另一端删除元素。
在队列中,允许插入的一段称为队尾(rear),允许删除的一端称为队头(front)。
假设队列为q=(a
1,a2,a3,···,an),那么a1是队头元素,an是队尾元素。
入队(Enqueue):将元素添加到队尾;
出队(Dequeue):从队首移除元素(需先判断队列非空);
队列的顺序结构实现
定义
#define MAXSIZE 100
typedef int ElemType;
typedef struct {
ElemType data[MAXSIZE];
int front;
int rear;
}Queue;
初始化
void initQueue(Queue* Q) {
Q->front = 0;
Q->rear = 0;
}
判断队列是否为空
当队头和队尾相等时,队列为空
int isEmpty(Queue* Q) {
if (Q->front == Q->rear) {
printf("队列为空\n");
return 1;
}
else {
return 0;
}
}
出队
出队就是删除内容,但对于顺序结构来说,是将下标诺一个位置。
//方法1
ElemType dequeue1(Queue* Q) {
if (Q->front == Q->rear) {
printf("队列为空\n");
return 0;
}
ElemType e = Q->data[Q->front];
Q->front++;
return e;
}
//方法2
int dequeue2(Queue* Q, ElemType* e) {
入队
//队尾满了,调整队形
int queueFull(Queue* Q) {
if (Q->front > 0) {
int step = Q->front;//step是队列前的空位个数
for (int i = Q->front; i <= Q->rear; ++i) {
Q->data[i - step] = Q->data[i];//将队列向前复制
}
Q->front = 0;//将队头移到0出
Q->rear = Q->rear - step;//移动队尾,空出空间
return 1;
}
else {
printf("真的满了\n");
return 0;
}
}
//入队
int enqueue(Queue* Q, ElemType e) {
if (Q->rear >= MAXSIZE) {
if (!queueFull(Q)) {
return 0;
}
}
Q->data[Q->rear] = e;
Q->rear++;
return 1;
}
假溢出队尾指针rear触达数组容量上限(MAXSIZE),导致无法继续入队;而队首front前仍有大量空闲空间(因出队操作让front后移),这些空间无法被复用,看似 “满了” 实则是 “假满”。如下图所示情况,此时用queueFull函数可以调整队形,再判断队伍是否可以再插入数据。
获取队头数据
//方法1
ElemType getFront(Queue* Q) {
if (Q->front == Q->rear) {
printf("队列为空\n");
return 0;
}
ElemType e = Q->data[Q->front];
return e;
}
//方法2
int getFront(Queue* Q, ElemType* e) {
if (Q->front == Q->rear) {
printf("队列为空\n");
return 0;
}
*e = Q->data[Q->front];
return 1;
}
遍历
void PrintQueue(Queue* Q) {
if (Q->front == Q->rear) {
printf("队列为空\n");
return;
}
for (int i = Q->front; i < Q->rear; i++) {
printf("%d ", Q->data[i]);
}
printf("\n");
}
完整代码:
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100
typedef int ElemType;
typedef struct {
ElemType data[MAXSIZE];
int front;
int rear;
}Queue;
//初始化
void initQueue(Queue* Q) {
Q->front = 0;
Q->rear = 0;
}
//判断队列是否为空
int isEmpty(Queue* Q) {
if (Q->front == Q->rear) {
printf("队列为空\n");
return 1;
}
else {
return 0;
}
}
//出队
ElemType dequeue(Queue* Q) {
if (Q->front == Q->rear) {
printf("队列为空\n");
return 0;
}
ElemType e = Q->data[Q->front];
Q->front++;
return e;
}
//队尾满了,调整队形
int queueFull(Queue* Q) {
if (Q->front > 0) {
int step = Q->front;//step是空位
for (int i = Q->front; i <= Q->rear; ++i) {
Q->data[i - step] = Q->data[i];
}
// 更新指针
Q->front = 0;
Q->rear = Q->rear - step;
return 1;
}
else {
printf("真的满了\n");
return 0;
}
}
//入队
int enqueue(Queue* Q, ElemType e) {
if (Q->rear >= MAXSIZE) {
if (!queueFull(Q)) {
return 0;
}
}
Q->data[Q->rear] = e;
Q->rear++;
return 1;
}
//获取队头数据
ElemType getFront(Queue* Q) {
if (Q->front == Q->rear) {
printf("队列为空\n");
return 0;
}
ElemType e = Q->data[Q->front];
return e;
}
//打印队列内所有元素
void PrintQueue(Queue* Q) {
if (Q->front == Q->rear) {
printf("队列为空\n");
return;
}
for (int i = Q->front; i < Q->rear; i++) {
printf("%d ", Q->data[i]);
}
printf("\n");
}
int main() {
Queue Q;
initQueue(&Q);
// 入队测试
enqueue(&Q, 10);
enqueue(&Q, 20);
enqueue(&Q, 30);
enqueue(&Q, 40);
printf("入队后:");
PrintQueue(&Q); // 输出:10 20 30 40
// 出队测试
ElemType e1=dequeue(&Q);
printf("出队元素:%d\n", e1);// 输出:10
// 获取队头测试
ElemType e2=getFront(&Q);
printf("当前队头:%d\n", e2);// 输出:20
// 打印调整后的队列
printf("操作后队列:");
PrintQueue(&Q); // 输出:20 30 40
return 0;
}
动态内存分配
#define MAXSIZE 100
typedef int ElemType;
typedef struct queue{
ElemType *data;
int front;
int rear;
}Queue;
//初始化
Queue* initQueue(Queue* Q) {
Queue* q = (Queue*)malloc(sizeof(Queue));
q->data = (ElemType*)malloc(sizeof(ElemType));
q->front = 0;
q->rear = 0;
return q;
}
在入栈时,用queueFull函数解决假溢出现象时,Q->data[i - step] = Q->data[i];一个一个向前移动,如果数据很多,效率很低,怎样解决这个问题呢?———循环队列
循环队列
- 队列空:
front == rear(与普通队列一致); - 队列满:
(rear + 1) % MAXSIZE == front(预留 1 个位置,避免与空队列混淆); - 队列有效元素个数:
(rear - front + MAXSIZE) % MAXSIZE。
指针移动规则
- 入队:
rear = (rear + 1) % MAXSIZE; - 出队:
front = (front + 1) % MAXSIZE;
入队
int enqueue(Queue* Q, ElemType e) {
if ((Q->rear + 1) % MAXSIZE == Q->front) {
printf("满了\n");
return 0;
}
Q->data[Q->rear] = e;
Q->rear = (Q->rear + 1) % MAXSIZE;
return 1;
}
循环队列有一个小问题,当是下列情况时(Q->rear + 1) % MAXSIZE= Q->front,无法插入数据,牺牲一个位置,只损失一个存储位。
出队
int deQueue(Queue* Q, ElemType *e) {
if (Q->front == Q->rear) {
printf("队列为空\n");
return 0;
}
*e = Q->data[Q->front];
Q->front = (Q->front + 1) % MAXSIZE;
return 1;
}
遍历
void PrintQueue(Queue* Q) {
if (Q->front == Q->rear) {
printf("队列为空\n");
return;
}
for (int i = Q->front; i != Q->rear; i=(i+1)%MAXSIZE) {
printf("%d ", Q->data[i]);
}
printf("\n");
}
完整代码:
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 8
typedef int ElemType;
typedef struct {
ElemType data[MAXSIZE];
int front;
int rear;
}Queue;
//初始化
void initQueue(Queue* Q) {
Q->front = 0;
Q->rear = 0;
}
//判断队列是否为空
int isEmpty(Queue* Q) {
if (Q->front == Q->rear) {
printf("队列为空\n");
return 1;
}
else {
return 0;
}
}
//出队
//ElemType dequeue(Queue* Q) {
// if (Q->front == Q->rear) {
// printf("队列为空\n");
// return 0;
// }
// ElemType e = Q->data[Q->front];
// Q->front++;
// return e;
//}
////队尾满了,调整队形
//int queueFull(Queue* Q) {
// if (Q->front > 0) {
// int step = Q->front;//step是空位
// for (int i = Q->front; i <= Q->rear; ++i) {
// Q->data[i - step] = Q->data[i];
// }
// // 更新指针
// Q->front = 0;
// Q->rear = Q->rear - step;
// return 1;
// }
// else {
// printf("真的满了\n");
// return 0;
// }
//}
//
////入队
//int enqueue(Queue* Q, ElemType e) {
// if (Q->rear >= MAXSIZE) {
// if (!queueFull(Q)) {
// return 0;
// }
// }
// Q->data[Q->rear] = e;
// Q->rear++;
// return 1;
//
//}
//
//入队
int enqueue(Queue* Q, ElemType e) {
if ((Q->rear + 1) % MAXSIZE == Q->front) {
printf("满了\n");
return 0;
}
Q->data[Q->rear] = e;
Q->rear = (Q->rear + 1) % MAXSIZE;
return 1;
}
//出队
int dequeue(Queue* Q, ElemType *e) {
if (Q->front == Q->rear) {
printf("队列为空\n");
return 0;
}
*e = Q->data[Q->front];
Q->front = (Q->front + 1) % MAXSIZE;
return 1;
}
//获取队头数据
ElemType getFront(Queue* Q) {
if (Q->front == Q->rear) {
printf("队列为空\n");
return 0;
}
ElemType e = Q->data[Q->front];
return e;
}
//打印队列内所有元素
void PrintQueue(Queue* Q) {
if (Q->front == Q->rear) {
printf("队列为空\n");
return;
}
for (int i = Q->front; i != Q->rear; i=(i+1)%MAXSIZE) {
printf("%d ", Q->data[i]);
}
printf("\n");
}
int main() {
Queue Q;
initQueue(&Q);
// 入队测试
enqueue(&Q, 10);
enqueue(&Q, 20);
enqueue(&Q, 30);
enqueue(&Q, 50);
enqueue(&Q, 60);
enqueue(&Q, 70);
enqueue(&Q, 80);
printf("入队后:");
PrintQueue(&Q);
// 出队测试
ElemType e1;
dequeue(&Q,&e1);
printf("出队元素:%d\n", e1);
// 获取队头测试
ElemType e2=getFront(&Q);
printf("当前队头:%d\n", e2);
// 打印调整后的队列
printf("操作后队列:");
PrintQueue(&Q);
return 0;
}
运行结果:
入队后:10 20 30 50 60 70 80 出队元素:10 当前队头:20 操作后队列:20 30 50 60 70 80
队列的链式结构
定义
typedef int ElemType;
typedef struct QueueNode{
ElemType* data;
struct QueueNode* next;
}QueueNode;
typedef struct {
QueueNode* front;
QueueNode* rear;
}Queue;
初始化
Queue* initQueue() {
Queue* q = (Queue*)malloc(sizeof(Queue));
QueueNode* node = (QueueNode*)malloc(sizeof(QueueNode));
node->data = 0;
node->next = NULL;
//队头队尾指针指向头节点
q->front = node;
q->rear = node;
return q;
}
判断队列是否为空
int isEmpty(Queue* q) {
if (q->front == q->rear) {
return 1;
}
else {
return 0;
}
}
入队
可选择
如果首节点是队尾——头插法
如果尾节点是队尾——尾插法
//尾插法
int enqueue(Queue* q, ElemType e) {
QueueNode* node = (QueueNode*)malloc(sizeof(QueueNode));
node->data = e;
node->next = NULL;
q->rear->next = node;//将node给原来尾指针的next
q->rear = node;//让rear移新的尾节点的位置
return 1;
}
出队
释放第一个有效节点
int dequeue(Queue* q, ElemType* e) {
QueueNode* node = q->front->next;
*e = node->data;
q->front->next = node->next;
if (q->rear == node) {
q->rear = q->front;
}
free(node);
return 1;
}
获取队头元素
ElemType getFront(Queue* q) {
if (isEmpty(q)) {
printf("队列为空\n");
return 0;
}
return q->front->next->data;
}
遍历
void printQueue(Queue* q) {
if (q->front == q->rear) {
printf("队列为空\n");
return;
}
printf("队列元素:");
QueueNode* p = q->front->next;
while (p != NULL) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
完整代码:
#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
typedef struct QueueNode {
ElemType data;
struct QueueNode* next;
}QueueNode;
typedef struct {
QueueNode* front;
QueueNode* rear;
}Queue;
Queue* initQueue() {
Queue* q = (Queue*)malloc(sizeof(Queue));
QueueNode* node = (QueueNode*)malloc(sizeof(QueueNode));
node->data = 0;
node->next = NULL;
//队头队尾指针指向头节点
q->front = node;
q->rear = node;
return q;
}
int isEmpty(Queue* q) {
if (q->front == q->rear) {
return 1;
}
else {
return 0;
}
}
//入队
int enqueue(Queue* q, ElemType e) {
QueueNode* node = (QueueNode*)malloc(sizeof(QueueNode));
node->data = e;
node->next = NULL;
q->rear->next = node;//将node给原来尾指针的next
q->rear = node;//让rear移新的尾节点的位置
return 1;
}
//出队
int dequeue(Queue* q, ElemType* e) {
QueueNode* node = q->front->next;
*e = node->data;
q->front->next = node->next;
if (q->rear == node) {
q->rear = q->front;
}
free(node);
return 1;
}
//获取队头元素
ElemType getFront(Queue* q) {
if (isEmpty(q)) {
printf("队列为空\n");
return 0;
}
return q->front->next->data;
}
//打印
void printQueue(Queue* q) {
if (q->front == q->rear) {
printf("队列为空\n");
return;
}
printf("队列元素:");
QueueNode* p = q->front->next;
while (p != NULL) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
int main() {
Queue* q = initQueue();
enqueue(q, 10);
enqueue(q, 20);
enqueue(q, 30);
enqueue(q, 40);
enqueue(q, 50);
printQueue(q);
ElemType e1;
dequeue(q, &e1);
printf("出队元素:%d\n", e1);
printQueue(q);
ElemType e2;
dequeue(q, &e2);
printf("出队元素:%d\n", e2);
printQueue(q);
printf("%d\n", getFront(q));
return 0;
}
运行结果:
队列元素:10 20 30 40 50 出队元素:10 队列元素:20 30 40 50 出队元素:20 队列元素:30 40 50 30
双端队列
双端队列(Deque,全称 Double-Ended Queue)是队列的进阶变种,核心特性是队首(Front)和队尾(Rear)两端均可执行入队、出队操作
- 两头都能进、两头都能出
- 两头能进、一头都能出
- 一头都进、两头能出