《程序设计语言综合设计》第五周上机练习

栈和队列

这周的题目,老师介绍了栈和队列,所以在这里先简要码一下知识。
栈和队列是两种操作受限的特殊的线性表。

  • :只能在表尾进行插入或删除操作。允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom)。不含元素的空表称空栈。栈的存储结构:顺序栈(顺序栈一般可以用一维数组和一个栈顶指针来进行实现)和链栈(链栈即栈的链式存储结构,可以用单链表进行实现)。
  • 队列:只能在表尾进行插入,在表头进行删除
    简要知道了概念,我们可以了解一下C++STL中栈与队列的模版,运用这些可以让我们的解题更加便捷。C++基础:C++标准库之栈(stack)和队列(queue)在这篇博客里面已经将这两个模版写的非常清楚了,我来简要码一下。

1、栈(stack)说明及举例:
使用栈,要先包含头文件 : #include<stack>
定义栈,以如下形式实现: stack<Type> s; 其中Type为数据类型(如 int,float,char等)。
栈的主要操作:

s.push(item);		//将item压入栈顶
s.pop();			//删除栈顶的元素,但不会返回
s.top();			//返回栈顶的元素,但不会删除
s.size();			//返回栈中元素的个数
s.empty();			//检查栈是否为空,如果为空返回true,否则返回false

2、队列(queue)说明及举例:
使用队列,要先包含头文件 : #include<queue>
定义队列,以如下形式实现:queue<Type> q;其中Type为数据类型(如 int,float,char等)。
队列的主要操作:

q.push(item)           //将item压入队列尾部
q.pop()                //删除队首元素,但不返回
q.front()              //返回队首元素,但不删除
q.back()               //返回队尾元素,但不删除
q.size()               //返回队列中元素的个数
q.empty()              //检查队列是否为空,如果为空返回true,否则返回false

1 移动圆盘

给出n个圆盘的半径,现在要把这些圆盘依次放在柱子上,当准备把第i个半径为ai的圆盘放置到柱子上时,如果柱子顶部的圆盘半径小于ai,那么将柱子顶部的圆盘拿出,如果顶部的盘子半径仍然小于ai,那么继续拿出,直到顶部圆盘半径大于或等于ai为止,此时才把第i个盘子放到柱子上。那么,最后从下往上输出柱子上的圆盘半径依次是什么?

输入格式:

第一行包含一个整数n(n<=100000),表示有n个圆盘要依次放到柱子上。 接下来n行,每行一个整数,表示第i个圆盘的半径ai (ai<=100000)。

输出格式:

输出多行,表示最后柱子上中的圆盘半径。

输入样例:

5
5
3
2
4
1

输出样例:

5
4
1

Accepted

先来贴一段我不使用栈的方法来写的代码:

#include <cstring>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string.h>
using namespace std;
 int r[100005]={0},rn[100005]={0};
int main(){
    int n,t=1;
    int flag=0;
    cin >> n;
    int i;
    for(i=1;i<=n;i++)
        cin >> r[i];
    rn[t]=r[1];
    for(i=2;i<=n;i++){ 
        if(r[i]>rn[t]){
            while(t>=1&&rn[t]<r[i])
            { t--;flag=1;}
            if(flag==1){t++;flag=0;}
            rn[t]=r[i];
        }
        else{
            t++;
            rn[t]=r[i];
        }
    }
    for(i=1;i<=t;i++){
        cout << rn[i]<< endl;}
    return 0;
}

那么这段代码主要是通过定义两个数组,从而实现满足情况的圆盘班级存入另一个数组中,主要是对t的加减进行判断。
前面已经大体介绍了队列与栈的思想与用法,那么下面我贴出用栈的方法解决的代码:

#include <cstring>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <stack>
using namespace std;
stack<int> radius;
int main(){
    int n,r,rn[100005];
    cin >> n;
    int i,top;
    for(i=0;i<n;i++){
        cin >> r;
        if(radius.empty()) radius.push(r);
        else{
            top=radius.top();
            while(r>top){
                radius.pop();
                if(radius.empty()) break;
               top=radius.top();
           }
            radius.push(r);
    }
    }
    i=0;
    while (!radius.empty()){
        rn[i]=radius.top();
        radius.pop();
        i++;
    }
    for(i--;i>=0;i--){
        cout << rn[i] << endl;
    }
    return 0;
}

2 微信号

小明刚认识了新同学小红,他想要小红的微信号,小红不想直接告诉他,所以给了小明一串加密了的数字,并且把解密规则告诉了小明。
解密规则是:首先删除第1个数,接着把第2个数放在这串数的最后面,再删除第3个数,并把第4个数放在这串数的最后面……直至只剩最后一个数,把最后一个数也删除。
按照删除的顺序,把这些数字连在一起就是小红的微信号。请你按照解密规则帮小明得到小红的微信号。

输入格式:

第一行包括一个正整数n(1 < n < 500),表示这串微信号的长度;
第二行包括n个数字,即加密的小红的微信号。

输出格式:

输出解密后的微信号,相邻数字之间有空格。

输入样例:

9
1 2 3 4 5 6 7 8 9

输出样例:

1 3 5 7 9 4 8 6 2

Accepted

这一题其实就是使用队列的方法就可以解决问题,但是特别要注意的是,在输出的每一个数字后都有一个空格,否则会出现格式错误,那么话不多说直接贴代码:

#include <cstring>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <stack>
#include<queue>
using namespace std;
queue<int> q;
int main(){
    int n,i,w[1000],we,t=1;
    cin >> n;
    for(i=1;i<=n;i++){
        cin >> we;
        q.push(we);
    }
    while(!q.empty()){
        w[t]=q.front();
        t++;
        q.pop();
        if(q.size()==1){
            w[t]=q.front();
            break;
        }
        q.push(q.front());
        q.pop();
    }
    for(i=1;i<=t;i++){
        cout <<w[i] <<" ";
     //   if(i!=t) cout <<" ";
       // else cout << endl;
    }
    return 0;
}

3 糖果

学校里有n个孩子,从1到n对这些孩子进行编号。老师将给孩子们分发糖果,第i个孩子希望至少获得ai个糖果。
老师要求孩子们排队。 最初,第i个孩子站在队伍的第i个位置。 然后,老师开始分发糖果。分发糖果的规则是:将m个糖果给队伍中的第一个孩子,如果这个孩子没有得到足够的糖果,那么这个孩子会走到队伍的尽头;否则这个孩子就回家了。当队伍不为空时,重复这个规则一直分发糖果。 如果考虑所有孩子回家的顺序。老师想知道,哪个孩子将是这个顺序中的最后一个?

输入格式:

第一行包含两个整数n,m(1≤n≤100; 1≤m≤100)。 第二行包含n个整数a1,a2,...,an(1≤ai≤100)。

输出格式:

输出一个整数,代表最后一个孩子的编号。

输入样例:

在这里给出一组输入。例如:
3 3
2 4 3

输出样例:

在这里给出相应的输出。例如:
2

Accepted

这题很明显是可以用队列的方法来解决问题(但是其实我认为不用队列的代码其实更简短)那么我们首先给出普通解法(来源:yjs)

#include<iostream>
using namespace std;
int candy[1000];
int main() {
    int n,m,ans;
    cin >> n >> m;
    for(int i=0;i<n;i++) {
        cin >>candy[i];
    }
    while(1) {
        int change=0;
        for(int i=0;i<n;i++) {
            if(a[i]>0) {
                candy[i]-=m;
                change=1;
                ans=i+1;
            }
        }
        if(change==0) {
            break;
        } 
    }
    cout << ans;
}

接下来贴出使用队列的代码:

#include <cstring>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <stack>
#include<queue>
using namespace std;
queue<int> q;
int main(){
    int n,i,m,candy[500];
    cin >> n >> m;
    for(i=1;i<=n;i++){
        cin >> candy[i];
        if(m<candy[i]) {
             q.push(i);
            candy[i]-=m;
        }
    }
    if(q.empty()) cout << n <<endl;
    else{
        while(!q.empty()&&q.size()!=1){
            if(candy[q.front()]>m){
                   candy[q.front()]-=m;
                q.push(q.front());
                q.pop();
            }
            else{
                q.pop();
            }
        }
        cout << q.front() << endl;
    }
    return 0;
}

4 谁比我大

给定一个含有n个整数的数列a1,a2,...an。定义函数 f(ai)表示数列中第i个元素ai之后第一个大于ai的元素的下标,若这样的元素不存在,则f(ai)=0。

输入格式:

第一行包含一个正整数n(n<=1e6);
第二行包含n个正整数 a1,a2,...an(1<=ai<=1e9)。

输出格式:

输出仅一行包含 n个整数,分别代表 f(ai) 的值。

输入样例:

5
1 4 2 3 5

输出样例:

2 5 4 5 0

Accepted

这一题刚拿到题目其实我就写了一个函数用一个循环解决了问题,因为一开始觉得这样做更加的简便:

#include <cstring>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <stack>
#include<queue>
using namespace std;
queue<int> q;
int a[1000005],n;
int find(int num){
    int i;
    for(i=num+1;i<=n;i++){
        if(a[i]>a[num]) return i;
    }
    return 0;
}
int main(){
    int i;
    cin >> n;
    for(i=1;i<=n;i++)
        cin >>a[i];
    for(i=1;i<=n;i++) {
          cout << find(i) <<" ";
      }
    return 0;
}

那如何使用栈或队列的知识解决这一题呢?其实这一题的解决方法是使用栈。那么如何解决呢?其实解决的关键在于我将输入的数,倒一头从尾到头输入栈中然后进行相应处理,那么结合代码更好理解:
假如a[n-1]>a[n],那么在n-1之前的数,答案肯定不为n。所以设置一个栈,然后倒着从第n个数开始判断。假如当前处理到a[i]:

  • 第一步:把栈中所有小于a[i]的元素出栈;
  • 第二步:栈头元素就是要找的,离a[i]最近的比它大的数;
  • 第三步:把a[i]压进栈;
  • 第四步:i=i-1;
    当然最后不要忘记倒序输出答案。
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <stack>
#include<queue>
using namespace std;
stack<int> s;
int a[1000005],n;
int main(){
    int i,j;
    int top;
    int ans[1000005];
    cin >> n;
    for(i=1;i<=n;i++)
        cin >>a[i];
    i=1;j=n;
    while(j>=1){
        top=a[j];
        while(!s.empty()&&a[s.top()]<=top)
            s.pop();
        if(s.empty()) ans[i]=0;
        else ans[i]=s.top();
        s.push(j);
        i++;
        j--;
    }
    for(j=i-1;j>=1;j--)
        cout << ans[j] << " ";
    return 0;
}

5 后缀表达式

所谓后缀表达式是指这样的一个表达式:式中不再引用括号,运算符号放在两个运算对象之后,所有计算按运算符号出现的顺序,严格地由左而右进行(不用考虑运算符的优先级)。
如:中缀表达式 3*(5–2)+7 对应的后缀表达式为:352-*7+ 。
请将给出的中缀表达式转化为后缀表达式并输出。

输入格式:

输入仅一行为中缀表达式,式中所有数字均为个位数,表达式长度小于1000。

输出格式:

输出一行,为后缀表达式,式中无空格。

输入样例:

2+48+(88+1)/3

输出样例:

248*+88*1+3/+

Accepted

(参考:中缀表达式转换为后缀表达式(思路)中缀表达式转换为后缀表达式(C语言代码+详解)洛谷

具体转换方式:
1.从左到右进行遍历
2.运算数,直接输出.
3.左括号,直接压入堆栈,(括号是最高优先级,无需比较)(入栈后优先级降到最低,确保其他符号正常入栈)
4.右括号,(意味着括号已结束)不断弹出栈顶运算符并输出直到遇到左括号(弹出但不输出)
5.运算符,将该运算符与栈顶运算符进行比较,
如果优先级高于栈顶运算符则压入堆栈(该部分运算还不能进行),
如果优先级低于等于栈顶运算符则将栈顶运算符弹出并输出,然后比较新的栈顶运算符.
(低于弹出意味着前面部分可以运算,先输出的一定是高优先级运算符,等于弹出是因为同等优先级,从左到右运算)
直到优先级大于栈顶运算符或者栈空,再将该运算符入栈.
6.如果对象处理完毕,则按顺序弹出并输出栈中所有运算符.

#include <cstring>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <stack>
using namespace std;
stack<char> sign;
int cmp(char a){
    if(a=='*'||a=='/')return 2;
    else if(a=='+'||a=='-') return 1;
    else if(a=='^') return 3;
    else return 0;
}
int main(){
    string c;
        cin >> c;
    for(int i=0;i<c.size();i++){
        if(c[i]>='0'&&c[i]<='9') //为数字,直接输出结果
            cout << c[i];
        else{
            if(c[i]==')'){//如果为右括号
                while(sign.top()!='('){//弹出栈并输出直到遇到左括号
                       cout << sign.top();
                       sign.pop();
                   }
                sign.pop();// 弹出左括号
            }
            else if(c[i]=='(') sign.push(c[i]); // 如果为左括号,直接入栈
            else{ //如果为普通的运算符
                while(!sign.empty()&&cmp(sign.top())>=cmp(c[i])){
                    // 一直弹出栈并输出直到遇到比当前优先级低的运算符,
                    cout << sign.top();
                    sign.pop();
                }
                sign.push(c[i]); //最后把当前运算符压入栈中
            }
       }
    }
   //最后把栈中剩余的运算符依次弹栈输出
    while(!sign.empty()){
        cout << sign.top();
        sign.pop();
    }
    return 0;
}

6 后缀表达式计算

Kunkun学长觉得应该让学弟学妹了解一下这个知识点:后缀表达式相对于中缀表达式更容易让计算机理解和学习。现在kunkun学长给出一串后缀表达式,你能帮他算出这个后缀表达式的值吗?

输入格式:

第一行输入后缀表达式长度n(1<=n<=25000);
第二行输入一个字符串表示后缀表达式(每个数据或者符号之间用逗号隔开,保证输入的后缀表达式合法,每个数包括中间结果保证不超过long long长整型范围)

输出格式:

输出一个整数,即后缀表达式的值。

输入样例1:

6
10,2,+

输出样例1:

12

输入样例2:

14
2,10,2,+,6,/,-

输出样例2:

0

Wrong Answer

有了前一题的基础,这一题处理起来相对会比较简单,只不过这次我们是让数字压入栈中,然后如果碰到运算符,在对前两个栈中的数字进行相应处理。特别注意数字可能有多位,以及运算时候的顺序(特别是减法和除法时,顺序不能颠倒)。

#include <cstring>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <stack>
using namespace std;
stack<long long> num;
int main(){
    int n,i,flag=0;
    long long num1,num2;
    char ch;
    cin >> n;
    getchar();
    for(i=0;i<n;i++){
        cin >> ch;
        if(ch>='0'&&ch<='9'&&flag==0) {//如果为数字压入栈中,注意数可能有多位
            num.push(ch-'0');
            flag=1;
        }
        else if(ch>='0'&&ch<='9'&&flag==1) {
            num.top()= num.top()*10+(ch-'0');
            flag=1;
        }
        else if(ch==','&&flag==1){
            flag=0;
        }
        else if(ch=='+'){
            num1=num.top();
            num.pop();
            num2=num.top();
            num.pop();
            num.push(num2+num1);
        }
        else if(ch=='-'){
            num1=num.top();
            num.pop();
            num2=num.top();
            num.pop();
            num.push(num2-num1);
        }
        else if(ch=='*'){
            num1=num.top();
            num.pop();
            num2=num.top();
            num.pop();
            num.push(num2*num1);
        }
        else if(ch=='/'){
            num1=num.top();
            num.pop();
            num2=num.top();
            num.pop();
            num.push(num2/num1);
        }
    }
    cout << num.top() << endl;
    return 0;
}

但是当我提交代码的时候,却发现有两个点显示段错误,这是为什么呢?这是因为在这个表达式中存在负数,所以我需要对负数的符号与减号符号区分开。

Accepted

(参考:洛谷

#include <cstring>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <stack>
using namespace std;
stack<long long> num;
int main(){
    int n,i,flag=0,flag1=0;
    long long num1,num2;
    char ch[25005] ;
    cin >> n;
    getchar();
    for(i=0;i<n;i++)
        cin >> ch[i];
    for(i=0;i<n;i++){
        if(ch[i]=='-'&&ch[i+1]>='0'&&ch[i+1]<='9') flag1=1;
        if(ch[i]>='0'&&ch[i]<='9'&&flag==0) {//如果为数字压入栈中,注意数可能有多位
            num.push(ch[i]-'0');
            flag=1;
        }
        else if(ch[i]>='0'&&ch[i]<='9'&&flag==1) {
            num.top()= num.top()*10+(ch[i]-'0');
            flag=1;
        }
        else if(ch[i]==','&&flag==1&&flag1==0)
            flag=0;
        else if(ch[i]==','&&flag==1&&flag1==1){ //若为负数
            flag=0;
            flag1=0;
             num.top()= num.top()*(-1);
        }
        else if(ch[i]=='+'){
            num1=num.top();
            num.pop();
            num2=num.top();
            num.pop();
            num.push(num2+num1);
        }
        else if(ch[i]=='-'&&flag1==0){
            num1=num.top();
            num.pop();
            num2=num.top();
            num.pop();
            num.push(num2-num1);
        }
        else if(ch[i]=='*'){
            num1=num.top();
            num.pop();
            num2=num.top();
            num.pop();
            num.push(num2*num1);
        }
        else if(ch[i]=='/'){
            num1=num.top();
            num.pop();
            num2=num.top();
            num.pop();
            num.push(num2/num1);
        }
    }
    cout << num.top() << endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/lvhang/p/12539751.html