✨✨C语言版数据结构基础---栈和队列
Posted 咖啡壶子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了✨✨C语言版数据结构基础---栈和队列相关的知识,希望对你有一定的参考价值。
C语言版数据结构基础— 线性表 | https://blog.csdn.net/TroyeSivanlp/article/details/119853106 |
---|---|
C语言版数据结构基础—栈和队列 | https://blog.csdn.net/TroyeSivanlp/article/details/120474691 |
🎈大家好,我是咖啡壶子,今天学习栈和队列🎈
栈和队列
栈和队列的表示和实现—C语言版数据结构
一.栈
1.1栈的定义和特点
- 栈是限定在表尾进行插入删除操作的线性表。表尾端称为栈顶,表头端称为栈底。不含元素的空表称为空栈。
- 插入元素到栈顶(即表尾)的操作,称为入栈。从栈顶(即表尾)删除最后一个元素的操作,称为出栈。
- 栈的特点:后进先出(Last In First Out)。
1.2栈的表示和操作的实现
1.2.1栈的类型定义
栈的抽象数据类型定义:
ADT Stack{
数据对象:D={a**i|a**i∈ElemSet, i=1,2,…,n, n≥0}
数据关系:R={<ai-*1, *ai >| a**i-1, a**i ∈D, i=2,…,n}
约定a**n端为栈顶,a1端为栈底。
基本操作:
-
InitStack(**&**S)(初始化栈)
操作结果:构造一个空栈S。
-
DestroyStack(**&**S)(销毁栈)
初始条件:栈S已存在。
操作结果:栈S被销毁。
-
ClearStack(**&**S)(清空栈)
初始条件:栈S已存在.
操作结果:清空栈S。
-
StackEmpty(S)(判断栈是否为空)
初始条件:栈S已存在。
操作结果:若栈S为空栈,则返回true,否则返回false。
-
StackLength(S)(求栈的长度)
初始条件:栈S已存在。
操作结果:返回栈S的元素个数,即栈的长
-
GetTop(S)(取栈顶元素)
初始条件:栈S已存在且非空。
操作结果:返回栈S的栈顶元素,不修改栈顶指针
-
Push(**&**S, e)(入栈)
初始条件:栈S已存在。
操作结果:插入元素e为新的栈顶元素。
-
Pop(**&**S, **&**e)(出栈)
初始条件:栈S已存在且非空。
操作结果:删除S的栈顶元素,并用e返回其值
-
StackTraverse(S)(遍历栈)
初始条件:栈S已存在且非空。
操作结果:从栈底到栈顶依次对S的每个数据元素进行访问。
}ADT Stack
1.2.2顺序栈的表示和实现
顺序栈:指利用顺序存储结构实现的栈,即利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时附设指针top指示栈顶元素在顺序栈中的位置。
顺序栈的存储结构
#define MAXSIZE 100//初始分配的存储空间
typedef struct{
SElemType *base; //栈底指针,始终指向栈底元素,构造之前和销毁之后base的值为NULL
SElemType *top; //栈顶指针,始终指向栈顶元素的下一个位置
int stacksize; //当前分配的存储容量,以元素为单位
}SqStack;
1 初始化
Status InitStack(SqStack &S){ //构造一个空栈S
S.base=new SElemType[MAXSIZE];
if(!S.base) exit(OVERFLOW); //若分配失败,异常退出
//否则,初始化栈顶指针top和stacksize
S.top=S.base;
S.stacksize=MAXSIZE;
return OK;
} //InitStack
2入栈
Status Push(SqStack &S, SElemType e){ //插入元素e为新的栈顶元素
if(S.top-S.base==S.stacksize) //栈满的标志,S.top-S.base为栈的长度
return ERROR;
*S.top++=e; //元素e进栈
return OK;
} //Push
3 出栈
Status Pop(SqStack &S, SElemType &e){
//若栈不空,则删除栈顶元素,用e返回其值,并返回OK,否则返回ERROR
if(S.top==S.base) return ERROR;
//若栈S为空,返回ERROR,S.top==S.base为栈空的标志
e=*--S.top; //若栈不空,用e返回其值,并删除该元素
rerurn OK;
} //Pop
4取栈顶元素
Status GetTop(SqStack S, SElemType &e){
//若栈不空,则用e返回栈顶元素,并返回OK,否则返回ERROR
if(S.top==S.base) return ERROR;
e=*(S.top-1); //考虑和e=*--S.top;的区别?
return OK;
} //GetTop
注意:
-
栈的长度: S.top-S.base
-
空栈:S.top==S.base
-
栈满:S.top-S.base>=S.stacksize
-
栈不存在:S.base==NULL
-
S.top在元素进出栈时改变
-
S.base只在追加存储空间时改变
二.队列
2.1队列的定义和特点
- 队列是限定在表的一端进行插入,在另一端进行删除操作的线性表。允许插入的一端称为队尾,允许删除的一端称为**队头。**不含元素的空表称为空队列。
- 插入元素到队尾的操作,称为入队。从队头删除一个元素的操作,称为出队。
- 队列的特点:先进先出(First In First Out)。
队列的类型定义
队列的抽象数据类型定义
ADT Queue{
数据对象:D={a**i|a**i∈ElemSet, i=1,2,…,n, n≥0}
数据关系:R={<ai-*1, *ai >| a**i-1, a**i ∈D, i=2,…,n}
约定a1端为队头,a**n端为队尾。
基本操作:
-
InitQueue(**&**Q)
操作结果:构造一个空队列Q。
-
DestroyQueue(**&**Q)
初始条件:队列Q已存在。
操作结果:队列Q被销毁。
-
ClearQueue(**&**Q)
初始条件:队列Q已存在。
操作结果:清空队列Q
-
QueueEmpty(Q)
初始条件:队列Q已存在。
操作结果:若队列Q为空队列,则返回true,否则返回false。
-
QueueLength(Q)
初始条件:队列Q已存在。
操作结果:返回队列Q的元素个数,即队列的长度。
-
GetHead(Q)
初始条件:队列Q已存在且非空。
操作结果:返回队列Q的队头元素。
-
EnQueue(**&**Q, e)
初始条件:队列Q已存在。
操作结果:插入元素e为Q的新的队尾元素。
-
DeQueue(**&**Q, **&**e)
初始条件:队列Q已存在且非空。
操作结果:删除Q的队尾元素,并用e返回其值。
-
QueueTraverse(Q)
初始条件:队列Q已存在且非空。
操作结果:从队头到队尾依次对Q的每个数据元素进行访问。
}ADT Queue
循环队列—队列的顺序表示和实现★
-
入队时将新元素插入rear所指的位置,然后将队尾指针加1。
-
出队时,删去front所指的元素,然后将队头指针加1并返回被删元素。
① 约定空队列:
front=rear
② 队列会满吗?
很可能会,数组有长度限制,而且数组前端空间通常无法释放。
在顺序队中,当尾指针已经到了数组的上界,不能再有入队操作,但其实数组中还有空位置,这就叫“假溢出”。
解决假溢出的途径———采用循环队列
问题:
队空时:front=rear
队满时:rear=front
解决办法:
法1:设一标志位flag表示队列是满还是空;
法2:约定front指向rear的下一个位置(环状的下一个位置)时为满,即浪费1个元素空间,队列最多可有n-1个元素;
法3:用一个计数器记录队列中元素的个数。
循环队列—队列的顺序表示和实现
队列的顺序存储表示(浪费一个存储空间的循环队列)
#define MAXSIZE 100 //最大队列长度
typedef struct {
QElemType *base; //动态分配存储空间的基地址
int front; //队列头指针,若队列不空,始终指向 队列头元素,实为队列首元下标
int rear; //队列尾指针,若队列不空,始终指向队列最后元素的
下一个位置, 实为队列最后元素的下一个位置下标
}**SqQueue**;
队空条件 : front == rear (初始化时:front = rear )
队满条件:front == (rear+1) %N (N=MAXSIZE)
队列长度:L== (rear-front +N )%N
1初始化:浪费一个元素空间的循环队列
Status InitQueue(SqQueue &Q) { //初始化空循环队列Q
Q.base=new QElemType[MAXSIZE]; //分配空间
if (!Q.base) exit(OVERFLOW); //空间分配失败,退出程序
Q.front=Q.rear=0; //置队列为空队列
return OK;
}//InitQueue
##### 2求队列长度:浪费一个元素空间的循环队列
int Queuelength(SqQueue Q){
return (Q.rear-Q.front+MAXSIZE)%MAXSIZE;
} //Queuelength
##### 3入队:浪费一个元素空间的循环队列
Status EnQueue(SqQueue &Q, QElemType e){
//向循环队列Q的队尾加入一个元素e
if (Q.front== (Q.rear+1)%MAXSIZE) return ERROR;
Q.base[Q.rear]=e; //插入e
Q.rear=(Q.rear+1)%MAXSIZE; //指针后移
return OK;
}//EnQueue
4 出队:浪费一个元素空间的循环队列
Status DeQueue(SqQueue &Q, QElemType &e) {
//若队列不空,删除循环队列 q 的队头元素,由 e 返回其值,并返回OK
if (Q.front==Q.rear) return ERROR; //若队列为空,则出错
e=Q.base[Q.front] ;
Q.front=(Q.front+1)%MAXSIZE ;
return OK;
} //DeQueue
5 取循环队列队头元素
SElemType GetHead(SqQueue Q){
if(Q.front!=Q.rear) return Q.base[Q.front];
}//GetHead
Q.base[Q.front] ;
Q.front=(Q.front+1)%MAXSIZE ;
return OK;
} //DeQueue
5 取循环队列队头元素
SElemType GetHead(SqQueue Q){
if(Q.front!=Q.rear) return Q.base[Q.front];
}//GetHead
三.总结
线性表、栈与队列的异同点
-
相同点:逻辑结构相同,都是线性的;都可以用顺序存储或链表存储; 栈和队列是两种特殊的线性表,即受限的线性表(只是对插入、删除运算加以限制)。
-
不同点:
①运算规则不同,线性表为随机存取,而栈是只允许在一端进行插入和删除运算,因而是后进先出表LIFO;队列是只允许在一端进行插入、另一端进行删除运算,因而是先进先出表FIFO。
②用途不同,线性表比较通用;堆栈用于函数调用、递归和简化设计等;队列用于离散事件模拟、多道作业处理和简化设计等。
🎈整理不易,喜欢的点赞收藏,快来和我一起学习吧🎈
以上是关于✨✨C语言版数据结构基础---栈和队列的主要内容,如果未能解决你的问题,请参考以下文章