数据结构(栈)

  1. 栈的基本概念
  2. 栈的顺序存储实现
  3. 栈的链式存储实现
  4. 队列的基本概念
  5. 队列的顺序实现
  6. 队列的链式实现
  7. 双端队列
  8. 栈的应用(在括号匹配中的作用)
  9. 栈的应用(在表达式求值中的应用)
  10. 栈的应用(在递归中的应用)
  11. 队列的应用
  12. 特殊矩阵的压缩存储

【1】
定义:栈(stack)是只允许在一端进行插入或删除操作的线性表
是一种操作受限的线性表。

重要术语:

  1. 空栈:相当于线性表的空表
  2. 栈顶:允许插入和删除的这一端
  3. 栈底:不允许插入和删除的一端
    【特点:后进先出——Last In FIrst Out(LIFO)】

与线性表的区别
1:逻辑结构上:与普通线性表相同
2:但是在一些数据的运算上:插入删除操作有区别

栈的基本操作
【创建销毁】【都是O(1)时间复杂度】
InitStack(&S)初始化栈,构造一个空栈S,分配内存空间
DestroyStack(&S)销毁栈,并释放stackS所占的内存空间
【增、删】
push(&s,x)进栈,若栈s没满,栈顶指针先加一【++s.top】,再将x加入送到栈顶使之成为新栈顶
pop(&s,&x)出栈,若栈s非空,则弹出栈顶元素,并用x返回【这一个操作删除了栈顶元素】【先取栈顶元素值,再将栈顶指针减1【S.top–】】
【查】
GetTop(S,&x):读取栈顶元素,若栈s非空,则用x返回栈顶元素【与pop不同的是这一步只读取不删除】
【其他操作】
判空操作:StackEmpty(S):判断一个栈S是否为空,若S为空,则返回true,否则返回false。
判满操作:s.topMaxisize-1;
栈长:S.top+1
栈顶指针:S.top,初始时设置为S.top
-1;栈顶元素S.data[s.top]
请添加图片描述

【2】用顺序存储方式实现的栈
因为是顺序存储,其实和顺序表是一样的
类比得
[2-1]初始化栈

#define MaxSize 10
typedef struct{
ElemType data[maxsize];//静态数组存放栈中元素
int top;//栈顶指针
}Sqstack;
void testStack(){
SqStack S;//声明一个顺序栈(分配空间)
}
//判空操作
bool StackEmpty(sqStack S){
if(s.top==-1)//空
return true;
else//非空
return false;
//初始化栈
void InitStack(&S){
S.top=-1;//初始化栈顶指针
}

其中,顺序存储的方式给各个数据元素分配连续的存储空间,大小为MaxSize*sizeof(ElemType)
此时最大容量为10,那么就开辟十个空间+top元素的字节
而top的作用是指向栈顶元素,如果此时压入栈S4个元素,那么top此时=4.
但是初始时,top指向的是data【0】,而此时data【0】中还没有存放数据,所以这样是不合理的,合理的做法应该在初始时让top=-1。
【判空操作中体现了这一点】

【2-2】入栈操作

#define MaxSize 10
typedef struct{
ElemType data[maxsize];
int top;
}SqStack;
bool Push(SqStack &S,Elemtype x){
if(S.top==maxsize-1)//栈满
return false;
S.top=S.top+1;
S.data[S.top]=x;
//上两句相同效果的代码时是:S.data[++S.top]=x;
//将top指针先+1,例如从-1的位置移到下标为0的地方,再将数据放入其中。

【2-3】出栈操作

#define MaxSize 10
typedef struct{
ElemType data[maxsize];
int top;
}sqStack;
//出栈操作
bool Pop(SqStack &S,ElemType &x){
if(s.top==-1)
return false//栈空报错
x=S.data[s.top];//栈顶元素先出栈,将出栈数据赋值给x
s.top=s.top-1;//栈顶指针下移
return true;//这里的等价代码是x=S.data[s.top--]
}
//只是逻辑删除,其实数据还是存留在内存中

【针对2-2/2-3】【另一种方式】

#define MaxSize 10
typedef struct{
ElemType data[maxsize];
int top;
}sqStack;
//初始化栈
void InitStack(&S){
S.top=0;//初始指向0
}
bool StackEmpty(SqStack S){
if(S.top==0)
return true;
else
return false;
}
void testStack(){
SqStack S;//声明一个顺序栈(分配空间)
InitStack(S);
}
//在这个方式下,如果想要元素入栈时需要先讲元素放入其中再将指针变化,那么就和原先方式相反。
bool Push(SqStack &S,Elemtype x){//入栈
if(S.top==maxsize)//栈满
return false;
S.data[S.top]=x;//元素先入,因为是0对0这样的关系
S.top=S.top+1;//改变下标上移
//等价于S.data[s.top++]=x;
//同理,出栈时
bool Pop(SqStack &S,ElemType &x){
if(s.top==0)
return false//栈空报错
s.top=s.top-1;//栈顶指针下移
x=S.data[s.top];//栈顶元素出栈,将出栈数据赋值给x
return true;//这里的等价代码是x=S.data[--s.top]
}

这个处理方案与第一个不同的点在于对于栈顶指针的区别,影响到后续判空判满的操作。

【共享栈】
1:两个栈共享同一片内存空间,两个栈从两边往中间增长。
2:初始化:0号栈栈顶指针初始时top0=-1;1号栈栈顶指针初始时top1=MaxSize。
3:栈满条件:top0+1==top1

【3】用链式存储方式实现的栈
【如何理解?将链栈看作一个只能执行头插操作的链表,因此链表的头部就作为栈顶,尾部作为栈底】
用头插法建立单链表:进栈操作。
只能对头结点的“后删”操作:出栈操作
因此,可以实现带头结点和不带头结点的操作。
但局限的是:进栈/出栈都只能在栈顶一端进行(链头作为栈顶)
推荐使用不带头结点的实现方式。

将链表头部作为栈顶的一端,可以避免在实现数据 “入栈” 和 “出栈” 操作时做大量遍历链表的耗时操作。

链表的头部作为栈顶,意味着:
1:在实现数据"入栈"操作时,需要将数据从链表的头部插入;
2:在实现数据"出栈"操作时,需要删除链表头部的首元节点;
链栈的实现思路同顺序栈类似,顺序栈是将数顺序表(数组)的一端作为栈底,另一端为栈顶;链栈也如此,通常我们将链表的头部作为栈顶,尾部作为栈底,如图 1 所示:
链栈示意图
在这里插入图片描述

typedef struct lineStack{
    int data;
    struct lineStack * next;
}lineStack;
//stack为当前的链栈,a表示入栈元素
lineStack* push(lineStack * stack,int a){
    //创建存储新元素的节点
    lineStack * line=(lineStack*)malloc(sizeof(lineStack));
    line->data=a;
    //新节点与头节点建立逻辑关系
    line->next=stack;
    //更新头指针的指向
    stack=line;
    return stack;
}

链栈元素出栈
示意图
在这里插入图片描述

//栈顶元素出链栈的实现函数
lineStack * pop(lineStack * stack){
    if (stack) {
        //声明一个新指针指向栈顶节点
        lineStack * p=stack;
        //更新头指针
        stack=stack->next;
        printf("出栈元素:%d ",p->data);
        if (stack) {
            printf("新栈顶元素:%d\n",stack->data);
        }else{
            printf("栈已空\n");
        }
        free(p);
    }else{
        printf("栈内没有元素");
        return stack;
    }
    return stack;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值