您的位置:首页 > 理论基础 > 数据结构算法

重读数据结构之--栈

2013-12-06 23:16 330 查看
四 栈

1.栈的定义 限定仅在一端可以进行插入和删除操作的线性表,该端称为栈顶(top),另一端称为栈底(bottom)。栈是后进先出的线性表,简称LIFO结构。

2.操作 插入称为进栈 push;删除称为出栈 pop,应有的操作列表如下:

InitStack(*S) 初始化一个空栈S

DestoryStack(*S) 栈存在时,销毁它

ClearStack(*S) 将栈清空

StackEmpty(S) 若栈为空,返回true,否则返回false

GetTop(S,*e) 若栈存在且非空,用e返回S的栈顶元素

Push(*S,e) 若栈S存在,插入新元素e到栈S中并成为栈顶元素

Pop(*S,e) 删除栈S中的栈顶元素,并用e返回其值

StackLength(S) 返回栈S的元素个数

3.栈的两种物理结构

①栈的顺序存储结构

1.也就是数组来实现栈,把下标为0的一端作为栈底,在数组尾部进行插入和删除。并规定栈存在一个元素时top为0,因此空栈时top=-1.

2.结构体表示:

//顺序栈的定义
typedef int SElemType;
typedef struct{
SElemType data[MAXSIZE];
int top;   //指向栈顶的指针
}SqStack;

3.进出栈的实现:

//顺序栈的进栈操作
Status Push(SqStack *S,SElemType e){
if(S->top==MAXSIZE-1)
return ERROR;
S->top++;
S->data[S->top]=e;
return OK;
}

//顺序栈的出栈操作
Status Pop(SqStack *S,SElemType *e) {
if(S->top==-1)
return ERROR;
*e=S->data[S->top];
S->top--;
return OK;
}

4.双栈共享数组空间,提高空间使用率

①定义 一个数组放入两个栈,数组的两端分别是两个栈的栈底,两个栈的进栈相当于从数组两端向中间靠拢。(假设1栈在左,2栈在右)

②双栈的边界判断

1.数组满的时候也就是双栈Top指针碰头的时候,此时top1+1=top2。

2.top1=-1 和top2=n是两个栈栈空。

3.top1=n-1 说明栈1满而栈2空; top2=0说明栈2满栈1空。

③结构体表示

//顺序双栈的结构体表示
typedef struct{
SElemType data[MAXSIZE];
int top1;
int top2;
}SqDouleStack;
//双栈的进栈,需要一个栈号表示哪一个栈
Status Push(SqDouleStack *S,SElemType e,int stackNumber){

if(S->top1==MAXSIZE-1 || S->top2==0 || S->top1+1==S->top2)
return ERROR;
if(stackNumber==1){
S->data[++S->top1]=e;
}
else if(stackNumber==2){
S->data[--S->top2]=e;
}
return OK;
}

//双栈的出栈
Status Pop(SqDouleStack *S,SElemType *e,int stackNumber){
if(stackNumber==1){
if(S->top1==-1)
return ERROR;
*e=S->data[S->top1];
S->top1--;

}else if(stackNumber==2){
if(S->top2==MAXSIZE-1)
return ERROR;
*e=S->data[S->top2];
S->top2++;
}
return OK;
}

②栈的链式存储结构

1.单链表实现栈,优点明显,不会有栈满的情况。头指针当做栈顶指针。因此头结点失去意义。

2.链栈的结构体表示

//单链栈结构体表示

//链栈结点结构体
typedef struct StackNode{
SElemType data;
struct StackNode *next;
}StackNode,*LinkStackPtr;

//链栈结构体
typedef struct LinkStack{
LinkStackPtr top;
int count;
}LinkStack;

3.链栈的进出栈操作

//链栈的进栈操作
Status Push(LinkStack *S,SElemType e){
LinkStackPtr p=(LinkStackPtr)malloc(sizeof(LinkStackPtr));
p->data=e;
p->next=S->top;
S->top=p;
S->count++; //这里容易忘,记得把链长度计数+1
return OK;
}

//链栈的出栈操作,需要释放一个结点
Status Pop(LinkStack *S,SElemType *e){
if(S->count==0)
return ERROR;
*e=S->top->data;
StackNode *p=S->top;  //当前栈顶结点
StackNode *n=S->top->next;//栈顶的下一个结点
S->top=n; //栈顶指向下一个
free(p); //释放被删原栈顶
S->count--;  //链长-1
return OK;
}

4.栈的实践作用

1.实现了递归。

①递归和迭代的区别:迭代使用的是循环结构,递归使用的是选择结构,递归会建立大量函数副本,消耗时间和内存,迭代不需要额外空间。

②递归的栈利用: 递归过程退回的顺序是它前行的顺序,退回过程中也可以执行某些动作,包括恢复在前行过程中存储起来的某些数据。

这种先存后以存的逆序恢复数据的操作符合栈的数据结构。前行时,对于每一层的递归,局部变量,参数值及饭后地址都被压入栈中。退回时这些值按逆序返回调用层执行。

2.逆波兰表示法

①后缀表达式:不需要括号,所有的符号都是在运算数字后面出现。

②逆波兰原理:遍历后缀表达式,遇到数字就进栈,遇到符号则将栈顶两个元素取出进行运算,把结果进栈,继续遍历。

③中缀表达式转后缀表达式:对符号的栈操作。从左到右遍历中缀表达式,遇到数字直接输出作为结果的一部分,遇到符号,判断该符号优先级与栈顶符号优先级的高低,

优先级低于栈顶元素或者当前符号是右括号,则此时栈顶元素一次全部出栈输出作为结果一部分,当前符号进栈,继续遍历。

④总结:对于后缀表达式的具体机器运算,栈用来对运算的数字实现进出。 对于中缀式转后缀式,栈用来对运算的符号实现进出。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: