title: 第三章
grammar_cjkRuby: true
[TOC]
第三章:栈和队列
1. 应用实例
- 堆栈经常被使用在编译软件和程序设计软件当中
- 队列则经常被使用在操作系统和实务管理当中
栈的一些常用应用:
- 括号匹配问题
- 表达式求值
- 数值转换问题
- 行编辑程序
- 栈与递归,“后调用,先执行”
队列中的典型例子就是操作系统中的作业排队。
- 应用实例一:迷宫求解问题
- 应用实例二:马踏棋盘问题
- 应用实例三:伴舞问题
2. 栈
2.1 栈的概念及运算
栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的#一端进行插入和删除运算。这一端被称为栈顶(top),相对地,把另一端称为栈底(bottom)。向一个栈插入新元素又称作进栈(push)、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈(pop),它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
1. InitStack(S)初始化:初始化一个新栈。
2. Empty(S)栈的非空判断:若栈S不为空则返回true,否则,返回false
3. Push(S,x)入栈:在栈S的顶部插入元素x,若栈满,则返回false,否则返回true。
4. Pop(S)出栈:若栈S不为空,则返回栈顶元素,并从栈顶中删除该元素;否则,返回空元素null。
5. SetEmpty(S)置栈空操作:置栈S为空栈。
6. GetTop(S)取栈顶元素:若栈S不空,则返回栈顶元素,否则返回空元素null。
2.2 栈的顺序储存结构
2.2.1栈的顺序储存结构
利用顺序储存方式实现的栈称为顺序栈,类似于顺序表的定义,栈中的数据元素用一个预设的足够长度的一维数组来实现:datatype data[MAXSIZE],栈低的位置可以设为数组的任一个端点,而栈顶是随着插入和删除而随时改变的,顺序栈的类型描述如下:
#define MAXSIZE<栈最大元素数>
typedef struct
{
datatype data[MAXSIZE];
int top; //作为栈顶指针
}SeqStack;
定义一个指向顺序栈的指针:
SeqStack *s;
- 通常0下标端设为栈底,这样空栈时栈顶指针top=-1;入栈时,栈顶指针加1,即s->top++;出栈时,栈顶指针加1,即s->top--
顺序栈的基本操作如下
- 置空栈:首先建立栈空间,然后初始化栈顶指针。
SeqStack *Init_SeqStack()
{
SeqStack *s;
s=malloc(sizeof(SeqStack));
s->top=-1;
return s;
}
2.判空栈:
int Empty_SeqStack(SeqStack *s)
{
if(s->top=-1) return 1;
else return 0;
}
3.入栈
int Push_SeqStack (SeqStack *s ,datatype x)
{
if(s->top==MAXSIZE-1) return 0;
else
{
s->top++;
s->data[s->top]=x;
return 1;
}
}
4.出栈
int Pop_SeqStack(SeqStack *s ,datatype *x)
{
if(Empty_SeqStack(s)) return 0;
else
{
*x=s->data[s->top];
s->top--;
return 1;
}//栈顶元素存入*x,返回*
}
5.取栈顶元素
datatype Top_SeqStack(SeqStack *s)
{
if (Empty_SeqStack(s)) return 0;//栈空
else
return (s->data[s->top])'
}
- 注意 1. 对于顺序栈,入栈的时候首先判断栈是否满了,栈满的条件是s->top=MAXSIZE-1.栈满时不能入栈,负责会发生错误,称为上溢。
2.2.2多栈共享临近空间
- 共享栈 让多个栈公用一个足够大的连续储存空间,则可利用栈的动态特性是他们的储存空间互补,这就是栈的共享临近空间。
- 两栈共享的的数据结构可定义为;
typedef struct
{
Elemtype stack[MAXNUM];
int lefttop;//左栈栈顶位置指示器
int righttop;;
}dupsqstack;
- 两个栈共享空间的方法,左栈入栈时,栈顶指针加一,右栈入栈时,栈顶指针减一。由于两个栈顶均可向中间伸展,互补余缺,因此使得每个栈的最大空间均大于m/2;
- 为了识别左右栈,我们领外设定标识
char status;
status='L';//左栈
status='R';//右栈
在进行栈操作时,需指定栈号:status='L'为左栈,status='R'为右栈:判断栈满的条件是:s->lefttop+1==s->righttop;
- **共享栈的基本操作如下:
1. 初始化
void InitStack(DqStack *S)
{
S->top[0]=-1;
S->top[1]=M;
}
2. 进栈
int Push(DqStack *S, StackElementType x, int i)
{
if(S->top[0]+1==S->top[1]) return(FALSE);
switch(i)
{
case 0:S->top[0]++;
S->Stack[S->top[0]]=x;
break;
case 1: S->top[1]--;
S->Stack[S->top[1]]=x;
break;
default: return(FALSE);
}
return(TRUE);
}
3.出栈
int Pop(DqStack *S, StackElementType *x, int i)
{
switch(i)
{
case 0: if(S->top[0]==-1) return(FALSE);
*x=S->Stack[S->top[0]];
S->top[0]--;
break;
case 1: if(S->top[1]==M) return(FALSE);
*x=S->Stack[S->top[1]];
S->top[1]++;
break;
default: return(FALSE);
}
return(TRUE);
}
### 2.3 栈的链式储存结构
#### 2.3.1链式栈:
- 链式栈是一种数据存储结构,可以通过单链表的方式来实现,使用链式栈的优点在于它能够克服用数组实现的顺序栈空间利用率不高的特点,但是需要为每个栈元素分配额外的指针空间用来存放指针域。
- 链式栈中的元素以Node的形式存储,节点Node中存有此节点存于栈中的元素以及指向下个节点的指针。链式栈的数据成员只用保存指向栈顶节点的指针 *top_node。
- 链式栈相对于顺序栈的优点
顺序栈的实现在于使用了数组这个基本数据结构,数组中的元素在内存中的存储位置是连续的,且编译器要求我们在编译期就要确定数组的大小,这样对内存的使用效率并不高,一来无法避免因数组空间用光而引起的溢出问题,二在系统将内存分配给数组后,则这些内存对于其他任务就不可用;而对于链栈而言,使用了链表来实现栈,链表中的元素存储在不连续的地址,由于是动态申请内存,所以我们可以以非常小的内存空间开始,另外当某个项不使用时也可将内存返还给系统
1、链式栈的类型定义
typedef struct node
{
datatype data; /数据域/
struct node * next; /指针域/
}LinkStack;
2、判断空栈
int StackEmpty(LinkStack *top)
{
return (top?0:1);
}
//返回0,则不为空。
3、取栈顶元素
datatype GetTop(LinkStack *top)
{
if(!top)
{
printf("/n链表是空的!");
return 0;
}
return top->data;
}
4、入栈
//入栈
LinkStack *Push(LinkStack *top,datatype x)
{
LinkStack *p;
p=(LinkStack *)malloc(sizeof(LinkStack));//分配空间
p->data=x; /*设置新结点的值*/
p->next=top; /*将新元素插入栈中*/
top=p; /*将新元素设为栈顶*/
return top;
}
5、出栈
//出栈
LinkStack *Pop(LinkStack *top)
{
LinkStack *p;
if(!top)
{
printf("/n链栈是空的!");
return NULL;
} //判断是否为空栈n
p=top; //指向被删除的栈顶
top=top->next; //修改栈顶指针
free(p);
return top;
}
6. 多个链栈的操作
多个链栈的操作,我们可以将多个链栈的栈顶指针放在一个一维数组中统一管理,设一维数组top[M]:
slStacktype *top[M];
其中,top[0],top[1],top[2],...top[i].top[M-1]指向M个不同的链栈。分别是M个链栈的栈顶指针,操作时只需确认链栈号i。然后以top[i],为栈顶指针进行链栈的操作,就可以实现各种操作。
- 入栈操作
int pushDupLs(slStacktype *top[M],int i,Elemtype x)
{
//将元素x压入链栈top[i]中
slStacktype *p;
if((p=(slStacktype *)malloc(sizeof(slStacktyoe)))==NULL)
return FALSE;
//申请一个节点
p->data=x;
p->next=top[i]->next;
top[i]->next=p;
return TRUE;
}
2.出栈操作
Elemtype popDupLs(slStacktype *top[M],int i)
{
slStacktype *p;
Elemtypr x;
if(top[i]->next==NULL)
return NULL;//空栈
p=top[i]->next;
top[i]->next=p->next;
x=p->data;
free(p);
return x;
}