栈
一、栈的逻辑结构
- 栈:限定仅在表尾进行插入和删除操作的线性表。
- 空栈:不含任何数据元素的栈。
- 抽象数据类型定义
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端为栈底。 基本操作: InitStack (&S) (构造空栈 ) 操作结果:构造一个空栈S. DestroyStack(&S) (销毁栈结构) 初始条件:栈S已存在。 操作结果:栈S被销毁。 ClearStack (&S) (栈清空) 初始条件:栈S已存在。 操作结果:栈S清为空栈。 StackEmpty (S) (判空) 初始条件:栈S已存在。 操作结果:若栈S为空栈,则返回TRUE, 否则FALSE. StackLength (S) (求栈长) 初始条件:栈S已存在。 操作结果:返回S的元素个数,即栈的长度。 StackTraverse (S, visit( )) (遍历栈) 初始条件:栈S已存在且非空。 操作结果:从栈底到栈顶依次对S的每个数据元素调用函数visit()。一旦visit()失败,则操作失效。 GetTop (S, &e) (求栈顶元素) 初始条件:栈S已存在且非空。 操作结果:用e返回S的栈顶元素。 Push (&S, e) (入栈) 初始条件:栈S已存在。 操作结果:插入元素e为新的栈顶元素。 Pop (&S, &e) (出栈) 初始条件:栈S已存在且非空。 操作结果:删除S的栈顶元素,并用e返回其值。 }
二、顺序存储结构及实现
!S.base时未初始化
S.top==S.base时为空
S.top-S.base>=S.stacksize时栈满
结构示意
#define STACK_INIT_SIZE 100;
#define STACKINCREMENT 10;
typedef struct {
SElemType *base;
SElemType *top;
int stacksize;
}SqStack;
也有使用一个整数top,S.base[top]访问元素的做法
实现
- 初始化栈
bool InitStack(SqStack *S){ S->base = (int*)malloc(STACK_INIT_SIZE*sizeof (int)); if (!S->base) return false; S->top = S->base; S->stacksize = STACK_INIT_SIZE; return true; }
- 销毁栈结构
bool Destory(SqStack *S){ free(S->base); S->base = NULL; return true; }
- 清空栈
bool Clean(SqStack *S){ if (S->base==S->top) { return false; //本身为空,不需要再次清空 }else { for (S->top; S->top > S->base + 1; S->top--) { free(S->top - 1); } S->top = S->base; // or S->top-=1; S->stacksize = STACK_INIT_SIZE; //可能超出初始化值,在此重置 return true; } }
- 栈判空
bool Judgment(SqStack *S){ if (S->base==S->top) { return false; }else return true; }
- 求栈长度
int Length(SqStack *S){ if (S->base == S->top){ return 0; }else{ int i = 0; int *p = S->top; while (p > S->base) { p = p - 1; i++; } return i; } }
- 入栈
bool Push(SqStack *S,int x){ if (S->top - S->base >=S->stacksize) { S->base = (int *)realloc(S->base,(STACK_INIT_SIZE + STACKINCREMENT)*sizeof(int*)); if (S->base) { return false; } S->top = S->base + S->stacksize; } /* *S->top = x; S->top =S->top+1; */ *S->top++ = x; return true; }
- 出栈
bool Pop(SqStack *S){ if (S->base == S->top) { return false; } else { cout <<*(--S->top); return true; } }
- 输出所有元素不删除
bool OutPut(SqStack *S){ if (S->base == S->top) { cout<<"栈为空,无元素可输出!"<<endl; return false; }else { int *p = S->top; while (p > S->base) { cout<<*(--p)<<" "; } cout<<endl; return true; } };
- 进制转换
void Conversion(SqStack *S,int i,int t){ while(i) { Push(S, i%t); i=i/t; } while (Judgment(S)) { Pop(S); } cout<<endl; }
三、链式存储结构
结构示意
typedef struct SNODE
{
SElemType data;
struct snode *link;
}SNODE,*LinkStack;
LinkStack top, p;
int m=sizeof (SNODE);
以头指针为栈顶,在头指针处插入或删除!
实现(略)
- 入栈
- 出栈
四、顺序栈和链栈的比较
- 时间性能:相同,都是常数时间O(1)。
- 空间性能:
- 顺序栈:有元素个数的限制和空间浪费的问题。
- 链栈:没有栈满的问题,只有当内存没有可用空间时才会出现栈满,但是每个元素都需要一个指针域,从而产生了结构性开销。
- 总之,当栈的使用过程中元素个数变化较大时,用链栈是适宜的,反之,应该采用顺序栈。
栈的应用举例
数制转换
括号匹配检验
表达式求值
行编辑程序
迷宫求解
栈与递归的实现
汉诺塔问题