4.栈及其实现
线性逻辑结构:1.线性表---->顺序表(存储结构) 链表(存储结构)
线性逻辑结构: 2.栈------->顺序栈 (存储结构) 链式栈(存储结构)
线性表:增删改查 可以在线性表的任意位置进行操作-------太灵活
对线性表进行一些约束,一般来说对线性表进行两种约束,这两种约束分别得到另外的两个逻辑结构:一个是栈,另一个是队列
栈:限制在一端进行插入或者删除操作的线性表(俗称“堆栈”),中间也不许操作
允许进行操作的一端称为“栈顶”
不允许进行操作的一段称为“栈底”
栈中没有数据时 称为“空栈”
栈性质:先进后出/后进先出------>弹夹
栈顶是最后放入的数据(并不是指空间最上面的数据)
栈顶“指针”:标记栈顶的位置
(这个指针具有什么数据类型,取决于它保存啥--->如果是下标,则为int类型,“指针”只是说来用一下代指的,只是把名字拿来用,而不是它的含义,即变量)
栈底“指针”:标记栈底的位置
应用场景:
- 函数调用的实现(递归)
- 业务需求,倒着走的逻辑:历史记录 网页退回 撤销
- 物理上的堆栈
(堆)栈:先进后出的
堆
- C++ STL库 stack
栈的实现:初始化,入栈(添加一个元素),出栈(删除一个元素--->只能删除栈顶元素),判空/判满,得到栈顶元素
#define maxsize 10
顺序存储结构实现:顺序栈--->基于数组实现data[10]
初始化一个栈:(1)存储数据的东西---数组 (2)栈顶”指针”top:(下标)int类型
栈底指针 固定指向下标0,不需要单独的变量标识
栈顶指针指向什么位置:
(1)指向真正的栈顶元素
初始化:top=-1(等于0代表0下标有元素)
入栈:top++,data[top]=k;
出栈:top--
判满:top==maxsize-1
判空:top==-1
- 指向真正栈顶元素的下一个位置
初始化:top=0
入栈:data[top]=k,top++;
出栈:top--
判满:top==maxsize
判空:top==0
得到栈顶元素:return data[top-1];
链式存储结构实现:链栈--->基于单链表实现:带头结点的单链表
把头结点端 当作栈顶:真正的栈顶元素在首元结点中
- 栈顶“指针”:头指针
- 初始:初始化一个带头结点的空的单链表
- 入栈:头插法
- 出栈:删除首元结点
- 得到栈顶元素:输出首元结点的数据
溢出:
顺序栈:上溢:栈满之后,继续入栈
下溢:栈空之后,继续出栈
链栈: 动态分配不存在上溢
存在下溢:栈空之后,继续出栈
顺序栈代码:
#include<stdio.h>
#include<stdlib.h>
#define maxsize 10
///顺序栈///
/*typedef struct{
int date[maxsize];//栈中元素
int top;// 栈顶指针
}sstack;*/
typedef struct{
int *date;//栈中元素
int top;// 栈顶指针
}sstack;
//---------------------------------------------------------------------
//栈操作:初始化,判空,入栈(push),出栈(pop),读取栈顶元素(top)
//以顺序栈为例子
//初始化
sstack initstack()
{
sstack s;
s.date =(int*)malloc(sizeof(int)*maxsize);
s.top=-1;//初始化栈顶指针
//也有 s.top=0
// =-1,先加后赋值,指针指向栈顶元素
//=0,先赋值再加,指针指向 栈顶元素的下一个存储单元
return s;
}
//入栈--对栈进行了修改,指针传递
void Pushh(sstack *s,int k)
{
//栈空间固定,加入一个元素要先判断是否满了,还能不能加入
if(s->top==maxsize-1)
{
printf("栈满\n");//栈满报错,不能加入
}
else{
s->top++;
s->date[s->top]=k;
//s.date [++s.top]=k;
}
}//若top初始化为0,如何改写
//出栈
void Popp(sstack *s)
{
//删除之前判空,还有没有的删
if(s->top==-1)
{
printf("栈空\n");//栈空报错,不能删除
}
else{
s->top--;
}
}//若top初始化为0,如何改写
//读取栈顶元素
void gettop(sstack s)
{
int x;
//判空,还有没有的读取
if(s.top==-1)
{
printf("栈空\n");//栈空报错,不能删除
}
else{
x=s.date[s.top];
printf("%d\n",x);
}
} //若top初始化为0,如何改写
int main()
{
int x;
sstack s=initstack();
Pushh(&s,1);
Pushh(&s,2);
Pushh(&s,3);
gettop(s);
Popp(&s);
gettop(s);
Popp(&s);
gettop(s);
Pushh(&s,4);
gettop(s);
return 0;
}
链栈代码:
#include<stdio.h>
#include<stdlib.h>
///链栈--单链表实现栈///
typedef struct listackNode{//链栈结点
int date;//链栈结点中元素
struct listackNode* next;//指针
}sstack,*listack;
//此处的listack与链表代码中的 linklist类似
//listack==sstack *;
//使用 listack 声明链栈中的结点指针,意在强调操作对象是栈;
//使用 sstack* 声明链栈中的结点指针,意在强调操作对象是栈中的某个结点
//头指针相当于 栈顶指针
//直接在链表表头进行操作,链表表头相当于栈顶
//入栈:头插。 出栈:删除首元结点
//---------------------------------------------------------------------
//栈操作:初始化,判空,入栈(push),出栈(pop),读取栈顶元素(top)
//初始化
listack initstack()
{
listack s=(listack)malloc(sizeof(sstack));
s->next=NULL;
return s;
}
//入栈--对栈进行了修改,指针传递
void Pushh(listack s,int k)
{
//链表结点个数可以动态调整,无需判满
//头插法插入结点,实现入栈
sstack *p=(sstack*)malloc(sizeof(sstack));//p是待插入的新结点
p->date =k;
//头插法 插入p
p->next =s->next;
s->next =p;
}
//出栈
void Popp(listack s)
{
//删除之前判空,还有没有的删
if(s->next==NULL)
{
printf("栈空\n");//栈空报错,不能删除
}
else
{
sstack *p=s->next ;//让p指向待删除的首元结点
s->next =p->next ;
free(p);
p=NULL;
}
}
//读取栈顶元素
void gettop(listack s)
{
int x;
//判空,还有没有的读取
if(s->next==NULL)
{
printf("栈空\n");//栈空报错,不能删除
}
else
{
x=s->next->date;
printf("%d\n",x);
}
}
int main()
{
int x;
listack s=initstack();
Pushh(s,1);
Pushh(s,2);
Pushh(s,3);
gettop(s);
Popp(s);
gettop(s);
return 0;
}