目录
一、栈的类型定义
栈(stack)是限定仅在表尾进行插入或删除操作的线性表。表尾称为栈顶(top),表头称为栈底(bottom),不含元素的表称为空栈。
栈的特点除了具有线性表的所有特点之外,还有自己特有的性质,它具有后进先出(last in first out,简称LIFO)的结构。
入栈:插入元素的操作;出栈:删除栈顶元素的操作。
上溢:栈已满,又要压入元素(这是一种错误情况,使问题的处理无法进行);下溢:栈已空,还要弹出元素(这一般认为是一种结束条件,即问题处理结束)。
栈有两种表示方式(存储方式):一种是顺序结构存储,我们叫做顺序栈;另一种是链式结构存储,我们叫做链栈。这会在后面分别介绍。
栈的基本操作:
InitStack(&S) // 构造一个空栈S
DestroyStack(&S) // 销毁栈S
ClearStack(&S) // 将S清为空栈
IsEmpty(S) // 判断栈S是否为空栈
StackLength(S) // 返回栈S的长度
GetTop(S, &e) // 返回S的栈顶元素
Push(&S, e) // 插入元素e为新的栈顶元素
Pop(&S, &e) // 删除S的栈顶元素并用e返回
二、顺序栈
顺序栈是利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时附设指针 top 指示栈顶元素的顺序栈中的位置。
顺序栈的图示结构:
顺序栈的代码实现:
1.存储结构:
#define STACK_INIT_SIZE 100 // 存储空间初始分配量
#define STACKINCREMENT 10 // 存储空间分配增量
typedef struct{
SElemType *base; // 在栈构造之前和销毁之后,base的值为NULL
SElemType *top; // 栈顶指针
int stacksize; // 当前已分配的存储空间,以元素为单位
}SqStack;
2.初始化操作:
Status InitStack(SqStack &S){
// 构造一个空栈
S.base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));
if(!S.base) exit(OVERFLOW); // 存储分配失败
S.top = S.base; // 初始化栈顶指针与栈底指针相等
S.stacksize = STACK_INIT_SIZE; // 初始化栈的空间大小
return OK;
} // InitStack
时间复杂度:
3.销毁操作:
Status DestroyStack(SqStack &S){
// 销毁顺序栈S
if(S.base){
free(S.base); // 释放栈底的内存
S.stacksize = 0; // 将栈的大小变为0
S.base = S.top = NULL; // 将栈顶指针和栈底指针设置为空
}
return OK;
} // DestroyStack
时间复杂度:
4.清空操作:
Status ClearStack(SqStack &S){
// 将顺序栈S清为空栈
if(S.base) S.top = S.base;
return OK;
} // ClearStack
时间复杂度:
5.入栈操作:
Status Push(SqStack &S, SElemType e){
// 插入元素e为新的栈顶元素
if(S.top - S.base >= S.stacksize){ // 栈满,追加存储空间
S.base = (SElemType *)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(SElemType));
if(!S.base) exit(OVERFLOW); // 存储分配失败
S.top = S.base + S.stacksize;
S.stacksize += STACKINCREMENT;
}
*S.top++ = e; // 即(1)*S.top = e;(2)S.top++;
return OK;
} // Push
时间复杂度:
6.出栈操作:
Status Pop(SqStack &S, SElemType &e){
// 删除S的栈顶元素,并用e返回其值
if(S.top == S.base) return ERROR; // 判断是否栈为空
e = *--S.top; // 即(1)S.top--;(2)e = *S.top;
return OK;
} // Pop
时间复杂度:
7.判断空栈:
int StackLength(SqStack S){
// 求顺序栈S中元素的个数,即顺序栈S的长度
return S.top - S.base;
} // StackLength
时间复杂度:
8.求长度操作:
int StackLength(SqStack S){
// 求顺序栈S中元素的个数,即顺序栈S的长度
return S.top - S.base;
} // StackLength
时间复杂度:
9.取栈顶操作:
Status GetTop(SqStack S, SElemType &e){
// 取栈顶元素,且不删除该元素,用e返回其值
if(S.top == S.base) return ERROR; // 栈为空
e = *(S.top - 1);
return OK;
} // GetTop
时间复杂度:
三、链栈
链栈是运算受限的单链表,只能在链表头部进行操作,即插入和删除仅在栈顶执行。为了操作方便,链栈中指针的指向为从栈顶到栈底,头指针指向栈顶,不带头结点。空栈相当于头指针指向空。
链栈的图示结构:
链栈的代码实现:
1.存储结构:
typedef struct StackNode{
SElemType data; // 数据元素
struct StackNode *next; // 指针元素
}StackNode, *LinkStack;
2.初始化操作:
Stack InitStack(LinkStack &S){
// 构造一个空的链栈S,栈指针为空
S = NULL;
return OK;
} // InitStack
时间复杂度:
3.判断为空:
Stack InitStack(LinkStack &S){
// 构造一个空的链栈S,栈指针为空
S = NULL;
return OK;
} // InitStack
时间复杂度:
4.入栈操作:
Status Push(LinkStack &S, SElemType e){
// 将元素e插入链栈S的栈顶
p = (LinkStack)malloc(sizeof(StackNode)); // 生成新节点p
p->data = e; // 将新节点数据赋为e
p->next = S; // 将新节点插入栈顶
S = p; // 修改栈顶指针
return OK;
} // Push
时间复杂度:
5.出栈操作:
Status Pop(LinkStack &S, SElemType &e){
// 将栈顶元素出栈并赋给e返回其值
if(S == NULL) return ERROR; // 栈为空
e = S->data; // 值赋给e
p = S; // p指向栈顶
S = S->next; // S指向新栈顶
free(p); // 释放栈顶元素
reutrn OK;
} // Pop
时间复杂度:
6.取栈顶操作:
SElemType GetTop(LinkStack S){
// 返回栈顶元素
if(S != NULL) return S->data;
} // GetTop
时间复杂度:
总结
基于栈自身后进先出的特点,常用于实现递归的操作,递归每一层时存入栈中再进入下一层,最终再一步一步回退,这是栈的最明显的优点。
除此之外,栈的基本操作时间复杂度都很小,但缺点是只能在栈顶进行操作。