栈
栈的定义
栈是一种重要的线性结构,栈是前面讲过的线性表的一种具体形式。
栈是一种后进先出的结构,例如子弹的压入和发出,例如c语言中的函数。
栈是一种后进先出(Last in first out,LIFO)的线性表,它要求只在表尾进行删除和插入操作。
- 栈的元素必须“后进先出”
- 栈的操作只能在这个线性表的表尾进行。
- 对于栈来说,这个表尾称为栈的栈顶(top),相应的表头称为栈底(bottom)
栈的插入和删除操作
- 栈的插入操作(Push),叫做进栈,也成为压栈、 入栈。类似于子弹压入弹夹的动作。
- 栈的删除操作(Pop),叫做出栈,也称为弹栈。如同弹夹中的子弹出夹。
栈的顺序存储结构
- 因为栈的本质是一个线性表,线性表有两种存储形式,那么栈也分为栈的顺序存储结构和栈的链式存储结构
- 最开始的栈不含有任何数据,叫做空栈,此时栈顶就是栈底。然后数据从栈顶进入,栈底分离,整个栈的当前容量变大。数据出栈时从栈顶弹出,栈顶下移,整个栈的当前容量变小。
- 栈的抽象数据类型定义
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
typedef struct{
ElemType *base; //栈底指针
ElemType *top; //栈顶指针
int stackSize; //栈可用最大容量
}sqStack;
-
这里定义了一个顺序存储的栈,它包含了三个元素:base,top,stacksize,其中base是指向栈底的指针,top是指向栈顶的指针,stacksize是指示当前栈当前可使用的最大容量。
-
创建一个栈
status initStack(sqStack &s){
s.base=(ElemType *)malloc(MAX_SIZE * sizeof(ElemType));
if(!s.base) return ERROR;
s.top=s.base;// 栈顶=栈底
s.stackSize=MAX_SIZE;
}
- 判断栈是否为空
Status StackEmpty(sqStack S){
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){
free(S.base);
S.stacksize=0;
S.base=S.top=NULL;
}
return OK;
}
- 入栈操作又叫压栈操作,就是向栈中存放数据。
Status Push(sqStack &s,ElemType e)
{
//如果栈满,返回错误
if(s.top-s.base>=s.stackSize)
{
return ERROR;
}
*s.top = e;
s.top++;
return OK;
}
- 顺序栈的出栈
Status Pop(sqStack &s,ElemType &e){
if(s.top==s.base) return ERROR;
--s.top;
e=*s.top;
return OK;
}
栈的链式存储结构
- 链栈是运算受限的单链表,只能在链表头部进行操作。
typedef struc StackNode{
ELemType data;
struct StackNode *next;
}StackNode,*LinkStack;
-
链栈的特点:
- 链表的头指针就是栈顶
- 不需要头结点
- 基本不存在栈满的情况
- 空栈相当于头指针指向空
- 插入和删除仅在栈顶处执行
-
链栈的初始化
Status InitStack(LinkStack &S){
//构造一个空栈,栈顶指针置为空
S=NULL;
return OK;
}
- 链栈的入栈
Status Push(LinkStack &S,ElemType e){
p = (StackNode*)malloc(sizeof(StackNode));//生成新结点P
p->data = e;//将新结点数据域置为e
p->next = S;//将新结点插入栈顶
S=p;//修改栈顶指针
return OK;
}
- 链栈的出栈
Status Pop(LinkStack &S,ElemType &e){
if(S==NULL)return ERROR;
e = S->data;
P=S;
S=S->next;
free(P);
return OK;
}
栈与递归
-
递归的定义:
- 若一个对象部分地包括它自己,或用它自己给他自己定义,则称这个对象是递归的;
- 若一个过程直接地或间接地调用自己,则称这个过程是递归的过程。
- 例如:递归求n的阶乘
long Fact(long n){ if(n==0) return 1; else return n*Fact(n-1); }
- 以下三种情况常常用到递归方法:
- 递归定义的数学函数(斐波那契数列)
- 具有递归特性的数据结构(树、广义表等)
- 可递归求解的问题(汉诺塔)
- 递归问题——用分治法求解:
- 对于一个复杂的问题,能够分解成几个相对简单的方法且解法相同或类似的子问题来求解。
- 必备的三个条件
-
能够将一个问题变成一个新问题,而新问题与原问题的解法相同或类同。
-
可以通过上述转化使问题简化
-
必须有一个明确的递归出口,或称递归的边界。
void p(参数表){ if(递归结束条件) 可直接求解步骤; //基本项 else p(较小的参数); //归纳项 }
-
-
函数调用过程:
- 调用前,系统完成:
- 将实参,返回地址等参数传递给被调用函数
- 为被调用函数的局部变量分配存储区
- 将控制转移到被调用函数的入口
- 调用后,系统完成:
- 保存被调用函数的计算结果
- 释放被调用函数的数据区
- 依照被调用函数保存的返回地址将控制转移到调用函数
- 调用前,系统完成:
队列
- 队列是一种先进先出的线性表,在表一端插入(尾部),在另一端(表头)删除,类似我们的排队。
- 队列的存储结构为链队或顺序队(常用顺序循环队)
队列的顺序存储结构
ADT Queue{
数据对象:D={ai|ai∈ElemSet,i=1,2....n(n≥0)}
数据关系:R={<ai-1,ai>|ai-1,ai∈D,i=2,...n}
基本操作:
InitQueue(&Q) 操作结果:构造空队列Q
DestroyQueue(&Q) 条件:队列Q已存在;操作结果:队列Q被销毁;
ClearQueue(&Q) 条件:队列Q已存在;操作结果:将Q清空;
QueueLength(&Q) 条件:队列Q已存在;操作结果:返回Q的元素个数,即队长
GetHead(Q,&e) 条件:Q为非空状态;操作结果:用e返回队列Q点队头元素;
EnQueue(&Q,e) 条件:队列Q已存在;操作结果:插入元素e为队列Q的队尾元素;
DeQueue(&Q,&e) 条件:Q为非空队列;操作结果:删除Q的队头元素,用e返回。
}ADT Queue
#define MAXQSIZE 100 //最大度列长度
typedef struct{
QElemType *base; //初始化的动态分配存储空间
int front; //头指针
int rear; //尾指针
}SqQueue;
- 队列的初始化
Status InitQueue(SqQueue &Q){
Q.base = (QElemType*)malloc(sizeof(QElemType)*MAXQSIZE);
if(!Q.base) exit(OVERFLOW); //存储分配失败
Q.front = Q.rear = 0; //头尾指针置为0 , 队列为空
return OK;
}
- 求队列的长度
int QueueLength(SqQueue Q){
return ((Q.rear-Q.front+MAXQSIZE)%MAXQSIZE);
}
- 循环队列的入队
Status EnQueue(SqQueue &Q,QElemType e){
if((Q.rear+1)%MAXQSIZE ==Q.front) return ERROR; //队满
Q.base[Q.rear]=e;
Q.rear=(Q.rear+1)%MAXQSIZE;
return OK;
}
- 循环队列的出队
Status DeQueue(SqQueue &Q,QElemType &e){
if(Q.front==Q.rear) return ERROR; //队空
e=Q.base[Q.front]; //保存队头元素
Q.front=(Q.front+1)%MAXSIZE; //将队头指针+1
return OK;
}
- 取队头元素
QElemType GetHead(SqQueue Q){
if(Q.front!=Q.rear) //队列不为空
return Q.base[Q.front]; //返回队头指针元素的值,队头指针不变
}
队列的链式存储结构
-
若用户无法估计所用队列长度,宜采用链队列
- 链队列的类型定义:
#define MAXQSIZE 100 //最大队列长度 typedef struct Qnode{ QElemType data; struct Qnode *next; }QNode,*QuenePtr;
typedef struct{ QuenePtr front; //队头指针 QuenePtr rear; //队尾指针 }LinkQueue;
-
链队列的初始化
Status InitQueue (LinkQueue &Q)
{
Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));
if(!Q.front) exit(OVERFLOW);
Q.front->next=NULL;
return OK;
}
- 链队列的销毁
Status DestroyQueue(LinkQueue &Q)
{
while(Q.front)
{
p=Q.front->next;
free(Q.front);
Q.front=p;
}
return OK;
}
- 将元素e入队
Status EnQueue(LinkQueue &Q,QElemType e){
p=(QueuePtr)malloc(sizeof(QNode));
if(!p) exit(OVERFLOW); //分配失败
p->data = e;
p->next=NULL;
Q.rear->next=p;
Q.rear = p;
return OK;
}
- 链队列出队
Status QUeue(LinkQueue &Q,QElemtype &e)
{
if(Q.front==Q.rear) return ERROR; //队空
p=Q.front->next;
e=p->data;
Q.front->next=p->next;
if(Q.rear==p)Q.rear=Q.front;
//如果头指针的下一个就是尾指针,那么也要修改尾指针
free(p);
return OK;
}
- 求队头元素
Status GetHead(LinkQueue Q,QElemType &e){
if(Q.front==Q.rear)return ERROR;
e=Q.front->next->data;
return OK;
}