跳到主要内容数据结构基础:栈与队列的实现原理 | 极客日志C算法
数据结构基础:栈与队列的实现原理
综述由AI生成栈和队列是特殊的线性结构,分别遵循后进先出和先进先出原则。文章详细阐述了顺序栈、链栈的定义与操作,包括初始化、进栈出栈及获取栈顶元素。同时讲解了顺序队列的假溢出问题及其三种解决方案,并对比了带头结点与不带头结点的链式队列实现。掌握这些基础数据结构有助于理解更复杂的算法应用。
DebugKing6 浏览 一、前言
顺序表和链表都是线性结构,栈和队列也是特殊的线性表。顺序表和链表没有所谓的进出限制,但栈只允许在一端进行插入和删除操作,遵循后进先出(LIFO)原则;队列只允许从前面插入,后面出队,遵循先进先出(FIFO)原则。
二、栈

只允许在一端进行插入和删除操作的线性表。空栈表示没有元素,栈顶允许插入和删除,栈底不允许。
2.1 顺序栈
用顺序方式存储的栈称为顺序栈。

顺序栈的定义
使用 top 指针标记栈顶位置,初始化时 top = -1,表示栈为空。
#define MAXSIZE 100
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct {
int data[100];
int top;
} SqStack;
初始化
void InitStack(SqStack *S) {
S->top = -1;
}
bool StackEmpty(SqStack *S) {
(S->top == ) {
;
} {
;
}
}
if
-1
return
true
else
return
false
进栈操作
bool Push(SqStack *S, int x) {
if (S->top == MAXSIZE) {
return false;
}
S->top++;
S->data[S->top] = x;
return true;
}
出栈操作
bool Pop(SqStack *S, int *x) {
if (S->top == -1) {
return false;
}
*x = S->data[S->top];
S->top--;
return true;
}
获取栈顶元素
bool GetTop(SqStack *S, int *x) {
if (S->top == -1) {
return false;
}
*x = S->data[S->top];
return true;
}
2.2 共享栈
共享栈让两个栈共享同一块数组空间,栈底分别在数组的两端,向中间生长。
建立
typedef struct {
int data[100];
int top;
int top1;
} SharedStack;
初始化
初始化时,第一个栈的 top 在 -1,第二个栈的 top1 在最大容量处。
void InitSharedStack(SharedStack *S) {
S->top = -1;
S->top1 = MAXSIZE;
}
当 top + 1 == top1 时,表示栈满。
2.3 链栈
链栈用链表实现,链表的头结点作为栈顶,进栈和出栈操作都能在 O(1) 时间内完成。
建立
typedef struct LinkNode {
int data;
struct LinkNode *next;
} LinkNode;
typedef LinkNode* LinkStack;
这里栈顶指针就是链表的头指针,进栈就是在头结点前插入新节点,出栈就是删除头结点。
三、队列
队列是'先进先出'(FIFO)的,只能在队尾插入(入队),在队头删除(出队)。
顺序队列
顺序队列用数组实现,存在假溢出问题。即 front 和 rear 都会往后移动,可能导致数组前面的空间浪费。
定义
typedef struct {
int data[100];
int front;
int rear;
} SqQueue;
初始化队列
void InitQueue(SqQueue *Q) {
Q->front = Q->rear = 0;
}
判断队列是否为空
bool QueueEmpty(SqQueue *Q) {
if (Q->front == Q->rear) {
return true;
} else {
return false;
}
}
入队操作
bool EnQueue(SqQueue *Q, int x) {
if (Q->rear == MAXSIZE) {
return false;
}
Q->data[Q->rear] = x;
Q->rear++;
return true;
}
出队操作
bool DeQueue(SqQueue *Q, int *x) {
if (Q->front == Q->rear) {
return false;
}
*x = Q->data[Q->front];
Q->front++;
return true;
}
解决假溢出问题
法一:牺牲一个单元
上述方法中 rear 到达数组末尾时,即使前面有空位也会被判为队满。可以通过牺牲一个存储单元来解决,但这通常不是最优解。
法二:增加长度变量
增加一个 size 变量记录队列长度,队满条件是 size == MaxSize,队空条件是 size == 0。
#define MaxSize 10
typedef struct {
int data[10];
int front, rear;
int size;
} SqQueue;
法三:增加 tag 变量
增加一个 tag 变量,记录最近操作是插入(1)还是删除(0)。队满条件是 front == rear && tag == 1,队空条件是 front == rear && tag == 0。
#define MaxSize 10
typedef struct {
int data[10];
int front, rear;
int tag;
} SqQueue;
链式存储实现队列
链式队列用链表实现,队头指针指向头结点,队尾指针指向最后一个节点。
定义一个链式队列
typedef struct LinkNode {
int data;
struct LinkNode *next;
} LinkNode;
typedef struct {
LinkNode *front, *rear;
} LinkQueue;
初始化(带头结点)
void InitQueue(LinkQueue *Q) {
Q->front = Q->rear = (LinkNode *)malloc(sizeof(LinkNode));
Q->front->next = NULL;
}
bool QueueEmpty(LinkQueue *Q) {
if (Q->front == Q->rear) {
return true;
} else {
return false;
}
}
入队(带头结点)
入队就是在队尾添加新节点,然后把 rear 移到新节点。
bool EnQueue(LinkQueue *Q, int x) {
LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));
if (s == NULL) {
return false;
}
s->data = x;
s->next = NULL;
Q->rear->next = s;
Q->rear = s;
return true;
}
入队(不带头结点)
bool EnQueue(LinkQueue *Q, int x) {
LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));
if (s == NULL) {
return false;
}
s->data = x;
s->next = NULL;
if (QueueEmpty(Q)) {
Q->front = s;
Q->rear = s;
} else {
Q->rear->next = s;
Q->rear = s;
}
return true;
}
不带头结点的链式队列入队操作,核心区别在于需要处理'首次入队'的特殊情况。
出队(带头结点)
bool DeQueue(LinkQueue *Q, int *x) {
if (QueueEmpty(Q)) {
return false;
}
LinkNode *p = Q->front->next;
*x = p->data;
Q->front->next = p->next;
if (Q->rear == p) {
Q->rear = Q->front;
}
free(p);
return true;
}
出队(不带头结点)
bool DeQueue(LinkQueue *Q, int *x) {
if (QueueEmpty(Q)) {
return false;
}
LinkNode *p = Q->front;
*x = p->data;
if (Q->front == Q->rear) {
Q->front = Q->rear = NULL;
} else {
Q->front = Q->front->next;
}
free(p);
return true;
}
队列满的条件
- 顺序存储:预分配的空间耗尽时队满。
- 链式存储:一般不会队满,除非内存不足。
四、总结
栈和队列都是特殊的线性表,只是对操作的位置做了限制:
- 栈:只允许在栈顶操作,后进先出。
- 队列:只允许在队尾入队、队头出队,先进先出。
它们的实现可以用数组(顺序存储)或链表(链式存储),各有优缺点:
- 顺序存储:访问快,但大小固定(或需要扩容)。
- 链式存储:大小灵活,但访问需要遍历指针。
掌握它们的逻辑和实现,对于理解更复杂的数据结构至关重要。
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online