声明:本博客仅为本人学习途中做的笔记 采自青岛大学王卓老师的视频教学 主要内容为算法思路,具体代码实现还需修改后才能运行,望各位看官多多包涵,您的点赞与评论是对我最大的肯定!
1.栈和队列的定义和特点
栈和队列是两种常用的、重要的数据结构
栈和队列是限定插入和删除只能在表的"端点"进行的线性表
栈和队列是线性表的子集(是插入和删除位置受限的线性表)
由于栈的操作具有后进先出的固有特性,使得栈成为程序设计中的有用工具。另外,如果问题求解的过程具有"后进先出"的天然特性的话,则求解的算法中也必然需要利用"栈".
- 数制转换
- 括号匹配的检验
- 行编辑程序
- 迷宫求解
- 表达式求值
- 八皇后问题
- 函数调用
- 递归调用的实现
线性表 | 栈 | 队列(先进先出) |
---|---|---|
Insert(L,i,x) 1<=i<=n+1 | Insert(S,n+1,x) | Insert(Q,n+1,x) |
Delete(L,i)1<=i<=n | Delete(S,n) | Delete(Q,1) |
栈是一个特殊的线性表,是限定仅在一端(通常是表尾)进行插入和删除操作的线性表。又称为后进先出的线性表
表尾a n _n n端称为栈顶,表头a 1 _1 1端称为栈底Base
插入元素到栈顶(即表尾)的操作,称为入栈。
从栈顶(即表尾)删除最后一个元素的操作,称为出栈。
队列是一种先进先出的线性表,在表一端插入,在另一端删除
2.栈的表示和操作的实现
由于栈本身就是线性表,于是栈也有顺序存储和链式存储两种实现方式
栈的顺序存储–顺序栈
栈的链式存储–链栈
顺序栈的表示和实现
存储方式:同一般线性表的顺序存储结构完全相同,利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。栈底一般在低地址端
附设top指针,指示栈顶元素在顺序栈中的位置
另设base指针,指示栈底元素在顺序栈中的位置
但是,为了方便操作,通常top指针指示真正的栈顶元素之上的下标地址
另外,用Stacksize表示栈可使用的最大容量
空栈:base == top是栈空标志
栈满:top-base == stacksize
栈满时的处理方法:
1.报错,返回操作系统
2.分配更大的空间,作为栈的存储空间,将原栈的内容移入新栈
使用数组作为顺序栈存储方式的特点:
简单、方便、但易产生溢出(数组长度固定)
上溢(overflow):栈已经满,又要压入元素
下溢(underflow):栈已经空,还要弹出元素
#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;
}
求顺序栈长度
int StackLength(SqStack S){
return S.top-S.base;
}
清空顺序栈
Status ClearStack(SqStack S){
if(S.base) S.top = S.base;
return OK;
}
销毁顺序栈
Status DestoryStack(SqStack &S){
if(S.base){
delete S.base;
S.stacksize=0;
S.base=S.top=NULL;
}
return OK;
}
顺序栈的入栈
1.判断是否栈满,若满则出错(上溢)
2.元素e压入栈顶
3.栈顶指针+1
Status Push(SqStack &S, SElemType e){
if(S.top-S.base==S.stacksize)//栈满
return ERROR;
*S.top=e;
S.top++;
//*S.top++=e;
return OK;
}
顺序栈的出栈
1.判断元素是否栈空,若空则出错(下溢)
2.获取栈顶元素e
3.栈顶指针减1
Status Pop(SqStack &S, SElemType &e){
//若栈不空,则删除栈顶元素,用e返回其值,并返回OK,否则返回ERROR
if(S.top==S.base)//等价于if(StackEmpty(S))
return ERROR;
--S.top;
e=*S.top;
//e=*--top;
return OK;
}
链栈的表示和实现
链栈是运算受限的单链表,只能在链表头部进行操作
typedef struct StackNode{
SElemType data;
struct StackNode *next;
}StackNode, *LinkStack;
LinkStack S;
链表的头指针就是栈顶
不需要头结点
基本不存在栈满的情况
空栈相当于头指针指向空
插入和删除只在栈顶处进行
链栈的表示和实现
链栈是运算受限的单链表,只能在链表头部进行操作
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;
}
链栈的入栈
Stack 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(LinkList S){
if(S!=NULL)
return S->data;
}
4.队列的表示和操作
队列是仅在表尾进行插入操作,在表头进行删除操作的线性表
表尾即an端,称为队尾,表头为a1端,称为队头
它是一种先进先出的线性表
队列的相关概念:
1.定义 只能在表的一端进行插入运算,在表的另一端进行删除运算的线性表(头插尾删)
2.逻辑结构 与同线性表相同,仍为一对一关系
3.存储结构 顺序对或链队,以循环顺序队列更常见
4.运算规则 只能在队首和队尾运算且访问结点时依照先进先出的原则
队列的顺序表示和实现
顺序队列
用一维数组
#define MAXQSIZE 100//最大队列长度
Typedef struct{
QElemType *base//初始化的动态分配存储空间
int front;//头指针
int rear;//尾指针
}SqQueue
解决假上溢的方法:引入循环队列
base[0]接在base[MAXQSIZE-1]之后,若rear+1==M,则令rear=0;
实现方法:利用模(%,c语言:mod )运算
插入元素:Q.base[Q.rear]=x;
Q.rear=(Q.rear+1)%MAXQSIZE;
删除元素:x=Q.base [s.front]
Q.front=(Q.front+1)%MAXSIZE
循环队列的操作–队列的初始化
#define MAXQSIZE 100 //最大队列长度
typedef struct{
QElemType *base//动态分配存储空间
int front; //头指针,若队列不空,指向队列头元素
int rear;//尾指针,若队列不空,指向队列尾元素的下一个元素
}SqQueue
Status InitQueue(SqQueue &Q){
Q.base=new QElemType*;//分配数组空间
//Q.base=(QElemType*)malloc(MAXQSIZE*sizeof(QElemType));
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;//队尾指针+1
return OK;
}
循环队列的操作–循环队列出队
Status DeQueue(SqQueue &Q,QElemType &e){
if(Q.front==Q.rear) return ERROR;//队空
e=Q.base[Q.front];//保存队头元素
Q.fromt=(Q.front+1)%MAXQSIZE;//队头指针+1
}
循环队列的操作–取队头元素
Status GetHead(SqQueue Q){
if(Q.feont!=Q.rear)//队列不为空
return Q.base[Q.front];//返回队头指针元素的值,队头指针不变
}
队列的链式表示和实现
链式队列
若用户无法估计所用队列的长度,则宜采用链队列
#define MAXQSIZE 100//最大队列长度
typedef struct Qnode{
QElemType data;
struct Qnode *next;
}QNode, *QuenPtr;
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 DestoryQueue(LinkQueue &Q){
while(Q.front){
p=Q.front->next;
free(Q.front);
Q.front=p;
} //Q.rear=Q.front->next;
//free(Q.front);
//Q.front=Q.rear
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 DeQueue(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;
delete p;
return OK;
}
链队列的操作–求链队列的队头元素
Status GetHead(LinkQueue Q,QElemType &e){
if(Q.front==Q.rear) return ERROR;
e=Q.front->next->data;
return OK;
}