跳到主要内容数据结构:栈与队列的实现及应用 | 极客日志C算法
数据结构:栈与队列的实现及应用
综述由AI生成栈(后进先出)和队列(先进先出)的基本概念及结构特点。详细阐述了使用数组实现栈和使用链表实现队列的 C 语言代码逻辑,包括初始化、增删改查及销毁操作。此外,通过括号匹配问题演示了栈的实际应用,并提供了使用两个队列模拟栈结构的具体算法实现方案。
字节跳动11 浏览 1. 栈
1.1. 栈的概念及结构
首先,我们来了解一种新的数据结构——栈。栈是一种特殊的线性表,其只允许在固定一端进行插入和删除元素操作。进行数据的插入和删除这一端,称之为栈顶,另一端即栈底。栈中的数据是采取后进先出 LIFO(Last In First Out)的规则。
压栈:栈的插入操作(也称为进栈/压栈)
出栈:栈的删除操作
栈的导入与导出数据均在栈顶进行。值得注意的是:栈的导出不一定是根据栈的导入时的顺序进行的。
1.2. 栈的实现
我们可以使用数组或链表来实现栈,对数组而言,在数组尾部插入数据代价较小,因为其不需要遍历整个数组。
接下来,我们分析栈的实现。
Stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int STDataType;
typedef struct Stack {
STDataType* a;
int top;
int capacity;
}ST;
void STInit(ST* pst);
void STDestroy(ST* pst);
void STPush(ST* pst, STDataType x);
void STPop(ST* pst);
STDataType STTop(ST* pst);
;
;
bool
STEmpty
(ST* pst)
int
STSize
(ST* pst)
STInit: 对栈进行初始化,该栈使用数组实现,a(数组名),top(指向栈顶位置的下一个位置),capacity(容量)。
#include"stack.h"
void STInit(ST* pst) {
assert(pst);
pst->a = NULL;
pst->top = 0;
pst->capacity = 0;
}
**STPush:**将数据导入栈中,首先,将空间进行扩容(防止空间不足引起的越界访问),并将扩容后的空间传给数组 a,然后,再于新空间进行导入数据的操作,结尾记得改变新栈的个数。
void STPush(ST* pst, STDataType x) {
assert(pst);
if (pst->top == pst->capacity) {
int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
STDataType* tmp = (STDataType*)realloc(pst->a, newcapacity * sizeof(STDataType));
if (tmp == NULL) {
perror("Realloc failed\n");
exit(1);
}
pst->a = tmp;
pst->capacity = newcapacity;
}
pst->a[pst->top] = x;
pst->top++;
}
**STPop:**导出栈中的数据,直接传数组的实参,将其栈的个数减少一位,便导出一个数据。
void STPop(ST* pst) {
assert(pst);
assert(pst->top > 0);
pst->top--;
}
**STTop:**由于 top 指向栈顶的下一个位置,使用我们解引用 top-1 位置,也就是栈顶数据。
STDataType STTop(ST* pst) {
assert(pst);
assert(pst->top > 0);
return pst->a[pst->top - 1];
}
**STEmpty:**通过 return 返回值(bool 型)
bool STEmpty(ST* pst) {
assert(pst);
return pst->top == 0;
}
**STSize:**top 指向栈顶的下一个位置,对应数组的数据个数=下标 +1,top 即栈的数据个数。
int STSize(ST* pst) {
assert(pst);
return pst->top;
}
**STDestroy:**将栈内的数据置空或变为 0。
void STDestroy(ST* pst) {
assert(pst);
free(pst->a);
pst->a = NULL;
pst->top = pst->capacity = 0;
}
2. 队列
2.1. 队列的概念及结构
队列与栈不同的是,队列是一种只允许一端进行插入数据,另一端进行删除数据的特殊线性表。队列采取的是先进先出 FIFO(First In First Out)。入队列,在队尾上进行插入数据操作,在队头进行删除数据操作。
2.2. 队列的实现
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int QDataType;
typedef struct QListNode {
struct QListNode* next;
QDataType val;
}QNode;
typedef struct Queue {
QNode* qhead;
QNode* qtail;
int size;
}Queue;
void QueueInit(Queue* q);
void QueuePush(Queue* q, QDataType data);
void QueuePop(Queue* q);
QDataType QueueFront(Queue* q);
QDataType QueueBack(Queue* q);
int QueueSize(Queue* q);
int QueueEmpty(Queue* q);
void QueueDestroy(Queue* q);
QueueInit: 首先,我们使用了一个 Queue 的结构体,声明了 qhead 和 qtail 两个链表,现在,我们将其进行初始化。
void QueueInit(Queue* q) {
assert(q);
q->qhead = NULL;
q->qtail = NULL;
q->size = 0;
}
**QueuePush:**我们可以先开辟链式结构的空间,如果 qtail 尾有节点,即:先将其 next 节点的值改为 newnode,再将 qtail 节点与 newnode 节点相互连接。
void QueuePush(Queue* q, QDataType data) {
assert(q);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL) {
perror("malloc failed\n");
exit(1);
}
newnode->next = NULL;
newnode->val = data;
if(q->qtail == NULL) {
q->qhead = q->qtail = newnode;
} else{
q->qtail->next = newnode;
q->qtail = newnode;
}
q->size++;
}
**QueuePop:**如果队列只有一个节点即队头,那我们直接将其 free,并置为空;若节点大于一个,那我们可以创建一个 QNode*的 next 节点指向队头指向的下一个节点,然后再将队头的节点 free,再重新将队头的节点改为 next 指针指向的新节点。
void QueuePop(Queue* q) {
assert(q);
assert(q->size!=0);
if (q->qhead->next == NULL) {
free(q->qhead);
q->qhead = q->qtail = NULL;
} else {
QNode* next = q->qhead->next;
free(q->qhead);
q->qhead = next;
}
q->size--;
}
以下,获取队头元素、队尾元素、队列中有效元素个数、判空函数均于栈的实现思想大体一致,在这里就不赘述了。
QDataType QueueFront(Queue* q) {
assert(q);
assert(q->qhead);
return q->qhead->val;
}
QDataType QueueBack(Queue* q) {
assert(q);
assert(q->qtail);
return q->qtail->val;
}
int QueueSize(Queue* q) {
assert(q);
return q->size;
}
int QueueEmpty(Queue* q) {
assert(q);
return q->size == 0;
}
**QueueDestroy:**销毁队列,我们可以采取遍历整个队列的方式,将其中的数据从头至尾进行 free。
void QueueDestroy(Queue* q) {
assert(q);
QNode* cur = q->qhead;
while (cur) {
QNode* next = cur->next;
free(cur);
cur = next;
}
q->qhead = q->qtail = NULL;
q->size = 0;
}
3. 运用栈理解一道题
首先,我们已经实现好了栈,接下来,我们可以直接使用栈的函数。
对于这道题,我们应该先开辟一个栈(st),我们可以先将题目给定(s)的 '(' 、'[' 、 '{' 这三个左括号导入栈(st)中。然后,我们可以使用 STTop 取出栈(st)中的数据,进行对栈(s)的左右括号的匹配,如果匹配不成功,则返回 false,直到栈(s)为空时,过程结束。代码如下所示:
bool isValid(char* s) {
ST st;
STInit(&st);
while(*s) {
if(*s =='(' || *s == '[' || *s == '{') {
STPush(&st,*s);
} else {
if(STEmpty(&st)) {
STDestroy(&st);
return false;
}
char top = STTop(&st);
STPop(&st);
if((top == '(' && *s != ')') ||(top == '[' && *s != ']') ||(top == '{' && *s != '}')) {
STDestroy(&st);
return false;
}
}
++s;
}
bool ret = STEmpty(&st);
STDestroy(&st);
return ret;
}
4. 使用两个队列实现一个栈
依照题意,我们创建一个栈(MyStack),包含两个队列(Queue q1\q2);
然后,我们需要为新创建的栈开辟新的空间(obj),并将其中的两个队列 q1、q2 初始化;
设计一个函数 myStackPush,用于非空队列尾部添加数据;
在函数 myStackPop 中,我们将非空队列的 size-1 个元素移动至空队列中;
然后,使用 mytStackTop 函数取得栈顶元素;
为了检测我们实现的栈是否为空,我们可以直接使用 QueueEmpty 返回这两个队列 q1、q2;
最后,当我们释放内存的时候,我们应先将这两个队列 q1、q2 先释放,最后再释放 obj 这个指向栈(MyStack) 的指针。
typedef struct {
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {
MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
QueueInit(&(obj->q1));
QueueInit(&(obj->q2));
return obj;
}
void myStackPush(MyStack* obj, int x) {
if(!QueueEmpty(&(obj->q1))) {
QueuePush(&(obj->q1),x);
}else{
QueuePush(&(obj->q2),x);
}
}
int myStackPop(MyStack* obj) {
Queue* empty = &(obj->q1);
Queue* nonEmpty = &(obj->q2);
if(!QueueEmpty(&(obj->q1))) {
nonEmpty = &(obj->q1);
empty = &(obj->q2);
}
while(QueueSize(nonEmpty)>1)
{
QueuePush(empty,QueueFront(nonEmpty));
QueuePop(nonEmpty);
}
int top = QueueFront(nonEmpty);
QueuePop(nonEmpty);
return top;
}
int myStackTop(MyStack* obj) {
if(!QueueEmpty(&(obj->q1))) {
return QueueBack(&(obj->q1));
}else{
return QueueBack(&(obj->q2));
}
}
bool myStackEmpty(MyStack* obj) {
return QueueEmpty(&(obj->q1)) && QueueEmpty(&(obj->q2));
}
void myStackFree(MyStack* obj) {
QueueDestroy(&(obj->q1));
QueueDestroy(&(obj->q2));
free(obj);
}
相关免费在线工具
- 加密/解密文本
使用加密算法(如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