JLU数据结构荣誉课——第一次上机实验

Posted by φκι on Monday, September 20, 2021

JLU数据结构荣誉课——第一次上机实验

一、7—1重复计数

在一个有限的正整数序列中,有些数会多次重复出现。请你统计每个数的出现次数, 然后按数字在序列中第一次出现的位置顺序输出数及其次数。

image

输入格式: 第1行,1个整数N,表示整数的个数,(1≤N≤50000)

第2行,N个正整数,每个整数x 都满足 1 ≤ x ≤2000000000

输出格式: 若干行,每行两个用一个空格隔开的数,第一个是数列中出现的数,第二个是该数在序列中出现的次数。 输入样例:

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

12
8 2 8 2 2 11 1 1 8 1 13 13

输出样例:

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

8 3
2 3
11 1
1 3
13 2

方法:

这里直接推荐使用C++STL库中的map容器,数据在map容器中存储结构是红黑树,其操作函数中find``count在查找过程中均为O(nlogn) 不超出这道题的时间限制,且退化性不强,下面是代码:

#include<iostream>
#include<map>
using namespace std;
int a[50010];
int main()
{
    int n;
    cin>>n;
    map<int,int>m;//定义map容器,key值为所给数字值,value值为所给计数值
    int count1=0;
    int n1;
    for(int i=0;i<n;i++)
    {
        cin>>n1;
        map<int,int>::iterator pos=m.find(n1);
        if(pos!=m.end())//没有该数字
        {
            pos->second++;
        }
        else
        {
            m.insert(pair<int,int>(n1,1));
            a[count1++]=n1;
        }
    }
    for(int i=0;i<count1;i++)
    {
        map<int,int>::iterator p0=m.find(a[i]);//查找为O(nlogn)
        cout<<a[i]<<" "<< (*p0).second ;
        if(i!=count1-1)
            cout<<endl;
    }
    return 0;
}

//本题目没有使用时间复杂度较低的扫描和打印就通过了.

评价:没有像许多同学一样,开了数组存value,直接存到second中不香么。

二、7-2报数游戏

image

n个人围成一圈,从1开始依次编号,做报数游戏。 现指定从第1个人开始报数,报数到第m个人时,该人出圈,然后从其下一个人重新开始报数,仍是报数到第m个人出圈,如此重复下去,直到所有人都出圈。 总人数不足m时将循环报数。请输出所有人出圈的顺序。

输入格式: 一行,两个整数n和m。n表示游戏的人数,m表示报数出圈的数字,1≤n≤50000,1≤m≤100.

输出格式: 一行,n个用空格分隔的整数,表示所有人出圈的顺序

输入样例: 在这里给出一组输入。例如:

5 2

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

2 4 1 5 3

分析:约瑟夫问题,我认为这类题用循环链表实现最为直观,每次报数报到m-1个时,跳出循环,删去节点。具体代码如下:

#include<iostream>
using namespace std;
struct Node
{
    int n;
    Node*next;
};
int main()
{
    int m,n;
    cin>>m>>n;
    int num=m;
    Node*p=NULL;
    Node*p1=NULL;
    Node*head=NULL,*rear=NULL;//用尾插法
    while(num!=0)
    {
        p=new Node;
        p->n=m-num+1;
        p->next=NULL;
        if(head==NULL)
            head=rear=p;
        else
        {
            rear->next=p;
            rear=p;
        }
        num--;
    }//建立链表
    rear->next=head;
    p=head;
    p1=rear;
    while(p->next!=p)
    {
        int num1=n-1;
        while(num1!=0)
        {
            p1=p;
            //cout<<p->n;
            p=p->next;
            num1--;
        }
        cout<<p->n<<" ";
        p1->next=p->next;
        delete p;
        p=p1->next;//删除操作
    }
    cout <<p->n;
    return 0;
}

建议:这里建议使用静态链表,空间缩小的同时,操作和书写较为方便。

三、7-3 算术表达式计算

任务: 计算算术表达式的值。 算术表达式按中缀给出,以=号结束,包括+,-,/四种运算和(、)分隔符。运算数的范围是非负整数,没有正负符号,小于等于109 。

image

计算过程中,如果出现除数为0的情况,表达式的结果为”NaN” ; 如果中间结果超出32位有符号整型范围,仍按整型计算,不必特殊处理。 输入保证表达式正确。

思路:使用了双栈结构,典型的大模拟,麻烦不好调,但是冗长的代码,依旧可以写的很优美。用一个栈存储符号,用另一个栈存储数据,根据优先级进行评判出栈入栈顺序。

注意:这道题的测试点给的非常刁钻,注意空廓号,和单括号的处理,以及多种符号的评判。

代码如下:

#include<iostream>
#include<stack>
#include<cstdlib>
using namespace std;
bool level(char &ch1,char &ch2);//比较优先级函数
int givelevel(const char ch);//评级函数
int calculate(const int num1,const int num2,const char operator_ch);//计算函数
bool checklevel(int n1,int n2);//显示等级函数
void do_calculate();//单步计算函数
stack<char>Cstack;//符号栈
stack<int>Istack;//数字栈
int main()
{

    char ch;
    int num1=0;
    cin>>ch;
    while(1)
    {
        if(ch>='0'&&ch<='9')
        {
            while(ch>='0'&&ch<='9')
            {
                num1=num1*10+(ch-'0');
                cin>>ch;
            }
        Istack.push(num1);
        num1=0;
        }
        if(ch!='=')
        {
            if(Cstack.empty())
            {
                 Cstack.push(ch);
            }
            else if(ch==')')
            {
                while(Cstack.top()!='(')
                {
                    do_calculate();
                }
                Cstack.pop();
            }
            else if(ch=='('||
            checklevel(givelevel(ch),givelevel(Cstack.top())))
            {
                Cstack.push(ch);
            }
            else
            {
                while(!Cstack.empty()&&
                (!checklevel(givelevel(ch),givelevel(Cstack.top()))))
                      {
                          do_calculate();
                      }
                      Cstack.push(ch);
            }
        }
        else
        {
            while(!Cstack.empty())
            {
                 do_calculate();
            }
            if(!Istack.empty())
            cout << Istack.top();
            else
            cout<<'0';
            break;
        }
        cin>>ch;
    }
    return 0;
}
bool checklevel(int n1,int n2)
{
    if(n1>n2)
        return true;
    else return false;
}
int givelevel(const char ch)
{
    if(ch=='*'||ch=='/')
        return 2;
    else if(ch=='+'||ch=='-')
        return 1;
        else return 0;
}
int calculate(const int num1,const int num2,const char operator_ch)
{
    if(operator_ch=='*')
        return num1*num2;
    else if(operator_ch=='/')
        return num1/num2;
    else if(operator_ch=='+')
        return num1+num2;
    else if(operator_ch=='-')
        return num1-num2;
    else
        return -1;
}
void do_calculate()
{
    char ch1=Cstack.top();
    Cstack.pop();
    int num1=Istack.top();
    Istack.pop();
    int num2=Istack.top();
    Istack.pop();
    if(num1==0&&ch1=='/')
    {
        cout << "NaN"<<endl;
        exit(0);
    }
    else
    Istack.push(calculate(num2,num1,ch1));
}

四、7-4 最喜爱的序列

小唐这段时间在研究序列。拿来N个整数的序列,他给序列中的每个整数都赋予一个喜爱值。喜爱值也是整数,有正有负,越大表明越喜欢。他想知道,如何从序列中连续取最多m个数,他获得喜爱值最大。1≤N≤500000,1≤m≤N。 输入格式: 第一行是两个整数N,m。分别代表序列中数的个数以及能取的最多个数。

image

第二行用空格隔开的N个整数,第i个整数Li代表他对第i个数的喜爱值。│Li│≤1000

输出格式: 一行,三个数,表示获得最大喜爱值,及第一个取最大喜爱值的区间。

输入样例: 在这里给出一组输入。例如:

5 2
1 4 5 2 3

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

9 2 3

思路:一开始一看没时间了,直接暴力了,不出所料,给了60,哎,要是我看错了题多好😂🤣,仔细一想,并看到大佬们的做法,豁然开朗,不就是单调队列+前缀和么,维护一个前缀和序列,维护一个给定区间,让队列保持递增,即最前者所代表前缀和在给定区间最小。

单调队列部分:

while(head<rear&&sum[i]<sum[que[rear-1]])rear--;
que[++rear]=i;
while(que[head]<i-m+1&&head<rear)head++;

代码如下:

#include<iostream>
#include<cstdlib>
using namespace std;
int ans[500010];
int sum[500010];
int que[500010];
int main()
{
    int N,m;
    cin>>N>>m;
for(register int i=0;i<N;i++)
{
    cin>>ans[i];
    sum[i]=sum[i-1]+ans[i];
}
int score[3]={0};
score[0]=sum[0];
int head=0,rear=0;
for(int i=1;i<N;i++)
{
    if(score[0]<sum[i]-sum[que[head]])
    {
         score[0]=sum[i]-sum[que[head]];
         score[1]=que[head]+1;
         score[2]=i;
    }//每次判断存储最大差值。
    while(head<rear&&sum[i]<sum[que[rear-1]])rear--;
    que[++rear]=i;
    while(que[head]<i-m+1&&head<rear)head++;
}
cout<<score[0]<<" "<<score[1]+1<<" "<<score[2]+1;
return 0;
}

还是太菜,继续努力qwq。