博客作业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>==

  • stacks:初始化栈,参数表示元素类型
  • 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 分析该题目解题优势及难点。

  • 解题优势
    将输入拆分成一系列的柱状图来计算最大宽度
  • 难点
    对柱状图的分析,将柱状图与题解联系在一起
posted @ 2021-04-05 21:37  noyiie  阅读(182)  评论(0编辑  收藏  举报
// 侧边栏目录 // https://blog-static.cnblogs.com/files/douzujun/marvin.nav.my1502.css