栈和队列
这个作业属于哪个班级 | 数据结构--网络2011/2012 |
---|---|
这个作业的地址 | DS博客作业02--栈和队列 |
这个作业的目标 | 学习栈和队列的结构设计及运算操作 |
姓名 | 林进源 |
0.PTA得分截图
1.本周学习总结(0-5分)
1.1 栈
栈的定义以及特点
- 栈是一种只能在一端进行插入和删除操作的线性表,表中允许进行插入和删除的一端为栈顶,另一端为栈底。
- 当栈中没有数据元素时称为空栈。
- 栈的插入操作称为入栈或者进栈,栈的删除操作称为出栈或者退栈。
- 栈的主要特点为后进先出,每次出栈的元素都为当前栈的栈顶元素。
顺序栈的结构
- 栈的代码定义
typedef struct {
ElemType data[MaxSize];//存放元素
int top=-1;//栈顶指针,下标
}SqStack;//顺序栈的类型
栈的存储和删除通过top的移动进行元素的进入和删除,对栈的元素进行删除实际上元素仍然在栈里面,仅是移动指针的位置。
顺序栈操作函数
- 栈空栈满的判断条件(进行插入删除时需要注意的条件)
若初始时s->top定义为-1,则栈空的条件为s->top=-1,栈满的条件为s->top=MaxSize-1(MaxSize为数组最大下标)。 - 初始化栈
void InitStack(S)
{
s=new Stack;//动态申请内存
s->top=-1;
}
- 进栈
bool Push(SqStack &S,ElemType e)
{
if(s->top==MaxSize-1)//顺序栈进栈时要注意栈是否已满
{
return false;
}
s->top++;//栈顶指针加1
s->data[s->top]=e;
return true;
}
进行是否栈满的判断,栈顶指针加一,放入元素
- 出栈
bool Pop(SqStack &s,ElemType &e)
{
if(s->top==-1)//顺序栈出栈时要注意栈是否为空
{
return flase
}
e=s->data[s->top];
s->top--;//栈顶指针减一
return ture;
}
进行是否栈空的判断,栈顶指针减一,移除元素
- 取栈顶元素
bool GetTop(SqStack *s,ElemType &e)
{
if(s->top==-1)//判断是否为空
return false;
e=s->data[s->top];
return true;
}
进行是否栈空的判断,取栈顶指针位置,输出元素
链栈的结构
- 栈的代码定义
typedef struct linknode
{
ElemType data;//数据域
struct linknode *next;//指针域
}LinkStNode;//类型
通过对指针的结点移动进行数据的插入和输出。
链栈的操作函数
- 栈空栈满的判断条件(区别于顺序栈)
栈空的判断条件为s->next=NULL,一般不用判断栈满,只有内存溢出时才栈满。 - 初始化栈
void InitStack(LiStack &s)
{
s=new LiNode;//动态申请内存
s->next=NULL;
}
- 进栈
void Push(LiStack &s,Elemtype e)
{
LiStack p;
p=new LiNode;//新建结点进行插入
p->data=e;//头插法
p->next=s->next;
s->next=p;
}
进栈一般不用判断栈满,利用头插法进行元素的进栈
- 出栈
bool Pop(LiStack &s,ElemType &e)
{
LiStack p;//新建结点临时保存
if(s->next=NULL)
{
return false;
}
p=s->next;
e=p->data;
s->enxt=p->next;//改变结点指向
delete p;//删除
return true;
}
出栈时需要对栈空进行判断,后利用链表删除某一结点的数据方法,新建临时结点保存需要删除的结点,后改变前继结点的指向,物理删除需要删除的结点。
c++模板类:stack
- stack <元素类型> s:初始化栈
- s.push(t):将元素t入栈
- s.pop():出栈顶元素
- s.top():返回栈顶元素
- s.empty():当栈为空时返回true
- s.size():访问栈的元素个数
1.2 栈的应用
表达式
- 中缀表达式:运算符号位于两个运算数之间。后缀表达式:运算符号位于两个运算数之后。
- 中缀表达式转为后缀表达式:
建立一个栈用于存放运算符,每个运算符号进栈时都要比较一下其和栈顶的的符号优先级大小,若优先级较低,则栈里面的运算符需要出来,其进栈,当操作结束后,需要观察栈里面是否还有运算符,若有则需要将其出栈,若遇到括号,由于括号的优先级最高,当符号配对成功时,需要出栈的元素。
例:
(1)将中缀表达式a+b * c+(d * e+f) * g转为后缀表达式,当读入f时栈里面的内容为+(+
解析:+进栈, * 进栈,+优先级较低,故出* 和 +,+进栈,(进栈, * 进栈,+优先级较低,故*出栈,+进栈,此时栈里面剩余+(+。
(2)将表达式a * (b+c)-d转为后缀表达式abc+ * d-。
解析: * 进栈,(进栈,+进栈,)进栈,配对成功,+出栈, * 出栈,-进栈,完成操作后,-还在栈里,-出栈。
1.3 队列
队列的定义以及特点
- 队列是一种操作受限的线性表,仅允许在表的一端进行插入操作,在表的另一端进行删除操作。
- 把进行删除的一端称为队头或队首,进行插入的一端称为队尾。
- 插入新元素,新元素进队后为新的队尾元素,元素出队后,其后继的元素称为队头元素。
- 队的特点为先进先出
顺序队列的结构
- 队列的代码定义
typedef struct
{
ElemType data[MaxSize];
int front,rear;//队首,队尾指针
}Queue;
通过队首队尾指针的移动进行元素的插入和删除,front指向队头元素,rear指向队尾元素。
顺序队列的操作函数
- 队列空满的判断条件(进行插入删除时需要注意的条件)
初始定义front和rear为-1,对空的条件为front=rear,队满的条件为rear=MaxSize。 - 初始化队列
void InitQueue(Queue &q)
{
q=new Queue;//动态申请内存
q->front=q->rear=-1;
}
- 进队列
bool enQueue(SqQueue &q,ElemType e)
{
if(q->rear+1==MaxSize)//判断是否栈满
return flase;
q->rear=q->rear+1;//移动指针
q->data[q->rear]=e;
return ture;
}
跟顺序栈的进栈一样,顺序队列的进队需要判断rear的下一位是否已满,若未满即可移动指针插入元素。
- 出队列
bool deQueue(SqQueue &q,Elemtype &e)
{
if(q->front==q->rear)//判断队是否为空
return flase;
e=q->data[q->front];
q->front=q->front+1;//移动指针
return ture;
}
顺序队列得到进队需要判断队列是否为空,后移动front到下一位,进行出队操作。
环形队列的结构
由于顺序队列的队满条件为rear=MaxSize-1,当满足这一条件时,即跳出操作,但此时可能仍有若干空位置,这种情况为假溢出。
为更好利用存储空间,可构建一个环形队列。
- 队列的代码定义
typedef struct
{
ElemType data[MaxSize];
int front,rear;//队首,队尾指针
}Queue;
环形队列的操作函数
- 队头指针front循环增1:front=(front+1)%MaxSize,队尾指针rear循环增1:rear=(rear+1)%MaxSize
- 队满的判定条件:(q->rear+1)%MaxSize=q->front,队空的判定条件:q->front=q->rear。
- 初始化队列
void InitQueue(SqQueue &q)
{
q=new Queue;//动态申请内存
q->front=q->rear=0;
}
通过分别移动front和rear进行进队列和出队列操作。
- 进环形队列
bool enQueue(SqQueue &q,Elemtype e)
{
if((q->rear+1)%MaxSize==q->front)//判断是否队满
return false;
q->rear=(q->rear+1)%MaxSize;//移动rear
q->data[q->rear]=e;
return true;
}
进队列需要判断是否队满,判断(q->rear+1)%MaxSize是否等于q->front,后移动rear指针,q->rear=(q->rear+1)%MaxSize。
- 出环形队列
bool deQueue(SqQueue &q,Elemtype e)
{
if(q->front==q->rear)//判断是否队空
return false
e=q->data[q->front];
q->front=(q->front+1)%MaxSize;//移动front
return true;
}
出队需要判断是否对空,判断q->front==q->rea,后移动front指针q->front=(q->front+1)%MaxSize。
链队列的结构
- 队列的代码定义
typedef struct
{
QNode *front;
QNode *rear;
}LinkQueue;
通过移动front和rear实现进栈和出栈
链队列的操作函数
- 队空的判断条件为front=rear=NULL,一般不用考虑队满
- 初始化链队列
void InitQueue(LinkQuNode &q)
{
q=new LinkQuNode;
q->front=q->rear=NULL;
}
- 进队列
bool enQueue(LinkQuNode &q,ElemType e)
{
p=new QNode;
p->data=e;//新建结点
p->next=NULL;//避免后面无结束
q->rear->next=p;
q->rear=p;//尾指针移动
}
进队列不需要判断队满,利用尾插法插入,并且改变rear的指向。
- 出队列
bool deQueue(LinkQuNode &q,ElemType e)
{
Node t;
if(q->rear==NULL)
return false;
t=q->front;
if(q->front==q->rear)//此时只有一个数据
{
q->front=q->rear=NULL;
}
else
{
q->front=q->front->next;//移动front
}
e=t->data;
delete t;//删除
}
出队列判断是否为空,并考虑是否只有一个数据,移动front。
队列应用
例题:
#include <iostream>
#include <queue>
using namespace std;
int main()
{
int m;//人数
int n;//数字
queue <int>person;
cin >> m >> n;
if (n > m)//特殊情况
{
printf("error!");
}
else
{
int i ;
for (i = 1; i <= m; i++)//进入队列
{
person.push(i);
}
int flag = 1;//控制空格
int num;
i = 0;
while (!person.empty())
{
i++;
if (i%n == 0)
{
num = person.front();
if (flag == 1)
{
flag = 0;
cout << num;
}
else
{
cout << ' ' << num;
}
person.pop();//出队列
i = 0;//重新计数
}
else
{
num = person.front();
person.push(num);//进队列
person.pop();//出队列
}
}
}
return 0;
}
对于此类的报数问题还有搜索迷宫路径问题,可用队列解决,题中不满足报数条件的单位需要进行出队再入队的操作,具体的要求根据题意改变。
c++模板类:queue
- q.push():入队
- q.pop():出队
- q.front():取队头元素
- q.back():取队尾元素
- q.empty():队是否为空
2.PTA实验作业(4分)
7-2 符号配对
解题思路及伪代码
解题思路:定义flag进行判断操作,输入左符号时入栈,输入右符号时并且判断栈里面是否有元素,若无则为一种情况,若有则进行配对,判断是否是该右符号对应的左符号,用flag标记对应的情况。
伪代码:
int flag=0;
for (遍历条件)
{
if (为左符号)
{
入栈;
}
else if (栈为空且有右符号)
{
flag = 1;
break;
}
else if (各自左符号与右符号配对情况)
{
出栈;
}
}
if (flag == 1)
{
右符号剩余;
}
else if (栈为空)
{
匹配;
}
else
{
正常情况的不匹配;
}
总结解题所用的知识点
stack相关函数的应用、flag标记情况、情况不同的判断考虑,本题需要考虑到左符号剩余,右符号剩余,配对情况,不配对情况,需要多重if 来进行约束判断,最后根据flag的值输出对应的结果。
7-6银行业务队列简单模拟
解题思路及伪代码
解题思路:
先判断编号的奇偶,若为奇数进A队列,若为偶数进B队列,再分别计算A队列和B队列的元素个数,后判断A无元素和只有1个元素的特殊情况,两种情况下再判断B的个数,之后普通情况,另A的元素个数除以2,因为2人为一组,1组输出2个编号后B输出1个编号,但要考虑到A的元素个数为奇数个,故要添加判断,若其为奇数最后一组输出1个。
伪代码:
for (遍历条件)
{
if (编号为奇数)
{
进A;
}
else
{
进B;
}
}
统计A和B的个数
if (A个数为0)
{
全部输出B元素;
}
else if (A元素个数为1)
{
输出A;
全部输出B;
}
else
{
int i = A个数 / 2;
for (n = 1; n <= i; n++)
{
if (n < i)
{
输出A2个元素;
}
else if (n == i && A个数为奇数)
{
输出A1个元元素;
}
else if (n == i && A个数为偶数)
{
输出A2个元素;
}
if (B还有元素)
{
输出B1个元素;
}
}
}
总结解题所用的知识点
queue相关函数的使用、队列优先输出的顺序、本题需要考虑A对列的特殊情况,考虑其个数以及奇偶性。
3.阅读代码(0--1分)
3.1 题目及解题代码
代码:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped)
{
stack<int> story;
int j=0;//popped的index
for(int i=0;i<pushed.size();++i)
{
story.push(pushed[i]);
while(!story.empty()&&story.top()==popped[j])//出与入相同时执行pop
{
story.pop();
j++;
}
}
while(!story.empty()&&j<popped.size())
{
if(story.top()==popped[j])//为pop首元素时执行pop
{
story.pop();
j++;
}
else
{
return false;
}
}
return true;
}
3.2 该题的设计思路及伪代码
伪代码:
for(遍历条件)
{
push入新栈;
while(栈顶与pop的top一样时)
{
执行pop;
}
}
while(新栈不为空)
{
if(栈顶与pop的top一样)
{
执行pop;
}
else
{
错误;
}
}
相当于对新栈要遍历一遍故时间复杂度为O(n),空间复杂度为O(n)
3.3 分析该题目解题优势及难点
优势:执行思路清晰,明确找到需要判断的点,构建新栈与pop进行一 一比较。
难点:需要想到构建新栈导入push,利用栈的特性,一 一与pop进行逆序匹对。