一、栈的概念
栈Stack:
是只允许在一端进行插入或删除的性表线。首先栈是一种线性表,但限定这种线性表只能在某一端进行插入和删除操作(类似于一个只有开口的瓶子)。(遵循LIFO原则)——后进先出(Last In First Out)
栈顶 Top:
进行插入或者删除的那一端
栈底 Bottom:
栈顶的另一端
空栈:
不含任何元素的空表
压栈 Push:
栈的插入操作(还可以叫入栈,进栈)
出栈 Pop:
栈的删除操作
图例说明:
如图,将数据一个一个压进去,相当瓶子,你倒水进去,就是先流入底部,一直到填满
你喝水,先喝的是上面的水,最后才喝底部的水,所以就是 先进后出
你想想弹夹,装子弹是不是也是先放的子弹,最后才打出来的
既然如此:像子弹一样,想用下一个就会Pop(出栈)掉这颗子弹
二、栈的实现:
涉及以下:
1、栈的初始化
2、压栈
3、出栈
4、判断是否为空
5、销毁
6、数据个数(可以写可以不写,我觉得写一下也行,能够知道压栈数据有几个)
7、取栈顶数据
以下三个数据结构都可以来创建栈
相比于双链表和单链表,更会倾向于单链表,因为栈是只有栈顶进行删除和增加,而双链表会比单链表每一个节点多一个指针
对于单链表:使用头插和尾删(尾插和头删)来实现压栈和出栈
但是更倾向于选择顺序表,因为它的缺点虽然是扩容,但是也不是每次都要扩容,而且cpu的缓存利用率也高
三、改写顺序表实现:
1、创建栈
如下:和顺序表相同,但是,第二个参数由size(有效数据个数改成了栈顶top)
2、初始化栈:
有个讲究了,对于top有2种取值,Top栈顶用来获取元素
若是栈顶是从0开始的话,那么,你放入第一个数据,此时Top=1,但是你的数据位于的下标是0 所以说Top指向的栈顶元素的下一个位置
若是栈顶从-1开始的话,同理,top=0,你的数据下标(元素)刚好是top指向的位置
如下图:
我就以top=0为例子了,另一种可以自己去试试
3、压栈:
在顺序表时,都是一个原理,assert使用以及realloc使用之前就已经讲过了,不做赘述
因为用的top=0,所以先将数据放入,再top++
4、出栈:
简单多了,和尾删差不多,就是直接将最后一个数据下标删掉,要断言一下top是否为0,因为你在>0的时候进来,就如:top为1,进入删除以后为0,下次再Pop就不对了,因为若是为0了,就不能删了,因为进来时top的值就是0,那么出栈以后top的初始值不能变
5、判空:
用到了bool,若是为真(true)则返回1,假(false)返回0 还要一个<stdbool.h>的头文件
我用的p->top==0,若是不等于0,则为假,返回0.若是等于 0则为真,返回1;
6、销毁:
栈的使用就是一次性,出栈一次就会走掉一个数据;因此出栈一个数据就会向底部走,最后Top会回到进来时的样子,然后销毁即可
7、数据个数:
让我们知道有几个数据
8、取出栈顶数据:
无非就是该位置数据传回去:
9、测试:
它的打印,不像之前顺序表的打印,用创建一个SLPrin()来遍历打印;而是通过while循环,每次进入循环都要Pop掉一个,因为我们的Pot指向的是下一个数据的位置,所以先Pop,再打印(防止越界访问哦)
四、分装代码:
Stack.h文件:
#pragma once#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<assert.h>typedef int LTDataType;
//顺序表(栈)
typedef struct SL
{LTDataType* a;int top;int capacity;
}SL;
//入栈
void SLPush(SL* p, LTDataType x);
//出栈
void SLPop(SL* p);
//初始化
void SLInit(SL* p);
//销毁
void SLDestroy(SL* p);
//判空
bool SLEmpty(SL* p);
//数据个数
int SLsize(SL* p);
//取数据
LTDataType SLPot(SL* p);
Stack.c文件:
#define _CRT_SECURE_NO_WARNINGS 3#include"Stack.h"//入栈
void SLPush(SL* p,LTDataType x)
{//不能传NULL,判空;assert(p);if (p->top == p->capacity){//先判断是否为0,好进行扩容int newnode = p->capacity == 0 ? 4 : 2 * (p->capacity);//扩容;创建一个临时变量接收新的空间,成功在将其交给p->a;LTDataType* s = (LTDataType*)realloc(p->a,newnode * sizeof(LTDataType));if (s == NULL){perror("realloc");return;}p->a = s;p->capacity = newnode;}p->a[p->top] = x;//指向下一个数据地址p->top++;
}
//出栈(类似尾删)
void SLPop(SL* p)
{//是否为空assert(p);assert(p->top > 0);p->top--;
}
//初始化
void SLInit(SL* p)
{p->a = NULL;p->capacity = 0;//p->top = -1;//指向栈顶的数据p->top = 0;//指向栈顶的下一个数据
}
//销毁
void SLDestroy(SL* p)
{assert(p);free(p->a);p->a = NULL;p->capacity = p->top = 0;
}
//判空
bool SLEmpty(SL* p)
{//不能是空地址assert(p);//为0就是真(true),为1就是假(flase)return p->top == 0;
}
//数据个数
int SLsize(SL* p)
{int size = p->top;return size;
}
//取数据:
LTDataType SLPot(SL*p)
{assert(p);return p->a[p->top];
}
test.c文件:
#define _CRT_SECURE_NO_WARNINGS 3
#include"Stack.h"int main()
{SL pts;//初始化SLInit(&pts);//入栈SLPush(&pts, 1);SLPush(&pts, 2);SLPush(&pts, 3);SLPush(&pts, 4);while (!SLEmpty(&pts)){//指向的是下一个数据的位置,此时在栈顶,我要将先控制到数据位置;SLPop(&pts);printf("%d ",SLPot(&pts));}SLDestroy(&pts);return 0;
}
希望对你有帮助,今天也要好好学习哦!!!