一、栈:
只能在一端插入或删除的线性表,允许插入和删除操作的一端称为栈顶(top),栈底固定不变。
栈的操作:
入栈、出栈。
栈的特点:
先进后出,就像车队进入死胡同,只能后车先出。
栈的本质:
线性表
栈中n个元素的出栈序列种数:
组合 C(2n,n)/(n+1) = 2n! / (n!)^2 / (n+1)
二、顺序栈:
实质是数组,需要连续的空间。
顺序栈的状态:
栈空(top = -1)、栈满top = (MaxSize - 1)、非法状态(上溢、下溢)
上溢:
栈满仍继续入栈
下溢:
栈空仍出栈
顺序栈实现
#include<malloc.h>
#include<stdio.h>
//顺序栈:数组栈,用top指针,指向栈顶,判断栈是否为空或者栈满。
#define MaxSize 10 //栈的空间
//栈
typedef struct SequenceStack{
int data[MaxSize];
int top; //初始值为-1,表示栈空
}SequenceStack;
//判断栈是否为空
void isEmptyStack(SequenceStack stack)
{
if(stack.top==-1)
{
printf("栈为空\n");
}
else{
printf("栈不为空\n");
}
}
//元素出栈操作
void popStack(SequenceStack &stack,int &k) //k表示要pop的元素返回处
{
if(stack.top!=-1)//栈不为空
{
k=stack.data[stack.top];
--stack.top;
printf("执行出栈操作,出栈元素%d\n",k);
return;
}
printf("执行出栈操作失败\n");
return;
}
//元素进栈操作
void pushStack(SequenceStack &stack,int k) //k表示要push进栈的元素
{
if(stack.top!=MaxSize-1)//栈不满
{
stack.data[++stack.top]=k;
printf("执行进栈操作,进栈元素%d\n",k);
return;
}
printf("未执行进栈操作\n");
return;
}
void printEle(SequenceStack stack)
{
int i=0;
printf("栈中元素:");
while(i<stack.top+1)
{
printf("%d ",stack.data[i]);
++i;
}
printf("\n");
}
//初始化顺序栈
void initStack(SequenceStack &stack)
{
stack.top=-1;//初始化栈顶 ,只向栈中添加 MaxSize-5 个元素
for(int i=0;i<MaxSize-5;++i)
{
pushStack(stack,i+1);//执行进栈操作
}
printf("初始化栈成功\n");
}
int main()
{
SequenceStack squ;
initStack(squ); //初始化栈
isEmptyStack(squ); //判断栈是否为空
printEle(squ); //查看栈中元素
pushStack(squ,33); //元素进栈
printEle(squ);
int result=-1;
popStack(squ,result); //元素出栈
printEle(squ);
}
顺序栈简单化
int stack[maxSize];
int top=-1;//初始化栈顶元素
//进栈
stack[++top]=x;
//出栈
ele=stack[top--];
//判空
top>-1?0:1;
三、链栈 :
实质是一个链表,用头插法创建链栈,有一个头结点,头结点相当于链栈的栈顶指针,指向栈顶元素,控制着栈中元素的出入。(由于是动态创建,默认栈空间为无限大,不存在溢出情况)
链栈的状态:(默认内存无限大)
栈空、栈满、
Coding
//链栈单个结点 ( 用带头结点的单链表作为存储体)
typedef struct Node{
int data; //数据域
struct Node *next; //指针域
}StackNode;
//链栈进栈
void pushChainStack(StackNode *&head,int data){
StackNode *elem=(StackNode *)malloc(sizeof(StackNode));
elem->data=data;
elem->next=head->next;//将之前插入栈中的元素链在新加入的元素后面。
head->next=elem; //将head头指针,指向这个新加入的元素,代表栈顶。
printf("元素%d进栈成功\n",head->next->data);
}
//链栈出栈
void popChainStack(StackNode *&head){
if(head->next==NULL)
{
printf("栈空\n");
return;
}
StackNode *elem;
elem=head->next;
head->next=elem->next;//将栈顶指向下一个元素。
printf("元素%d出栈成功\n",elem->data);
free(elem);//释放栈顶元素,表示出栈
}
//因为栈只有一端能进行操作,因此采用头插法创建链栈,head即栈顶。
void initChainStack()
{
StackNode *head;
head=(StackNode *)malloc(sizeof(StackNode));
head->next=NULL;
int data[3]={1,2,3};//即将进栈的数据
for(int i=0;i<3;i++)
{
pushChainStack(head,data[i]);//元素进栈
}
for(int i=0;i<3;i++)
{
popChainStack(head);//元素出栈
}
}
int main()
{
initChainStack();//链栈
}
四、共享栈
两个顺序栈共享一片连续的储存空间,两个栈的栈底分别位于储存空间的两端,栈顶在储存空间内,当两个栈顶相遇时,表示储存空间用尽,栈溢出(上溢)。
目的:提高内存利用率减少溢出可能性。
五、栈操作序列合法判定
给定序列是否合法规则( “I”表示进栈,“O”表示出栈 ):1.在给定操作序列中I的个数与O个数相等
2.给定序列开始到给定序列任意位置I个数大于等于O个数
void sequenceValid(char op[]) //op表示操作序列,I进栈,其它字符表示出栈
{
int res=0; //存储栈中情况
int i=-1,j;
while(op[++i]!='\0')
{
if(res<0) //当情况为负值即序列出栈数多于进栈数 或者 出栈的时候栈中元素为空
{
printf("序列不合法\n");
return;
}
if(op[i]=='I') //为I表示进栈
{
res++;
}
else //其他情况表示出栈
{
res--;
}
}
if(res!=0) //整个操作最终让栈空。res==0
{
printf("序列不合法\n");
return;
}
printf("序列合法\n");
}