详细介绍栈和队列,适合零基础小白反复使用【数据结构】
创始人
2025-06-01 14:22:43

文章目录

    • 栈的初始化
    • 压栈
    • 销毁
    • 出栈
    • 栈中有效元素个数
    • 判断栈是否为空
    • 拿到栈顶数据
  • 完整代码
  • 队列
    • 队列的初始化
    • 队尾入队列
    • 队列的销毁
    • 获取队列中有效元素个数
    • 判断队列是否为空
    • 获取队列头部元素
    • 获取队列尾部元素
    • 完整代码

在这里插入图片描述

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶另一端称为栈底
栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则

压栈(Push):栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶
出栈(Pop):栈的删除操作叫做出栈。出数据也在栈顶

在这里插入图片描述

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小

栈的初始化

typedef int  StackDataType;
typedef struct Stack
{StackDataType * a;int top; // 栈顶int capacity; 
}StackType;void StackTypeInit(StackType* ps)
{assert(ps);ps->a = NULL;ps->capacity = 0;ps->top = 0; // ps->top = -1 
}

栈初始化 ,top可以给-1 ,top可以给 0

如果top给的是 -1 , 意味着top 指向栈顶数据
top 先++ ,再放数据 ,此时top 指向栈顶的数据
在这里插入图片描述

初始化时 ,top 给的是0 , 意味着top指向栈顶数据的下一个
在这里插入图片描述

压栈

压栈需要考虑容量的问题 ,如果容量已满 ,则需要扩容

void StackTypePush(StackType* ps, StackDataType x)
{// 容量已满  ,考虑增容 if (ps->capacity == ps->top){int  newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;StackType* tmp = (StackType*)realloc(ps->a, sizeof(StackType) * newcapacity);if (tmp == NULL){printf("realloc fail");exit(-1);}//增容成功 ps->a = tmp;ps->capacity = newcapacity;}ps->a[ps->top] = x;ps->top++;
}

销毁

void StackTypeDestory(StackType* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->capacity = ps->top = 0;
}

出栈

当栈的数据为空时 此时注意不需要删除了

void StackTypePop(StackType* ps)
{assert(ps);assert(ps->top > 0);ps->top--;}

栈中有效元素个数

int StackSize(StackType* ps)
{assert(ps);return ps->top;
}

判断栈是否为空

如果栈为空 ,此时就不需要进行出栈操作 ,否则会导致越界,形成非法访问

bool StackEmpty(StackType* ps)
{assert(ps);return ps->top == 0 ;   // 栈为空 返回true ,不为空 返回 false 
}

拿到栈顶数据

StackDataType StackTop(StackType* ps)
{assert(ps);assert(!StackEmpty(ps));  //StackEmpty 是空 return true 不是空return false  return ps->a[ps->top - 1];  // 初始化是top = 0 是指向栈顶的下一个数据 top-1 即是栈顶数据}

完整代码

Stack.c

#include"Stack.h"
void StackTypeInit(StackType* ps)
{assert(ps);ps->a = NULL;ps->capacity = 0;ps->top = 0; // ps->top = -1 
}void StackTypePush(StackType* ps, StackDataType x)
{// 容量已满  ,考虑增容 if (ps->capacity == ps->top){int  newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;StackType* tmp = (StackType*)realloc(ps->a, sizeof(StackType) * newcapacity);if (tmp == NULL){printf("realloc fail");exit(-1);}//增容成功 ps->a = tmp;ps->capacity = newcapacity;}ps->a[ps->top] = x;ps->top++;
}void StackTypeDestory(StackType* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->capacity = ps->top = 0;
}void StackTypePop(StackType* ps)
{assert(ps);assert(ps->top > 0);ps->top--;}int StackSize(StackType* ps)
{assert(ps);return ps->top;
}bool StackEmpty(StackType* ps)
{assert(ps);return ps->top; 
}StackDataType StackTop(StackType* ps)
{assert(ps);assert(!StackEmpty(ps));  //StackEmpty 是空 return true 不是空return false  return ps->a[ps->top - 1];  // 初始化是top = 0 是指向栈顶的下一个数据 top-1 即是栈顶数据}

Stack.h

#pragma once
#include
#include
#include
#include
typedef int  StackDataType;typedef struct Stack
{StackDataType * a;int top; // 栈顶int capacity; 
}StackType;void StackTypeInit(StackType* ps);void StackTypePush(StackType* ps, StackDataType x);void StackTypeDestory(StackType* ps);void StackTypePop(StackType* ps); int StackSize(StackType* ps);  // 栈中有效元素个数bool StackEmpty(StackType* ps);  //判断栈是否为空  StackDataType StackTop(StackType* ps);  //拿到栈顶数据

test.c

#include"Stack.h" void TestStack1()
{StackType ST;StackTypeInit(&ST);StackTypePush(&ST, 1);StackTypePush(&ST, 2);printf("%d", StackTop(&ST)); //拿到栈顶元素 StackTypePop(&ST);StackTypePush(&ST, 3);StackTypePush(&ST, 4);StackTypeDestory(&ST);
}void TestStack2()
{StackType ST;StackTypeInit(&ST);StackTypePush(&ST, 1);StackTypePush(&ST, 2);StackTypePush(&ST, 3);StackTypePush(&ST, 4);StackTypePush(&ST, 5);//遍历 while (!StackEmpty(&ST)){printf("%d ", StackTop(&ST));StackTypePop(&ST);}StackTypeDestory(&ST);
}
void TestStack3()
{StackType ST;StackTypeInit(&ST);StackTypePush(&ST, 1);StackTypePush(&ST, 2);printf("%d ", StackTop(&ST));StackTypePop(&ST);StackTypePush(&ST, 3);StackTypePush(&ST, 4);printf("%d ", StackTop(&ST));StackTypePop(&ST);StackTypePush(&ST, 5);//遍历 while (!StackEmpty(&ST)){printf("%d ", StackTop(&ST));StackTypePop(&ST);}StackTypeDestory(&ST);}
int main()
{//TestStack1();//TestStack2();TestStack3();return 0;
}

队列

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头

在这里插入图片描述

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低

我们采用类似单链表来实现队列 ,但是队列要求队尾入队,队头出队 ,也就意味着需要频繁找尾指针 ,而单链表去找尾指针效率很低,所以定义一个头指针和尾指针,方便找尾节点,这样就会多两个指针 ,如果放进函数里当参数会很麻烦 ,干脆直接封装一个结构体

单链表不定义一个尾指针呢?如果我们给单链表定义了一个尾指针的话,尾插就不再需要去找尾节点了,但是尾删的话,定义的尾指针就显得很鸡肋,此时尾删依然需要找到尾节点的前一个节点,也就说如果单链表定义尾指针,也不能完美解决问题,所以单链表就不定义尾指针

typedef int   QueueNodeTypeData;typedef struct  QueueNode
{struct  QueueNode* next;QueueNodeTypeData data;
}QueueNodeType;typedef struct Queue
{QueueNodeType * tail; // 队尾QueueNodeType * head; //队头int size; 
} Queue;  // 链式结构:表示队列

队列的初始化

我们采用链表的形式来实现队列
在这里插入图片描述

将头和尾初始化为NULL

void QueueInit(Queue* pq)
{assert(pq);pq->head = pq->tail = NULL;pq->size = 0;
}

队尾入队列

和单链表尾插大致相同

void QueuePush(Queue* pq, QueueNodeTypeData x) // 入队 
{assert(pq);QueueNodeType* newnode =(QueueNodeType*) malloc( sizeof(QueueNodeType) );if (newnode == NULL){printf(" malloc fail");exit(-1);}newnode->data = x;newnode->next = NULL;//扩容成功//第一次入队if ( pq->head == NULL){assert(pq->tail == NULL);   //pq->head ==NULL , pq->tail != NULL ,就是出问题了pq->tail = pq->head = newnode;}//非第一次入队else {pq->tail->next = newnode; // 类似尾插pq->tail = newnode; // 更新tail 指针}pq->size++;
}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

队列的销毁

需要注意的是保存下一个节点的地址

void QueueDestory(Queue* pq) //队列的销毁
{assert(pq);QueueNodeType * cur = pq->head;//遍历 while (cur){QueueNodeType* next = cur->next;  //保存下一个节点的地址free(cur); //释放当前节点cur = next; //不能写成cur=cur->next }pq->tail = pq->head = NULL;pq->size = 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

获取队列中有效元素个数

int QueueSize(Queue* pq)//获取队列中有效元素个数
{assert(pq);return pq->size;
}

判断队列是否为空

bool QueueEmpty(Queue* pq)
{assert(pq);return pq->size == 0;
}

获取队列头部元素

QueueNodeTypeData QueueFront(Queue* pq) //获取队列头部元素
{assert(pq);assert(!QueueEmpty(pq)); //防止队列为空return pq->head->data;}

获取队列尾部元素

QueueNodeTypeData QueueBack(Queue* pq) //获取队列尾部元素
{assert(pq);assert(!QueueEmpty(pq)); //防止队列为空return pq->tail->data;
}

完整代码

Queue.h

#include
#include
#include
#includetypedef int   QueueNodeTypeData;typedef struct  QueueNode
{struct  QueueNode* next;QueueNodeTypeData data;
}QueueNodeType;typedef struct Queue
{QueueNodeType * tail; // 队尾QueueNodeType * head; //队头int size; 
} Queue;  // 链式结构:表示队列void QueueInit(Queue* pq);  // 初始化  void QueueDestory(Queue* pq); //队列的销毁void QueuePush(Queue* pq , QueueNodeTypeData x );  // 队尾入队列void QueuePop(Queue* pq); //队头出队列int QueueSize(Queue* pq);//获取队列中有效元素个数bool QueueEmpty(Queue* pq); // 判断队列是否为空 QueueNodeTypeData QueueFront(Queue* pq); //获取队列头部元素QueueNodeTypeData QueueBack(Queue* pq); //获取队列尾部元素

Queue.c

#include"Queue.h"
void QueueInit(Queue* pq)
{assert(pq);pq->head = pq->tail = NULL;pq->size = 0;
}void QueueDestory(Queue* pq) //队列的销毁
{assert(pq);QueueNodeType * cur = pq->head;//遍历 while (cur){QueueNodeType* next = cur->next;  //保存下一个节点的地址free(cur); //释放当前节点cur = next; }pq->tail = pq->head = NULL;pq->size = 0;
}
void QueuePush(Queue* pq, QueueNodeTypeData x) // 入队 
{assert(pq);QueueNodeType* newnode =(QueueNodeType*) malloc( sizeof(QueueNodeType) );if (newnode == NULL){printf(" malloc fail");exit(-1);}newnode->data = x;newnode->next = NULL;//扩容成功//第一次入队if ( pq->head == NULL){assert(pq->tail == NULL);   //pq->head ==NULL , pq->tail != NULL ,就是出问题了pq->tail = pq->head = newnode;}else //非第一次入队{pq->tail->next = newnode; // 类似尾插pq->tail = newnode; // 更新tail 指针}pq->size++;
}
void QueuePop(Queue* pq) //队头出队列
{assert(pq);assert(pq->head != NULL); //只有一个节点if (pq->head->next == NULL) {free(pq->tail);  //出队pq->tail = pq->head = NULL;}// 多个节点else{QueueNodeType* next = pq->head->next; // 保存下一个节点的地址 free(pq->head); // 出队pq->head = NULL;pq->head = next;  //更新pq->head ,往后走 }pq->size--;
}int QueueSize(Queue* pq)//获取队列中有效元素个数
{assert(pq);return pq->size;
}//bool QueueEmpty(Queue* pq) // 判断队列是否为空 
//{
//	assert(pq);
//
//	if (pq->head == 0)
//	{
//		return true;
//	}
//	else
//	{
//		return false;
//	}
//}bool QueueEmpty(Queue* pq)
{assert(pq);return pq->size == 0;
}QueueNodeTypeData QueueFront(Queue* pq) //获取队列头部元素
{assert(pq);assert(!QueueEmpty(pq)); //防止队列为空return pq->head->data;}QueueNodeTypeData QueueBack(Queue* pq) //获取队列尾部元素
{assert(pq);assert(!QueueEmpty(pq)); //防止队列为空return pq->tail->data;
}

Test.c

#include"Queue.h"void TestQueue1()
{Queue q;QueueInit(&q);QueuePush(&q, 1);QueuePush(&q, 2);QueuePush(&q, 3);QueuePush(&q, 4);QueueDestory(&q);}
void TestQueue2()
{Queue q;QueueInit(&q);QueuePush(&q, 1);QueuePush(&q, 2);QueuePush(&q, 3);QueuePush(&q, 4);QueuePop(&q);QueuePop(&q);QueuePop(&q);QueueDestory(&q);}void TestQueue3()
{Queue q; QueueInit(&q); QueuePush(&q, 1);QueueEmpty(&q);QueueDestory(&q);
}
void TestQueue4()
{Queue q;QueueInit(&q);QueuePush(&q, 1);QueuePush(&q, 2);QueuePush(&q, 3);QueuePush(&q, 4);QueueSize(&q);QueueDestory(&q);}
void TestQueue5()
{Queue q;QueueInit(&q);QueuePush(&q, 1);QueuePush(&q, 2);QueuePush(&q, 3);QueuePush(&q, 4);QueueFront(&q);QueueBack(&q);QueueDestory(&q);}
int main()
{                  //TestQueue1();//TestQueue2();//TestQueue3();//TestQueue4();TestQueue5();return 0;
}

在这里插入图片描述

如果你觉得这篇文章对你有帮助,不妨动动手指给点赞收藏加转发,给鄃鳕一个大大的关注
你们的每一次支持都将转化为我前进的动力!!!

相关内容

热门资讯

今年我省粮食产量达515.56... (来源:辽宁日报)转自:辽宁日报 图为在中储粮(盘锦)储运有限公司,装运粮食的重型卡车排起长队...
国家发展改革委部署促进投资止跌... (来源:辽宁日报)转自:辽宁日报 新华社北京12月13日电 (记者魏玉坤) 记者13日从全国发展和改...
江苏省实施《中华人民共和国森林... (来源:新华日报) 目 录 第一章 总则 第二章 森林、林木和林地权属管理...
姜堰数字化产品讲“活”理论 (来源:新华日报) □ 本报记者 卢佳乐 通讯员 姜宣 “王教授,您约我‘喝茶论道’,...
联合国维和部队在苏丹遇袭 6人... 转自:财联社【联合国维和部队在苏丹遇袭 6人死亡】财联社12月14日电,当地时间13日,苏丹武装部队...