一、栈的基本概念
1、首先栈是一种线性表,但这种线性表只能在一端进行插入和删除
2、后进先出(Last In First Out)
二、顺序栈
顺序栈:采用顺序存储的栈。它利用一组连续的存储单元存储元素,还有top指针指向栈顶元素
1.储类型描述
栈的顺序存储类型描述:
#define MaxSize 50
#define Elemtype int
//栈的顺序存储类型
typedef struct {
Elemtype data[MaxSize];
int top;
}SqStack;
栈顶指针:s.top, 初始设置s.top=-1
栈顶元素:s.data[s.top]
栈空条件:s.top==-1
栈满条件:s.top= =MaxSize-1(top指针指向栈顶元素)
栈长:s.top+1
带栈顶和栈底指针的存储类型:
//带栈顶和栈底指针的存储类型
typedef struct {
Elemtype* base;
Elemtype* top; //这里的top指向栈顶元素的下一个位置
int stacksize;
}SqStack2;
带栈顶和栈底指针的 栈满条件:S.top - S.base = = S.stacksize(top指向栈顶元素的下一个位置)
2.基本操作
1、初始化
//初始化
void InitStack(SqStack& s) {
s.top = -1;
}
//带栈底指针的初始化
Status InitStack2(SqStack2& s) {
s.base = new Elemtype[MaxSize];
if (!s.base)
return false;
s.top = s.base;
s.stacksize = MaxSize;
return true;
}
2、判栈空
//栈判空
bool StackEmpty(SqStack s) {
if (s.top == -1)
return true;
else
return false;
}
//带栈底指针的判空
bool StackEmpty2(SqStack2 s) {
if (s.top == s.base)
return true;
else
return false;
}
3、进栈
//进栈
bool Push(SqStack& s, Elemtype x) {
if (s.top == MaxSize - 1) //判断是否满栈;因为data[MaxSize]数组下标从0开始
return false;
s.data[++s.top] = x; //将元素压入栈顶,栈顶指针加一
return true;
}
//带栈底指针的进栈
int Push(SqStack& S, ElemType e)
{
if (S.top - S.base == S.stacksize)
return FALSE;
*S.top = e; //元素压入栈顶
S.top++; //栈顶指针加一
return OK;
}
4、出栈
//出栈
bool Pop(SqStack& s, Elemtype& x) {
if (s.top == -1) //判断是否栈空
return false;
x = s.data[s.top--]; //将元素赋值给x,栈顶指针减一
return true;
}
//带栈底指针的出栈
int Pop(SqStack& S, ElemType& e)
{
if (S.top == S.base) //if(StackEmpty(S))
return ERROR;
--S.top; //栈顶指针减一
e = *S.top; //栈顶元素赋值给e; 两步合起来*S.top++=e
return OK;
}
5、读栈顶元素
//读栈顶元素
bool GetTop(SqStack& s, Elemtype& x) {
if (s.top == -1) //判断是否栈空
return false;
x = s.data[s.top]; //返回栈顶元素,栈顶指针值不变
return true;
}
6、带栈底指针求栈长度
//带栈底指针求栈长度
int StackLength(SqStack2 s) {
return s.top - s.base;
}
7、带栈底指针清空栈
//带栈底指针清空栈
bool ClearStack(SqStack2 s) {
if (s.base)
s.top = s.base;
return true;
}
8、带栈底指针销毁栈
//带栈底指针销毁栈
bool DestoryStack(SqStack2 s) {
if (s.base) {
delete s.base; //delete:是释放用户开辟的空间,把数组回归内存
s.stacksize=0;
s.base = s.top = NULL; //置NULL是防止野指针的生成,野指针:访问一个已销毁或者访问受限的内存区域的指针
}
return true;
}
注:
栈顶指针初始化s.top=-1
进栈 :s.data[++s.top] = x;
出栈:x = s.data[s.top–];
栈顶指针初始化s.top=0
进栈 :s.data[s.top++] = x;
出栈:x = s.data[–s.top];
(主要是思想,算法的逻辑关系为主体。)
三、共享栈
1、利用栈底位置相对不变特性,可以让两个顺序栈共享一个一维数组空间
2、两个栈的栈顶指针都指向栈顶元素
判断栈空:
top0=-1时0号栈为空,
top1=MaxSize时1号栈为空
3、当且仅当两个栈顶指针相邻时
判断栈满:
top1-top0=1
4、0号栈进栈时top0先加一再赋值;1号栈进栈时top1先减一再赋值;
出栈相反
四、链栈
1、采用链式存储的栈称为链栈
2、优点:便于多个栈共享存储空间和提高效率
3、采用链式存储,便于结点的插入和删除
3、通常使用单链表实现,并操作在单链表的表头进行
4、这里链栈没有头结点,Lhead指向栈顶元素
1.储类型描述
栈的链式存储类型:
//链式存储类型描述
typedef struct Linknode {
Elemtype data; //数据域
struct Linknode* next; //指针域 用了嵌套定义
}StackNode, * LiStack;
1、链表的头指针就是栈顶(参考头插法理解)
2、不需要头结点
3、基本不存在栈满的情况
4、空栈相当于头指针指向空
5、插入和删除仅在栈顶执行
2.基本操作
链栈初始化:
//链栈初始化
bool InitStack(LinkStack& s) {
s = NULL; //构造一个空栈s,栈顶指针置空
return true;
}
判断链栈是否为空:
//判断链栈是否为空
bool StackEmpty(LinkStack s) {
if (s == NULL)
return true;
else
return false;
}
链栈入栈:
//链栈入栈
bool Push(LinkStack& s, Elemtype e) {
StackNode* p = (StackNode*)malloc(sizeof(StackNode));
p->data = e; //赋值
p->next = s; //插入栈顶
s = p; //修改栈顶指针
return true;
}
链栈出栈:
//链栈出栈
bool Pop(LinkStack& s, Elemtype& e) {
if (s==NULL) //栈空
return false;
e = s->data; //赋值
StackNode* p = (StackNode*)malloc(sizeof(StackNode));
p = s; //用p临时保存栈顶元素空间,以备释放
s = s->next; //修改栈顶指针
delete p; //释放栈顶元素的空间
return true;
}
取栈顶元素:
//取栈顶元素
Elemtype GetTop(LinkStack s) {
if (s != NULL) //栈非空
return s->data; //取元素
}