数据结构与算法学习笔记-栈和队列

声明:本博客仅为本人学习途中做的笔记 采自青岛大学王卓老师的视频教学 主要内容为算法思路,具体代码实现还需修改后才能运行,望各位看官多多包涵,您的点赞与评论是对我最大的肯定!


1.栈和队列的定义和特点

栈和队列是两种常用的、重要的数据结构
栈和队列是限定插入和删除只能在表的"端点"进行的线性表
栈和队列是线性表的子集(是插入和删除位置受限的线性表)

由于栈的操作具有后进先出的固有特性,使得栈成为程序设计中的有用工具。另外,如果问题求解的过程具有"后进先出"的天然特性的话,则求解的算法中也必然需要利用"栈".

  • 数制转换
  • 括号匹配的检验
  • 行编辑程序
  • 迷宫求解
  • 表达式求值
  • 八皇后问题
  • 函数调用
  • 递归调用的实现
线性表 队列(先进先出)
Insert(L,i,x) 1<=i<=n+1 Insert(S,n+1,x) Insert(Q,n+1,x)
Delete(L,i)1<=i<=n Delete(S,n) Delete(Q,1)

栈是一个特殊的线性表,是限定仅在一端(通常是表尾)进行插入和删除操作的线性表。又称为后进先出的线性表
表尾a n _n n端称为栈顶,表头a 1 _1 1端称为栈底Base
插入元素到栈顶(即表尾)的操作,称为入栈。
从栈顶(即表尾)删除最后一个元素的操作,称为出栈。

队列是一种先进先出的线性表,在表一端插入,在另一端删除

2.栈的表示和操作的实现

由于栈本身就是线性表,于是栈也有顺序存储和链式存储两种实现方式
栈的顺序存储–顺序栈
栈的链式存储–链栈

顺序栈的表示和实现

存储方式:同一般线性表的顺序存储结构完全相同,利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。栈底一般在低地址端
附设top指针,指示栈顶元素在顺序栈中的位置
另设base指针,指示栈底元素在顺序栈中的位置
但是,为了方便操作,通常top指针指示真正的栈顶元素之上的下标地址
另外,用Stacksize表示栈可使用的最大容量
空栈:base == top是栈空标志
栈满:top-base == stacksize
栈满时的处理方法:
1.报错,返回操作系统
2.分配更大的空间,作为栈的存储空间,将原栈的内容移入新栈
在这里插入图片描述
在这里插入图片描述

使用数组作为顺序栈存储方式的特点:
简单、方便、但易产生溢出(数组长度固定)
上溢(overflow):栈已经满,又要压入元素
下溢(underflow):栈已经空,还要弹出元素

#define MAXSIZE 100
typedef struct{
    
    
    SElemType	*base;//栈底指针
    SElemType	*top;//栈顶指针
    int stacksize;//栈可用最大容量
}SqStack;

顺序栈的表示和实现

Status InitStack(SqStack &S){
    
    //构造一个空栈
    S.base = new SElemType[MAXSIZE];
    //或S.base=(SElemType*)malloc(MAXSIZE*sizeof(SElemType)); 
    if(!S.base) exit(OVERFLOW);//存储分配失败
    S.top = S.base;//栈顶指针等于栈底指针
    S.stacksize = MAXSIZE;
    return OK;
}

顺序栈判断栈是否为空

Status StackEmpty(SqStack S){
    
    
    //若栈为空,返回TRUE;否则返回FALSE
    if(S.top == S.base)
        return TRUE;
    else 
        return FALSE;
}

求顺序栈长度

int StackLength(SqStack S){
    
    
    return S.top-S.base;
}

清空顺序栈

Status ClearStack(SqStack S){
    
    
    if(S.base) S.top = S.base;
    return OK;
}

销毁顺序栈

Status DestoryStack(SqStack &S){
    
    
    if(S.base){
    
    
        delete S.base;
        S.stacksize=0;
        S.base=S.top=NULL;
    }
    return OK;
}

顺序栈的入栈

1.判断是否栈满,若满则出错(上溢)
2.元素e压入栈顶
3.栈顶指针+1

 Status Push(SqStack &S, SElemType e){
    
    
     if(S.top-S.base==S.stacksize)//栈满
         return ERROR;
     *S.top=e;
     S.top++;
     //*S.top++=e;
     return OK;
 }

顺序栈的出栈

1.判断元素是否栈空,若空则出错(下溢)
2.获取栈顶元素e
3.栈顶指针减1

Status Pop(SqStack &S, SElemType &e){
    
    
    //若栈不空,则删除栈顶元素,用e返回其值,并返回OK,否则返回ERROR
    if(S.top==S.base)//等价于if(StackEmpty(S))
        return ERROR;
    --S.top;
    e=*S.top;
    //e=*--top;
    return OK;
}

链栈的表示和实现

链栈是运算受限的单链表,只能在链表头部进行操作

typedef struct StackNode{
    
    
    SElemType data;
    struct StackNode *next;
}StackNode, *LinkStack;

LinkStack S;

链表的头指针就是栈顶
不需要头结点
基本不存在栈满的情况
空栈相当于头指针指向空
插入和删除只在栈顶处进行

链栈的表示和实现

链栈是运算受限的单链表,只能在链表头部进行操作

typedef struct StackNode{
    
    
    SElemType data;
    struct StackNode *next;
}StackNode, *LinkStack;

LinkStack S;

链表的头指针就是栈顶
不需要头结点
基本不存在栈满的情况
空栈相当于头指针指向空
插入和删除只在栈顶处进行

在这里插入图片描述

链栈的初始化

void InitStack(LinkStack &S){
    
    
    //构造一个空栈,栈顶指针置为空
    S=NULL;
    return OK;
}

判断链栈是否为空

Status StackEmpty(LinkStack S){
    
    
    if(S==NULL) return TRUE;
    else return FALSE;
}

链栈的入栈

Stack Push(LinkStack &S,SElemType e){
    
    
    p=new StackNode;//生成新结点P
    p->data=e;//将新结点数据域置为e
    p->next=S;//将新结点插入栈顶
    S=p;//修改栈顶指针
    return OK;
}

链栈的出栈

Status Pop(LinkStack &S, SelemType &e){
    
    
    if(S==NULL) return ERROR;
    e=S->data;
    p=S;
    S=s->next;
    delete p;
    return OK;
}

取栈顶元素

SElemType GetTop(LinkList S){
    
    
    if(S!=NULL)
        return S->data;
}

4.队列的表示和操作

队列是仅在表尾进行插入操作,在表头进行删除操作的线性表
表尾即an端,称为队尾,表头为a1端,称为队头
它是一种先进先出的线性表

队列的相关概念:
1.定义 只能在表的一端进行插入运算,在表的另一端进行删除运算的线性表(头插尾删)
2.逻辑结构 与同线性表相同,仍为一对一关系
3.存储结构 顺序对或链队,以循环顺序队列更常见
4.运算规则 只能在队首和队尾运算且访问结点时依照先进先出的原则

队列的顺序表示和实现

顺序队列

用一维数组

#define MAXQSIZE 100//最大队列长度
Typedef struct{
    
    
    QElemType *base//初始化的动态分配存储空间
    int front;//头指针
    int rear;//尾指针
}SqQueue

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

解决假上溢的方法:引入循环队列
base[0]接在base[MAXQSIZE-1]之后,若rear+1==M,则令rear=0;
实现方法:利用模(%,c语言:mod )运算
插入元素:Q.base[Q.rear]=x;
Q.rear=(Q.rear+1)%MAXQSIZE;
删除元素:x=Q.base [s.front]
Q.front=(Q.front+1)%MAXSIZE

在这里插入图片描述

循环队列的操作–队列的初始化

#define MAXQSIZE 100	//最大队列长度
typedef struct{
    
    
    QElemType *base//动态分配存储空间
    int front;	//头指针,若队列不空,指向队列头元素
    int rear;//尾指针,若队列不空,指向队列尾元素的下一个元素
}SqQueue

Status InitQueue(SqQueue &Q){
    
    
    Q.base=new QElemType*;//分配数组空间
    //Q.base=(QElemType*)malloc(MAXQSIZE*sizeof(QElemType));
    if(!Q.base)exit(OVERFLOW);//存储分配失败
    Q.front=Q.rear=0;//头指针尾指针置为0,队列为空
    return OK;
}

循环队列的操作–求队列的长度

int QueueLength(SqQueue Q){
    
    
    return((Q.rear-Q.front+MAXQSIZE)%MAXQSIZE);
}

在这里插入图片描述

循环队列的操作–循环队列入队

Status EnQueue(SqQueue &Q,QElemType e){
    
    
   if((Q.rear+1)%MAXQSIZE==Q.front) return ERROR;//队满
   Q.base[Q.rear]=e;//新元素加入队尾
   Q.rear=(Q.rear+1)%MAXQSIZE;//队尾指针+1
   return OK;
}

循环队列的操作–循环队列出队

Status DeQueue(SqQueue &Q,QElemType &e){
    
    
    if(Q.front==Q.rear) return ERROR;//队空
    e=Q.base[Q.front];//保存队头元素
    Q.fromt=(Q.front+1)%MAXQSIZE;//队头指针+1
}

循环队列的操作–取队头元素

Status GetHead(SqQueue Q){
    
    
    if(Q.feont!=Q.rear)//队列不为空
        return Q.base[Q.front];//返回队头指针元素的值,队头指针不变
}

队列的链式表示和实现

链式队列

若用户无法估计所用队列的长度,则宜采用链队列
在这里插入图片描述

#define MAXQSIZE 100//最大队列长度
typedef struct Qnode{
    
    
    QElemType data;
    struct Qnode *next;
}QNode, *QuenPtr;

typedef struct{
    
    
    QuenePtr front;//队头指针
    QuenePtr rear;//队尾指针
}LinkQueue;

链队列的操作–链队列初始化

Status InitQueue(LinkQueue &Q){
    
    
    Q.front=Q.rear=(QueuePtr)malloc(sizeof(QNode));
    if(!Q.front) exit(OVERFLOW);
    Q.front->next=NULL;
    return OK;
}

链队列的操作–链队列的销毁

算法思想:从队头结点开始,依次释放所有结点

Status DestoryQueue(LinkQueue &Q){
    
    
    while(Q.front){
    
    
        p=Q.front->next;
        free(Q.front);
        Q.front=p;
    }	//Q.rear=Q.front->next;
    	//free(Q.front);
    	//Q.front=Q.rear
    return OK;
}

链队列的操作–将e元素入队

Status EnQueue(LinkQueue &Q,QElemType e){
    
    
    p=(QueuePtr)malloc(sizeof(QNode));
    if(!p) exit(OVERFLOW);
    P->data=e;
    p->next=NULL;
    Q.rear->next=p;
    Q.rear=p;
    return OK;
}

链队列的操作–链队列出队

Status DeQueue(LinkQueue &Q,QElemType &e){
    
    
    if(Q.front==Q.rear) return ERROR;
    p=Q.front->next;
    e=p->data;
    Q.front->next=p->next;
    if(Q.rear==p) Q.rear=Q.front;
    delete p;
    return OK;
}

链队列的操作–求链队列的队头元素

Status GetHead(LinkQueue Q,QElemType &e){
    
    
    if(Q.front==Q.rear) return ERROR;
    e=Q.front->next->data;
    return OK;
}

おすすめ

転載: blog.csdn.net/qq_53548177/article/details/121863941