当前位置: 首页 > news >正文

数据结构——栈与队列

1.栈

1.1概念

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

可以比作为羽毛球桶,弹夹​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

1.2栈的实现

1.2.1实现栈的选择

栈可以使用链表和数组来实现,哪种方法比较好

数组,我们对最后一个元素进行删除和插入,可以实现压栈和出栈

链表有两种,一种是单链表,另一种是双向链表

单链表我们可以把头结点当做栈顶,进行头插和头删

双向链表则没有什么限制,头插和头删,尾插,尾删都可以

那如何选择呢?

首先来看单双链表,我们既然能用单链表实现,就可以放弃双向链表,因为双向链表比单链表多一个指针,实现的时候就可以少维护一个指针,并且空间也优化了一点

其实单链表和数组的实现差不多,本章我们就只实现数组来实现栈,有兴趣的同学可以用链表来实现,而我们实现的栈和链表一样是动态的

1.2.2 准备工作

创建三个文件

1、头文件Stack.h  是来声明接口函数,定义顺序表,将几个公共用到的库函数集合起来
2、源文件Stack.c  是用来具体实现接口
3、源文件test.c  用于接口的测试工作 ,即体的使用场景

在这里提一嘴,有人可能会问创建文件为什么要用这个名字,例如头文件Stack.h不能换成XXX.h第一是可以让我们更好的辨认这个文件是干嘛的,Stack代表栈

第二后面学习STL库的时候,这个名字就很重要了,因此大家可以先按照我这个来写,同时在下面函数的实现中,有些函数名可能不知道什么意思,没关系,有印象就行。                                        

1.2.3基础接口实现

1.2.3.1 创建栈的结构
typedef int STDataType;
typedef struct Stack
{STDataType* _a;int _top; // 栈顶位置int _capacity; // 容量
}Stack;
1.2.3.2 初始化栈
void StackInit(Stack* ps)
{void StackInit(Stack* ps)
{assert(ps);//断言不作叙述ps->_a = NULL;ps->_capacity = 0;ps->_top = 0;//ps->_top = -1;
}
}

top可以选择0或者-1,这两者有什么区别

之后处理栈顶元素的时候根据自己的top处理一下就行,这里我就用top=0来处理

1.2.3.3 入栈
void StackPush(Stack* ps, STDataType data)
{assert(ps);//扩容if (ps->_top == ps->_capacity){int new = ps->_capacity == 0 ? 4 : ps->_capacity * 2;STDataType* tmp = (STDataType*)realloc(ps->_a, new*sizeof(STDataType));if (tmp==NULL){perror("relloc");return;}ps->_a = tmp;ps->_capacity = new;}//插入数据top=0ps->_a[ps->_top] = data;ps->_top++;//top=1//ps->_a[++ps->_top] = data;
}
1.2.3.4 出栈
// 出栈
void StackPop(Stack* ps)
{assert(ps);ps->_top--;
}
1.2.3.5 获取栈顶元素
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{assert(ps);return ps->_a[ps->_top - 1];//top=0//return ps->_a[ps->_top ];top=-1;
}
1.2.3.6  获取栈中有效元素个数
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{assert(ps);return ps->_a[ps->_top ];//top=0//return ps->_a[ps->_top+1 ];top=-1;
}
1.2.3.7 检测栈是否为空
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps)
{assert(ps);//top=0if (ps->_top == 0){return 1;}else{return 0;}//top=-1;/*if (ps->_top == -1){return 1;}else{return 0;}*/
}
1.2.3.8 销毁栈
// 销毁栈
void StackDestroy(Stack* ps)
{assert(ps);free(ps->_a);ps->_a = NULL;ps->_capacity = 0;ps->_top = 0;
}

2.队列

2.1概念

只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有 先进先出
入队列:进行插入操作的一端称为 队尾
出队列:进行删除操作的一端称为队头
可以当做我们日常生活中排队买东西的方式

2.2队列的实现

2.2.1实现队列的选择

和栈一样,队列有三种选择:数组,单双链表,那根据队列的先进先出的原则,来看看那个结构比较好

先来看看数组,我们发现,每次数组进行出队列的操作时,数组内的元素总要往前移动,效率较低

而链表就不需要移动数据,出队列头删,入队列尾插就行了,同样在创建队列的时候只需要单链表就行了,可以节约一部分空间

2.2.2准备工作

和链表一样创建三个文件

1、头文件Queue.h  是来声明接口函数,定义顺序表,将几个公共用到的库函数集合起来 2、源文件Queue.h  是用来具体实现接口

3、源文件test.c  用于接口的测试工作 ,即体的使用场景

2.2.3基础接口实现

2.2.3.1创建队列结构
typedef int QDataType;
typedef struct QListNode
{struct QListNode* _pNext;QDataType _data;
}QNode;

当我们实现这个结构的时候,我们每次入队列的时候都要找到最后一个节点,我们写入队列函数时要传入三个参数,分别是头结点,尾结点,数据,尾插为什么要头结点那,当队列没有数据是,为NULL,就需要头结点和尾结点维护

、、

而且实现函数时,要传二级指针,这样才能改变实参,这样实现函数非常麻烦,此时我们可以用一个结构体来存储队列的头尾节点

typedef int QDataType;
typedef struct QListNode// 链式结构:表示队列
{struct QListNode* _pNext;QDataType _data;
}QNode;
typedef struct Queue// 队列的结构
{QNode * phead;//头结点QNode* ptail;//尾结点int size;//数据个数
}Queue;

这样我们在入队列函数传参数的时候就可以只用传这个结构体的指针,以及数据,这样不仅可以少传一个参数,又避免了二级指针

2.2.3.2 队尾入队列
// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{assert(q);//创建链表QNode* newnode= (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc");return;}newnode->_pNext = NULL;newnode->_data = data;//没有数据时if ( q->ptail == NULL){q->phead = q->ptail = newnode;}//有数据时else{q->ptail->_pNext = newnode;q->ptail = newnode;}q->size++;
}
2.2.3.3队头出队列
// 队头出队列
void QueuePop(Queue* q)
{assert(q);assert(q->size != 0);//只有一个节点时if (q->phead->_pNext == NULL){free(q->phead);q->phead = q->ptail = NULL;}//多个节点else{QNode* node = q->phead->_pNext;free(q->phead);q->phead = node;}q->size--;
}
2.2.3.4获取队列头部元素
// 获取队列头部元素
QDataType QueueFront(Queue* q)
{assert(q);assert(q->phead);return q->phead->_data;
}
2.2.3.5获取队列队尾元素
// 获取队列队尾元素
QDataType QueueBack(Queue* q)
{assert(q);assert(q->ptail);return q->ptail->_data;
}
2.2.3.6获取队列中有效元素个数
int QueueSize(Queue* q)
{assert(q);return q->size;
}
2.2.3.7检测队列是否为空
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{assert(q);return q->size == 0;
}
2.2.3.7销毁队列
// 销毁队列
void QueueDestroy(Queue* q)
{assert(q);QNode* cur = q->phead;while (cur){QNode* next = cur->_pNext;free(cur);cur = next;}q->size = 0;
}

相关文章:

  • 简单场景下的目标关联算法:GNN全局最近邻与匈牙利算法
  • 制作一款打飞机游戏20:敌人被击中时的视觉效果
  • 理解js函数(Ⅱ)
  • 嵌入式Linux驱动开发:LED实验
  • Spring Boot中自定义404异常处理问题学习笔记
  • Android学习总结之Room篇
  • 发送网络请求
  • 《无尽的尽头》今日开播 刘家祎大胆演绎林磊儿的“另一面”
  • RAG(检索增强生成)技术详解与应用实践:从原理到落地
  • 简单几步,开启 Intel VT-x 让电脑“解开CPU封印”
  • 蓝桥杯 20. 压缩变换
  • 数据分析之 商品价格分层之添加价格带
  • 欧姆龙NJ系列PLC通讯
  • vue3-springboot-mysql的docker部署
  • 怎么实现RAG检索相似文档排序:similarities
  • 云蝠智能大模型呼叫:AI驱动的通信服务革新与实践
  • 操作系统---进程同步与互斥
  • 【频谱分析仪与信号分析仪】异同比较
  • Unity后处理全解析:从入门到优化
  • 《Linux程序设计》实验8 线程程序设计
  • 光线传媒:正与部分重点地区洽谈主题乐园合作,首款3A游戏预计三年左右推出
  • 外交部回应美财长涉中国发展经济模式言论:损害各国人民共同利益的是美方
  • 研究|和去年相比,人们使用AI的需求发生了哪些变化?
  • 刺激视网膜可让人“看”到全新颜色
  • 继加州后,美国又有11州起诉特朗普政府滥用关税政策“违法”
  • 乌克兰关切有中国人在俄军中服务,外交部:坚决反对无端指责