目录
栈的定义
概念
栈 (stack) 是限定仅在表尾进行插入或删除操作的线性表。 因此, 对栈来说, 表尾端有其特殊含义, 称为栈顶 (top), 相应地, 表头端称为栈底 (bottom)。 不含元素的空表称为空栈。
栈又称为后进先出 (Last In First Out, LIFO) 的线性表。
栈的抽象数据类型定义
ADT Stack {
数据对象:
D = {ai | ai ∈ElemSet , i = 1,2,...,n,n≥0 }
数据关系:
R1 = { <ai-1, ai > | ai-1, ai ∈ D, i=2,...,n }
约定an端为栈顶,a1端为栈底。
基本操作:初始化、进栈、出栈、取栈顶元素等
}ADT Stack
顺序栈的基本操作
存储方式
同一般线性表的顺序存储结构完全相同。
利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。栈底一般在低地址端。
· 附设top指针,指示栈顶元素在顺序栈中的位置。
· 另设base指针,指示栈底元素在顺序栈中的位置。
(但是,为了方便操作,通常top指示真正栈顶元素之上的下标地址。)
· 另外,用stacksize表示栈可使用的最大容量。
空栈:base==top是栈空标志。
栈满:top-base==stacksize。
下图所示栈中元素和栈指针之间的关系
顺序栈的表示
#define MAXSIZE 100
typedef struct {
SElemType *base; // 栈底指针
SElemType *top; // 栈顶指针
int stacksize; // 栈可用最大容量
} SqStack;
顺序栈的初始化
Status InitStack(SqStack &S) { // 构造一个空栈
S.base = new SElemType[MAXSIZE]; // 或S.base = (SElemType*)malloc(MAXSIZE*sizeof(SelemType));
if (!S.base) exit(OVERFLOW); // 存储分配失败
S.top = S.base; // 栈顶指针等于栈底指针
S.stacksize = MAXSIZE;
return OK;
}
顺序栈判断是否为空
栈顶指针等于栈底指针,就认为顺序栈为空
Status StackEmpty(SqStack S) {
// 若栈为空,返回TRUE;否则返回FALSE
if (S.top == S.base)
return TRUE;
else
return FALSE;
}
清空顺序栈
清空顺序栈就是将栈顶指针移到栈底指针。
Status ClearStack(SqStack S) {
if(S.base) S.top = S.base;
return OK;
}
销毁顺序栈
Status DestroyStack(SqStack &S) {
if(S.base) {
delete S.base;
S.stacksize = 0;
S.base = S.top = NULL;
}
return OK;
}
顺序栈的入栈
① 判断是否栈满,若满则出错(上溢)。
② 元素e压入栈顶。
③ 栈顶指针加1。
Status Push(SqStack &S, SElemType e) {
if(S.top - S.base == S.stacksize) // 栈满
return ERROR;
*S.top++=e; // 可以分开写,*S.top=e;S.top++;
return OK;
}
顺序栈的出栈
① 判断是否栈空,若空则出错(下溢)。
② 获取栈顶元素e。
③ 栈顶指针减1。
Status Pop(SqStack &S, SElemType &e) {
// 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
if(S.top == S.base) // 等价于if(StackEmpty(S))
return ERROR;
e = *--S.top; // 可分解为两步--S.top; e = *S.top;
return OK;
}
取顺序栈的栈顶元素
SElemType GetTop(SqStack S) {
// 返回S的栈顶元素,不修改栈顶指针
if(S.top != S.base) // 栈非空
return *(S.top-1) // 返回栈顶元素的值,栈顶指针不变
}
链栈的基本操作
链栈的表示
链栈是运算受限的单链表,只能在链表头部进行操作。
· 链表的头指针就是栈顶。
· 不需要头结点。
· 基本不存在栈满的情况。
· 空栈相当于头指针指向空。
· 插入和删除仅在栈顶处执行。
注意:链栈中的指针方向。
typedef struct StackNode {
SElemType data;
struct StackNode *next;
} StackNode, *LinkStack;
LinkStack S;
链栈的初始化
void InitStack(LinkStack &S) {
// 构造一个空栈,栈顶指针置为空
S=NULL;
return OK;
}
判断链栈是否为空
Status StackEmpty(LinkStack S) {
if(S==NULL) return TRUE;
else return FALSE;
}
链栈的入栈
Status Push(LinkStack &S, SElemType e) {
p = new StackNode; // 生成新结点p
p -> data = e; // 将新结点数据域置为e
p -> next = S; // 将新结点插入栈顶
S = p; // 修改栈顶指针
return OK;
}
链栈的出栈
Status Pop(LinkStack &S, SElemType &e) {
if (S == NULL) return ERROR;
e = S -> data;
p = S;
S = S -> next;
delete p;
return OK;
}
取栈顶元素
SElemType GetTop(LinkStack S) {
if (S != NULL)
return S -> data;
}
栈与递归
分治法求解递归问题算法的一般形式
void p (参数表) {
if(递归结束条件) 可直接求解步骤; ----- 基本项
else p(较小的参数); -----归纳项
}
例如
long Fact(long n) {
if(n == 0) return 1; // 基本项
else return n * Fact(n-1); // 归纳项
}
尾递归转变为循环结构
long Fact(long n) {
if(n == 0) return 1;
else return n * Fact(n-1);
}
以上递归可以写成循环结构
long Fact(long n) {
t = 1;
for(i = 1; i <= n; i++) t = t*i;
return t;
}
单向递归转变为循环结构
虽然有一处以上的递归调用语句,但各次递归调用语句的参数只和主调函数有关,相互之间参数无关,并且这些递归调用语句处于算法的最后。
long Fib(long n) { // Fibonacci数列
if(n==1||n==2) return 1;
else return Fib(n-1) + Fib(n-2);
}
转换为循环结构
long Fib(long n) {
if(n==1||n==2) return 1;
else {
t1 = 1; t2 = 1;
for(i=3;i<=n;i++){
t3=t1+t2;
t1=t2;t2=t3;
}
return t3;
}
}