✨✨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端为栈底。

基本操作:

  1. InitStack(**&**S)(初始化栈

    操作结果:构造一个空栈S。

  2. DestroyStack(**&**S)(销毁栈)

    初始条件:栈S已存在。

    操作结果:栈S被销毁。

  3. ClearStack(**&**S)(清空栈)

    初始条件:栈S已存在.

    操作结果:清空栈S。

  4. StackEmpty(S)(判断栈是否为空)

    初始条件:栈S已存在。

    操作结果:若栈S为空栈,则返回true,否则返回false。

  5. StackLength(S)(求栈的长度)

    初始条件:栈S已存在。

    操作结果:返回栈S的元素个数,即栈的长

  6. GetTop(S)(取栈顶元素)

    初始条件:栈S已存在且非空。

    操作结果:返回栈S的栈顶元素,不修改栈顶指针

  7. Push(**&**S, e)(入栈)

    初始条件:栈S已存在。

    操作结果:插入元素e为新的栈顶元素。

  8. Pop(**&**S, **&**e)(出栈)

    初始条件:栈S已存在且非空。

    操作结果:删除S的栈顶元素,并用e返回其值

  9. 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端为队尾。

基本操作:

  1. InitQueue(**&**Q)

    操作结果:构造一个空队列Q。

  2. DestroyQueue(**&**Q)

    初始条件:队列Q已存在。

    操作结果:队列Q被销毁。

  3. ClearQueue(**&**Q)

    初始条件:队列Q已存在。

    操作结果:清空队列Q

  4. QueueEmpty(Q)

    初始条件:队列Q已存在。

    操作结果:若队列Q为空队列,则返回true,否则返回false。

  5. QueueLength(Q)

    初始条件:队列Q已存在。

    操作结果:返回队列Q的元素个数,即队列的长度。

  6. GetHead(Q)

    初始条件:队列Q已存在且非空。

    操作结果:返回队列Q的队头元素。

  7. EnQueue(**&**Q, e)

    初始条件:队列Q已存在。

    操作结果:插入元素e为Q的新的队尾元素。

  8. DeQueue(**&**Q, **&**e)

    初始条件:队列Q已存在且非空。

    操作结果:删除Q的队尾元素,并用e返回其值。

  9. 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语言版数据结构基础---栈和队列的主要内容,如果未能解决你的问题,请参考以下文章

数据结构(C语言版) 栈和队列 算法设计Demo14

采用队列,编写程序打印出杨辉三角形 数据结构(C语言版) 帮帮忙啊~~

数据结构(C语言版) 栈和队列 算法设计Demo8

数据结构(C语言版) 栈和队列 算法设计Demo12

数据结构(C语言版) 栈和队列 算法设计Demo9

数据结构(C语言版) 栈和队列 算法设计Demo13