数据结构与算法复习总结(二)
写在前面
今天复习栈和队列,今天学习状态不太好,我很纠结很难过,更可气的是我一觉起床居然感冒了,鼻塞的不行!(憔悴.jpg)但我强迫自己坚持一下,把自己的计划完成好(化难过为力量.jpg)
一、栈的定义和特点
- 限定仅在表尾进行插入或删除操作的线性表
- 表尾端称为栈顶,表头端称为栈底
- 后进先出(Last In First Out)
二、栈的表示与操作实现
(一)顺序栈的表示与实现
- 顺序栈的存储结构
//顺序栈的存储结构
typedef int SElemType;
typedef struct{
SElemType *base; //栈底指针
SElemType *top; //栈顶指针
int stacksize;
}SqStack;
栈空时,top和base都指向栈底,栈非空时,top始终指向栈顶元素的上一个位置
- 初始化
//初始化
void InitStack(SqStack &S){
S.base = new SElemType[MAXSIZE];
if(!S.base)
return;
S.base = S.top;
S.stacksize = MAXSIZE;
}
- 入栈
//入栈
void Push(SqStack &S, SElemType e){
if(S.top-S.base == S.stacksize)
return;
*S.top++ = e; //先把元素e压入栈顶,栈顶指针再加一
}
- 出栈
//出栈
void Pop(SqStack &S, SElemType e){
if(S.top == S.base)
return;
e = *--S.top;
}
- 取栈顶元素
int GetTop(SqStack S){
if(S.top != S.base){
return (S.top-1);
}
}
(二)链栈的表示与实现
- 链栈的存储结构
//链栈的存储结构
typedef int SElemType;
typedef struct StackNode{
SElemType data;
Struct StackNode *next;
}StackNode,*LinkStack;
- 初始化
//初始化
//构造一个空栈,直接将栈顶指针置空
void InitStack(LinkStack &S){
S = NULL;
}
- 入栈
//入栈
void Push(LinkStack &S, SElemType e){
p = new StackNode; //生成新的节点
p->data = e;
p->next = S;
S = p;
}
- 出栈
//出栈
void Pop(LinkStack &S, SElemType &e){
if(S == NULL)
return;
e = S->data; //把栈顶元素赋给e
p = S; //用p临时保存栈顶元素空间,等待释放
S = S->next;
free(p);
}
- 取栈顶元素
//取栈顶元素
int GetTop(LinkStack S){
if(S!=NULL)
return S.data;
}
三、队列的定义和特点
- 先进先出的线性表
- 允许插入的一端是队尾,允许删除的一端是队头
四、队列的表示与操作实现
(一)循环队列–队列的顺序表示与实现
由于“队尾入队,队头出队”这种受限制的操作,在插入新的队尾元素时,可能会出现溢出现象,但此时队列的实际可用空间并未占满,所以这种现象称为“假溢出”,所以可以将顺序队列变为一个环状的空间,称之为循环队列。
对于循环队列还有一些需要探讨的地方,看下面的图
(a)对头元素为J5,在J6入队之前,Q.rear = 5,J6入队后,Q.rear = (Q.rear+1)%6 = 0,所以不会产生“假溢出”现象;
(b)此时队列空间都被占满,头尾指针相同;
(c)当(a)中J5, J6相继出队,此时队列是空的,头尾指针的值也相同;
所以,循环队列不能以头、尾指针的值是否相同来判别队列空间是“满”还是“空”。
故我们通常少用一个元素空间,就是说当队列空间大小为m的时候,有m-1个元素就认为队满。
队空的条件:Q.front == Q.rear
队满的条件:(Q.rear + 1) % MAXQSIZE == Q.front
- 队列的存储结构
typedef int QElemType;
typedef struct{
QElemType *base; //存储空间的基地址
int front; //头指针
int rear; //尾指针
}SqQueue;
- 初始化
//初始化
void InitQueue(SqQueue &Q){
Q.base = new QElemType[MAXQSIZE];
if(!Q.base)
return;
Q.front = Q.rear = 0;
}
- 求队列长度
//求循环队列的长度
int QueueLength(SqQueue &Q){
return (Q.rear-Q.front+MAXQSIZE) % MAXQSIZE;
}
- 循环队列的入队
//循环队列的入队
void EnQueue(SqQueue &Q, QElemType e){
if((Q.rear+1)%MAXQSIZE==Q.front) //判断是否队满
return;
Q.base[Q.rear]=e;
Q.rear=(Q.rear+1)%MAXQSIZE;
}
- 循环队列的出队
//循环队列的出队
void DeQueue(SqQueue &Q, QElemType &e){
if(Q.rear == Q.front)
return;
e = Q.front; //把出队元素保存在e中
Q.front = (Q.front+1) % MAXQSIZE;
}
- 取循环队列的队头元素
//取队头元素
int GetHead(SqQueue Q){
if(Q.front != Q.rear)
return Q.base[Q.front]; //返回队头元素,但头指针不变
}
(二)链队–队列的链式表示与实现
- 队列的链式存储结构
typedef struct QNode{
QElemType data;
struct QNode *next;
}QNode, *QueuePtr;
typedef struct{
QueuePtr front; //队头指针
QueuePtr rear; //队尾指针
}LinkQueue;
- 初始化
//初始化
void(LinkQueue &Q){
Q.front = Q.rear = new QNode; //生成新结点作为头结点,头指针始终指向头结点
Q.front -> next = NULL;
}
- 入队
//入队
void EnQueue(LinkQueue &Q,QElemType e){
p = new QNode; //为入队元素分配节点空间
p->data = e;
p->next = NULL;
Q.rear->next = p; //将新节点插入队尾
Q.rear = p;
}
- 出队
//出队
void DeQueue(LinkQueue &Q, QElemType &e){
if(Q.front == Q.rear)
return;
p = Q.front->next;
e = p->data;
Q.front->next = p->next; //队头元素出队
if(Q.rear = p) //如果最后一个元素被删,队尾指针指向头结点
Q.rear = Q.front;
free(p);
}
- 取队头元素
//取队头元素
int GetHead(LinkQueue Q){
if(Q.front != Q.rear)
return Q.front->next->data;
}
好的吧,差不多都总结完了,基本操作都实现之后,要写出完整的程序就很简单了呢~QAQ