数据结构(C语言版)栈和队列

109 阅读13分钟

栈和队列

(Stack)是限定仅在表尾进行插入或删除的线性表。因此对站来说,表尾端有其特殊含义,称为栈顶(Top)。不含元素的空表称为空栈。

image-20251207150826356.png

  • 核心规则后进先出(LIFO,Last In First Out),仅允许在「栈顶」进行插入(入栈)和删除(出栈)操作。

image-20251207150914487.png

栈是限制插入和删除操作只能在一个位置进行的表,该位置是表的末端,叫栈顶(top)。对栈的基本操作有进栈(push)和出栈(pop),前者相当于插入,后者则是删除最后插入的元素。

  • 实现方式:
    1. 数组实现:用数组存储数据,维护top指针指向栈顶,入栈 / 出栈仅需移动指针(时间复杂度 O (1)),但有容量限制;
    2. 链表实现:用单链表头插 / 头删模拟栈顶操作,无容量限制,灵活度更高。
  • 典型应用:表达式求值、函数递归调用、括号匹配、撤销操作。

栈的顺序结构实现

定义
#define MAXSIZE  100
typedef int ElemType;
typedef struct {
	ElemType data[MAXSIZE];
	int top;
}Stack;

image-20251207151906706.png

初始化
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] = 元素;

image-20251207153206415.png

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--

image-20251207154300624.png

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;

image-20251207160244608.png

初始化
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;
	}
}
进栈/压栈—头插法

image-20251207162327658.png

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=(a1,a2,a3,···,an),那么a1是队头元素,an是队尾元素。

入队(Enqueue):将元素添加到队尾;

image-20251207164506802.png

出队(Dequeue):从队首移除元素(需先判断队列非空);

image-20251207164926059.png

队列的顺序结构实现

定义
#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;
}
判断队列是否为空

当队头和队尾相等时,队列为空

image-20251207170249329.png

int isEmpty(Queue* Q) {
	if (Q->front == Q->rear) {
		printf("队列为空\n");
		return 1;
	}
	else {
		return 0;
	}
}
出队

出队就是删除内容,但对于顺序结构来说,是将下标诺一个位置。

image-20251207171737687.png

//方法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函数可以调整队形,再判断队伍是否可以再插入数据。

image-20251207182034983.png

获取队头数据
//方法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;
}

image-20251207185553216.png

动态内存分配
#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];一个一个向前移动,如果数据很多,效率很低,怎样解决这个问题呢?———循环队列

循环队列

image-20251207191928564.png

  • 队列空: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,无法插入数据,牺牲一个位置,只损失一个存储位。

image-20251207193643886.png

出队
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;
}

image-20251207201118402.png

判断队列是否为空
int isEmpty(Queue* q) {
	if (q->front == q->rear) {
		return 1;
	}
	else {
		return 0;
	}
}
入队

可选择

如果首节点是队尾——头插法

如果尾节点是队尾——尾插法

image-20251207201853140转存失败,建议直接上传图片文件

//尾插法
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)两端均可执行入队、出队操作

  1. 两头都能进、两头都能出
  2. 两头能进、一头都能出
  3. 一头都进、两头能出

image-20251207211255613.png