博客作业02-栈和队列
| 这个作业属于哪个班级 | 数据结构--网络2011/2012 |
| ---- | ---- | ---- |
| 这个作业的地址 | DS博客作业02--栈和队列 |
| 这个作业的目标 | 学习栈和队列的结构设计及运算操作 |
| 姓名 | 吕以晴 |
0.PTA得分截图
栈和队列题目集总得分,请截图,截图中必须有自己名字。题目至少完成2/3(不包括选择题),否则本次作业最高分5分。
1.本周学习总结(0-5分)
🌙1.1 栈
画一个栈的图形,介绍如下内容。
1.1.1顺序栈的结构、操作函数
- 四要素
①栈空
top=-1
②栈满
top=MaxSize-1
③进栈
top++;
st->data[top]=e;
④出栈
e=st->data[top];
top--
- 初始化InitStack
void InitStack(S)
{
s = new Stack;
s->top = -1;
}
- 销毁栈DestroyStack(&s)
void DestroyStack(SqStack& s)
{
delete s;
}
- 判断栈是否为空StackEmpty
若为空,则返回true;否则返回false
bool StackEmpty(SqStack s)
{
return(s->top == -1);
}
- 进栈Push(&s,e)
进栈前要先判断栈是否已满,在不满的情况下,先将栈指针+1,再在该位置上插入元素
bool Push(Sqstack &s,ElemType e)
{
if(s->top==MaxSize-1)
return false;
s->top++; //栈顶指针增1
s->data[s->top]=e;
return true;
}
- 出栈Pop(&s,&e)
出栈前要先判断栈是否为空,在不为空的情况下,先将栈顶元素赋给e,再将栈指针-1
bool Pop(SqStack &s,ElemType &e)
{
if (s->top==-1) //栈为空的情况,栈下溢出
return false;
e=s->data[s->top];//取栈顶指针元素
s->top--; //栈顶指针减1
return true;
}
- 取栈顶元素GetTop(s)
需先判断栈是否为空
bool GetTop(SqStack *s,ElemType &e)
{
if (s->top==-1) //栈为空的情况
return false;
e=s->data [s->top];
return true;
}
1.1.2链栈的结构、操作函数
- 四要素
①栈空
s->next=NULL
②栈满
链栈不需考虑栈满
③进栈
line->data=a;
line->next=stack;
stack=line;
④出栈
liStack * p=stack;
stack=stack->next;
free(p);
- 初始化InitStack
void Initstack(LiStack &s)
{
s=new LiNode;
s->next=NULL;
}
- 销毁栈DestroyStack
void Destroystack(LiStack &s)
{
LiStack node;
while (s!=NULL)
{
node=s;
s=s->next;
delete node;
}
}
- 判断栈是否为空StackEmpty
bool StackEmpty(LiStack s)
{
return(s->next==NULL);
}
- 进栈Push(&s,e)
进栈前要先判断栈是否已满,在不满的情况下,先将栈指针+1,再在该位置上插入元素
void Push(LiStack& s, ElemType e)
{
LiStack p;
p = new LiNode;
p->data = e;
p->next = s->next;
s->next = p;
}
- 出栈Pop(&s,&e)
出栈前要判断栈是否为空
bool Pop(LiStack &s,ElemType &e)
{
LiStack p;
if(s->next==NULL) //栈空的情况
return false;
p=s->next;//p指向开始节点
e=p->data;
s->next=p->next; //删除*p节点
delete p;//释放*p节点(物理删除)
return true;
}
- 取栈顶元素GetTop(s)
需先判断栈是否为空
bool GetTop(Listack s,ElemType &e)
{
if(s->next==NULL) //栈空的情况
return false;
e=s->next->data;
return true;
}
1.1.3栈在c++中的自带库--栈库(不用自己编译)
== 头文件#include<stack>
==
- stack
s:初始化栈,参数表示元素类型 - s.push(t):入栈元素t
- s.top():返回栈顶元素
- s.pop(): 出栈操作只是删除栈顶元素(物理删除,数据在栈内不存在),并不返回该元素
- s1.empty():当栈空时,返回true
- s1.size():访问栈中的元素个数
1.1.4重点及易错点
- 顺序栈的数据删除,不是真正的删除,只是将其移出栈,要使真正删除数据,应使用链栈。
- 栈的入栈出栈都是对栈顶进行操作
🌙1.2 栈的应用
1.2.1中缀表达式转化为后缀表达式
-
从左到右扫描每一个字符。如果扫描到的字符是操作数(如a、b等),就直接输出这些操作数。
-
如果扫描到的字符是一个操作符,分三种情况:
(1)如果堆栈是空的,直接将操作符存储到堆栈中(push it)
(2)如果该操作符的优先级大于堆栈出口的操作符,就直接将操作符存储到堆栈中(push it)
(3)如果该操作符的优先级低于堆栈出口的操作符,就将堆栈出口的操作符导出(pop it), 直到该操作符的优先级大于堆栈顶端的操作符。将扫描到的操作符导入到堆栈中(push)。
-
如果遇到的操作符是左括号"(”,就直接将该操作符输出到堆栈当中。该操作符只有在遇到右括号“)”的时候移除。这是一个特殊符号该特殊处理。
-
如果扫描到的操作符是右括号“)”,将堆栈中的操作符导出(pop)到output中输出,直到遇见左括号“(”。将堆栈中的左括号移出堆栈(pop )。继续扫描下一个字符
-
如果输入的中缀表达式已经扫描完了,但是堆栈中仍然存在操作符的时候,我们应该讲堆栈中的操作符导出并输入到output 当中。
1.2.2符号配对
#include <bits/stdc++.h>
using namespace std;
int main()
{
string s;
stack <char> z;
int bk=1;
while(getline(cin,s)){
if(s=="."){
break;
}
for(int i=0;i<s.length();i++){
if(s[i]=='('||s[i]=='{'||s[i]=='['){
z.push(s[i]);
}
else if(s[i]=='/'&&s[i+1]=='*'){
z.push('<');
i++;
}
else if(s[i]==')'||s[i]=='}'||s[i]==']'){
if(z.empty()){
cout<<"NO"<<endl;
cout<<"?-"<<s[i]<<endl;
return 0;
}
char q=z.top();
z.pop();
if(q!='('&&s[i]==')'){
cout<<"NO"<<endl;
if(q=='<') cout<<"/*-?"<<endl;
else cout<<q<<"-?"<<endl;
return 0;
}
else if(q!='['&&s[i]==']'){
cout<<"NO"<<endl;
if(q=='<') cout<<"/*-?"<<endl;
else cout<<q<<"-?"<<endl;
return 0;
}
else if(q!='{'&&s[i]=='}'){
cout<<"NO"<<endl;
if(q=='<') cout<<"/*-?"<<endl;
else cout<<q<<"-?"<<endl;
return 0;
}
}
else if(s[i]=='*'&&s[i+1]=='/'){
if(z.empty()){
cout<<"NO"<<endl;
cout<<"?-"<<s[i]<<s[i+1]<<endl;
return 0;
}
i++;///************//
char q1=0;
q1=z.top();
z.pop();
if(q1!='<'){
cout<<"NO"<<endl;
cout<<q1<<"-?"<<endl;
return 0;
}
}
}
if(bk==0) break;
}
if(z.empty()) cout<<"YES"<<endl;
else {
char q1=z.top();
z.pop();
if(q1!='<'){
cout<<"NO"<<endl;
cout<<q1<<"-?"<<endl;
}
else{
cout<<"NO"<<endl;
cout<<"/*-?"<<endl;
}
}
}
🌙1.3 队列
画一个队列的图形,介绍如下内容。
1.3.1顺序队列的结构、操作函数
- 五要素
①队空
front=rear
②队满
rear=MaxSize-1
③元素进队
rear++;
data[rear]=e;
④元素出队
front++;
e=data[front];
⑤初始化状态
front=rear=-1
- 初始化InitQueue(q)
将front和rear指针设置为初始状态-1
void InitQueue(SqQueue &q)
{
q=new Queue;
q->front=q->rear=-1;
}
- 销毁队列DestroyQueue(q)
释放队列所占用的存储空间
void DestroyQueue(SqQueue &q)
{
delete q;
}
- 判断队列是否为空QueueEmpty(q)
若队列满足q->front==q->rear
,则队列为空,返回true;否则队列不为空,返回false。
bool QueueEmpty(SqQueue q)
{
return (q->front==q->rear);
}
- 进队列enQueue(q,e)
进队列前必须先判断队列是否已满,在不满的条件下,先将队尾指针rear循环+1,再将对应元素e添加至该位置
bool enQueue(SqQueue &q,ElemType e)
{
if(q->rear+1==MaxSize)
return false;//队满上溢出
q->rear=q->rear+1;
q->data[q->rear]=e;
return true;
}
- 出队列deQueue(q,e)
出队列前必须先判断队列是否为空,在队列不为空的条件下,将队首指针front循环+1,并将该位置的元素赋给e
bool deQueue(SqQueue& q, ElemType& e)
{
if (q->front == q->rear)//队空下溢出
return false;
q->front = q->front + 1;
e = q->data[q->front];
return true;
}
因为用rear==MaxSize-1
作为队满条件,容易出现假溢出现象,而环形队列可以避免该问题
1.3.2环形队列的结构、操作函数
- 五要素
①队空
front=rear
②队满
(rear+1)%MaxSize=front
③元素进队
rear=(rear+1)%MaxSize;
data[rear]=e;
④元素出队
front=(front+1)%MaxSize;
e=data[front];
⑤初始化状态
front=rear=0
- 初始化InitQueue(q)
将front和rear指针设置为初始状态0
void InitQueue(SqQueue& q)
{
q = new Queue;
q->front = q->rear = 0;
}
-
销毁队列DestroyQueue(q)及判断队列是否为空QueueEmpty(q)与顺序队列相同
-
进队列enQueue(q,e)
进队列前必须先判断队列是否已满
bool enQueue(SqQueue& q, ElemType e)
{
if ((q->rear + 1) % MaxSize == q->front)//队满上溢出
return false;
q->rear = (q->rear + 1) % MaxSize;
q->data[q->rear] = e;
return true;
}
- 出队列deQueue(q,e)
出队列前必须先判断队列是否为空
bool deQueue(SqQueue& q, ElemType& e)
{
if (q->front ==q->rear )//队空下溢出
return false;
e = q->data[q->front];
q->front = (q->front + 1) % MaxSize;
return true;
}
1.3.3链队列的结构、操作函数
- 结构体
链表中节点与头尾指针需分开定义,因为队头队尾不属于链内容
⭐错误定义
typedef struct qnode
{
ElemType data;
struct qnode* next;
}QNode,*LiQueue;
LiQueue front, rear;
⭐正确定义
typedef struct qnode
{
ElemType data;
struct qnode* next;
}QNode;
typedef struct
{
QNode* front;
QNode* rear;
}LinkQueue;
当要使用头尾指针时
LinkQueue q;
q.rear++;
q.front++;
q.front->next=e;
- 四要素
①队空
front=rear=NULL
②队满
链队列不需考虑队满
③进队
q.rear->next=node;
q.rear=node;
④出队
node=q.front->next;
q.front->next=node->next;
delete node;
- 初始化InitQueue(q)
最好使用带头结点链表,不用每次操作都判断链表是否为空
void QueueInit(Queue * Q)//初始化为空的链队列
{
Q->front = (QueueNode*)malloc(sizeof(QueueNode));
Q->back = Q->front;/*头指针和尾指针都指向头结点*/
Q->front->next = NULL;
}
- 判断队列是否为空QueueEmpty(q)
Status QueueEmpty(LinkQueue Q)
{
return(Q.front==Q.rear);
}
- 进队列enQueue(q,e)
Status EnQueue(LinkQueue& Q, QElemType e)
{
p = new QNode;
if (!p)exit(OVERFLOW);
p->data = e;
p->next = NULL;
Q.rear->next = p;
Q.rear = p;
return OK;
}
- 出队列deQueue(q,e)
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;
}
- 求链队列队头元素getHead(q,&e)
Status GetHead(LinkQueue Q, QElemType &e)
{
if (Q.front == Q.rear)
return ERROR;
e = Q.front->next->data;
return OK;
}
1.3.4队列在c++中的模板(不用自己编译)
头文件为#include <queue>
- q1.push(x) 将x放入队列的末端。
- q1.pop():弹出队列的第一个元素
⭐注意:并不会返回被弹出元素的值,数据物理删除 - q1.front():取队头元素。
- q1.back():取队尾元素。
- q1.empty():当队列空时,返回true。
- q1.size() :访问队列中的元素个数
1.3.5队列应用,要有具体代码操作
舞伴匹配问题
#include<iostream>
#define MAXQSIZE 100//队列可能达到的最大长度
#define OK 1
#define ERROR 0
#define OVERFLOW -2
using namespace std;
typedef struct {
char name[20]; //姓名
char sex; //性别,'F'表示女性,'M'表示男性
} Person;
//- - - - - 队列的顺序存储结构- - - - -
typedef struct {
Person data[MAXQSIZE];
int front; //头指针
int rear; //尾指针
} Queue;
typedef Queue *SqQueue;
SqQueue Mdancers, Fdancers; //分别存放男士和女士入队者队列
int InitQueue(SqQueue &Q);
void DestroyQueue(SqQueue &q);
int QueueLen(SqQueue Q);//队列长度
int EnQueue(SqQueue &Q, Person e);//加入队列
int QueueEmpty(SqQueue &Q);//队列是否为空
int DeQueue(SqQueue &Q, Person &e);//出队列
void DancePartner(Person dancer[], int num); //配对舞伴
int main(){
int i;
int n;
Person dancer[MAXQSIZE];
cin>>n;
for(i=0;i<n;i++) cin>> dancer[i].name >> dancer[i].sex;
InitQueue(Mdancers); //男士队列初始化
InitQueue(Fdancers); //女士队列初始化
cout << "The dancing partners are:" << endl;
DancePartner(dancer, n);
if (!QueueEmpty(Fdancers)) {
cout << "F:"<<QueueLen(Fdancers) ;
} else if (!QueueEmpty(Mdancers)) {
cout << "M:"<<QueueLen(Mdancers) ;
}
DestroyQueue(Fdancers);
DestroyQueue(Mdancers);
return 0;
}
int InitQueue(SqQueue &Q) {//构造一个空队列Q
Q = new Queue; //为队列分配一个最大容量为MAXSIZE的数组空间
if (!Q->data)
exit( OVERFLOW); //存储分配失败
Q->front = Q->rear = 0; //头指针和尾指针置为零,队列为空
return OK;
}
void DestroyQueue(SqQueue &q)
{
delete q;
}
int QueueLen(SqQueue Q){//队列长度
return (Q->rear -Q->front + MAXQSIZE ) % MAXQSIZE;
}
int EnQueue(SqQueue &Q, Person e){//入队
Q->rear = (Q->rear + 1) %MAXQSIZE;
Q->data[Q->rear] = e;
return 0;
}
int QueueEmpty(SqQueue &Q){//判空
if(Q->front==Q->rear){
return 1;
}else{
return 0;
}
}
int DeQueue(SqQueue &Q, Person &e){//出队
//出队那个存到e中
Q->front=(Q->front+1)%MAXQSIZE;
e=Q->data[Q->front];
return 0;
}
void DancePartner(Person dancer[], int num){
/*
*函数作用:
*1.将Person里存的人分到Mdancers, Fdancers两个队列
*2.Mdancers, Fdancers一比一配队出列
*/
//Mdancers, Fdancers
for(int i=0;i<num;i++){
if(dancer[i].sex=='M'){
EnQueue(Mdancers,dancer[i]);
}else{
EnQueue(Fdancers,dancer[i]);
}
}
while(QueueEmpty(Mdancers)!=1&&QueueEmpty(Fdancers)!=1){
Person x,y;
DeQueue(Mdancers, x);
DeQueue(Fdancers, y);
cout<<y.name<<" "<<x.name<<endl;
}
}
2.PTA实验作业(4分)
🌙2.1 符号配对
⭐2.1.1 解题代码
#include <bits/stdc++.h>
using namespace std;
int main()
{
string s;
stack <char> z;
int bk=1;
while(getline(cin,s)){
if(s=="."){
break;
}
for(int i=0;i<s.length();i++){
if(s[i]=='('||s[i]=='{'||s[i]=='['){
z.push(s[i]);
}
else if(s[i]=='/'&&s[i+1]=='*'){
z.push('<');
i++;
}
else if(s[i]==')'||s[i]=='}'||s[i]==']'){
if(z.empty()){
cout<<"NO"<<endl;
cout<<"?-"<<s[i]<<endl;
return 0;
}
char q=z.top();
z.pop();
if(q!='('&&s[i]==')'){
cout<<"NO"<<endl;
if(q=='<') cout<<"/*-?"<<endl;
else cout<<q<<"-?"<<endl;
return 0;
}
else if(q!='['&&s[i]==']'){
cout<<"NO"<<endl;
if(q=='<') cout<<"/*-?"<<endl;
else cout<<q<<"-?"<<endl;
return 0;
}
else if(q!='{'&&s[i]=='}'){
cout<<"NO"<<endl;
if(q=='<') cout<<"/*-?"<<endl;
else cout<<q<<"-?"<<endl;
return 0;
}
}
else if(s[i]=='*'&&s[i+1]=='/'){
if(z.empty()){
cout<<"NO"<<endl;
cout<<"?-"<<s[i]<<s[i+1]<<endl;
return 0;
}
i++;///************//
char q1=0;
q1=z.top();
z.pop();
if(q1!='<'){
cout<<"NO"<<endl;
cout<<q1<<"-?"<<endl;
return 0;
}
}
}
if(bk==0) break;
}
if(z.empty()) cout<<"YES"<<endl;
else {
char q1=z.top();
z.pop();
if(q1!='<'){
cout<<"NO"<<endl;
cout<<q1<<"-?"<<endl;
}
else{
cout<<"NO"<<endl;
cout<<"/*-?"<<endl;
}
}
}
⭐2.1.2 解题思路及伪代码
⭐2.1.3 总结解题所用的知识点
- 用getline(cin,s)函数,读取一行的数据
- c++函数库--入栈push,出栈pop,判断栈是否为空empty,取栈顶元素top
🌙2.2 银行业务队列简单模拟
⭐2.2.1解题代码
#include<iostream>
#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;
int main()
{
queue<int> q1,q2;
int n;
cin>>n;
while(n--)
{
int m;
cin>>m;
if(m%2) q1.push(m);//进奇数对
else q2.push(m);//进偶数对
}
while(!q1.empty())
{
int cnt=2,i=0;
while(cnt--&&!q1.empty()) {//弹出两个奇数
if(i++) cout<<" ";
cout<<q1.front();
q1.pop();
}
if(!q2.empty()){//弹出一个偶数
cout<<" "<<q2.front()<<" ";
q2.pop();
}
}
int i=0;
while(!q2.empty())//把多余的偶数弹出
{
if(i++) cout<<" ";
cout<<q2.front();
q2.pop();
}
return 0;
}
⭐2.2.2 解题思路及伪代码
⭐2.2.3 总结解题所用的知识点
- c++自带库函数--入队列push(),出队列pop(),判断队列是否为空empty()
3.阅读代码(0--1分)
找1份优秀代码,理解代码功能,并讲出你所选代码优点及可以学习地方。主要找以下类型代码:
考研题
ACM题解
leecode--栈
leecode--队列
注意:不能选教师布置在PTA的题目。完成内容如下。
🌙3.1 题目及解题代码(来源:leetcode)
3.1.1题目
3.1.2解题代码
int maximalRectangle(char** matrix, int matrixSize, int* matrixColSize) {
int m = matrixSize;
if (m == 0) {
return 0;
}
int n = matrixColSize[0];
int left[m][n];
memset(left, 0, sizeof(left));
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i][j] == '1') {
left[i][j] = (j == 0 ? 0 : left[i][j - 1]) + 1;
}
}
}
int ret = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i][j] == '0') {
continue;
}
int width = left[i][j];
int area = width;
for (int k = i - 1; k >= 0; k--) {
width = fmin(width, left[k][j]);
area = fmax(area, (i - k + 1) * width);
}
ret = fmax(ret, area);
}
}
return ret;
}
🌙3.2 该题的设计思路及伪代码
⭐3.2.1解题思路
我们首先计算出矩阵的每个元素的左边连续 1的数量,使用二维数组left记录,其中left[i][j] 为矩阵第 i行第 j列元素的左边连续 1的数量。
随后,对于矩阵中任意一个点,我们枚举以该点为右下角的全 1 矩形。
具体而言,当考察以matrix[i][j] 为右下角的矩形时,我们枚举满足0≤k≤i 的所有可能的 k,此时矩阵的最大宽度就为
left[i][j],left[i−1][j],…,left[k][j]
的最小值。
下图有助于理解。给定每个点的最大宽度,可计算出底端黄色方块的最大矩形面积。
对每个点重复这一过程,就可以得到全局的最大矩形。
我们预计算最大宽度的方法事实上将输入转化成了一系列的柱状图,我们针对每个柱状图计算最大面积。
⭐3.2.2复杂度分析
-
时间复杂度
O(m^2n),其中 m和 n分别是矩阵的行数和列数。计算left 矩阵需要O(mn) 的时间。随后对于矩阵的每个点,需要O(m) 的时间枚举高度。故总的时间复杂度为 O(mn)+O(mn)⋅O(m)=O(m^2n) -
空间复杂度
O(mn),其中 m和 n分别是矩阵的行数和列数。我们分配了一个与给定矩阵等大的数组,用于存储每个元素的左边连续 1的数量。
🌙3.3 分析该题目解题优势及难点。
- 解题优势
将输入拆分成一系列的柱状图来计算最大宽度 - 难点
对柱状图的分析,将柱状图与题解联系在一起