吉林大学2023数据结构《第三次上机实验》解题报告

《第三次上机实验》解题报告

1  重复计数

先讲一下我在考试时的想法吧:二叉查找树(bst)但是我太菜了只会写最基本的bst同时为了图方便我使用了顺序存储,于是导致只要左边多了一层,总结点数就要*2,于是只要偏了四五层就必定会爆空间。而同学们居然暴力就能过,太痛了!!!!!!!

思想;谁是倒一,我是倒一!!!!!!!!于是回去之后学了一点map,用map直接逃课,将数字同时输入map,和一个记录顺序的数组,之后按数组记录的顺序输出。

时间复杂度;O(n)

总结;太离谱了,暴力直接过,要不就是map,谷老师说的排序怎么排我还是搞不懂。

#include<stdio.h>
#include<map>
int y[51000];
using namespace std;
map<int, int> maps;
int main()
{
    int n, a, b, c, d;
    scanf("%d", &n);
    for (a = 1; a <= n; a++)
    {
        scanf("%d", &b);
        y[a] = b;
        maps[b]++;
    }
    for (a = 1; a <= n; a++)
    {
        if (maps[y[a]] != 0)
        {
            printf("%d %d\n",y[a], maps[y[a]]);
            maps[y[a]] = 0;
        }
    }
}

2  二叉树加权距离

考试的时候,用广搜完成了部分,主要没想到询问的节点可以在根的同一子树当中,于是就没写找最小祖父节点的代码,等写完后就不好改了。

思想;回去了之后用了一下递归的深搜,找到节点,保存路径,保存深度,然后通过保存的路径找到最小祖父节点,我想的没错,但是因为递归爆空间了。于是我将保存的边倒过来,从节点找到根,保存深度,然后将深度统一,一起往上走,知道碰面。将路径长度输出。于是这样什么dfs,bfs都不用,直接四个循环就出来了。

时间复杂度;O(n);

总结;将边倒过来存,只保存从各节点的父亲节点,直到根,然后一个一个的找过去就行了。

#include<stdio.h>
int x[100100];
int main() {
    int n, a, b, c,i,j,t,t1=0,t2=0,s=0;
    scanf("%d", &n);
        for (a = 1; a < n; a++){
            scanf("%d %d", &b, &c);
            x[c]=b;
        }
        scanf("%d %d", &i, &j);
        c = i;
        while (c != 1){
            t1++;
            c = x[c];
        }
        c= j;
        while (c != 1){
            t2++;
            c = x[c];
        }
        while(t1 < t2){
            j = x[j];
            s += 2;
            t2--;
        }
        while (t1 > t2){
            i = x[i];
            s += 3;
            t1--;
        }
        while (i != j){
            j = x[j];
            i = x[i];
            s += 5;
        }
        printf("%d", s);
    return 0;
}
 

3  发红包

关键路径问题+森林,在考试的时候想到的是bfs但是写不出来

思想;用两个vector分别存两个方向的边,用一个数组存入读,用一个数组存最小深度,用一个数组当栈来进行bfs。开始先遍历将入度为0的点存入,然后进行bfs改变最小深度,最后遍历,将深度累加,若还有入度大与一的输出-1,不然输出累和。

时间复杂度;O(m)

总结;本来是最简单的一题,但是放在第三个,我被前两个卡住了,最后没时间写了,开始没想到关键路径,又走了弯路,最后也没编完。

#include<stdio.h>
#include<vector>
using namespace std;
vector<int>y[10100];
vector<int>z[10100];
int x[10100] = {};
int k[101000] = {};
int t[10100] = {};
int main()
{
    int n, m, a, b, c,s=0,r=1,l=1;
    scanf("%d %d", &n, &m);
    for (a = 1; a <= m; a++)
    {
        scanf("%d %d", &b, &c);
        z[b].push_back(c);
        y[c].push_back(b);
        x[c]++;
    }
    for (a = 1; a <= n; a++)
    {
        if (x[a] == 0)
        {
            k[r] = a;
            r++;
        }
    }
    for (l = 1; l <= r; l++)
    {
        a = k[l];
        for (b = 0; b < z[a].size(); b++)
        {
            x[z[a][b]]--;
            if (x[z[a][b]] == 0)
            {
                k[r] = z[a][b];
                r++;
            }
        }
        c = 0;
        for (b = 0; b < y[a].size(); b++)
        {
            if (t[y[a][b]] > c)
                c = t[y[a][b]];
        }
        t[a] = c + 1;
    }
    b = 0;
    for (a = 1; a <= n; a++)
    {
        if (x[a] != 0)
        {
            b = 1;
            break;
        }
        s += t[a];
    }
    if (b == 1)
    {
        printf("-1");
    }
    else
    {
        printf("%d", n * 887 + s);
    }
    return 0;
}

4  数据结构设计I

在考试的时候想骗一点分,没想到是一点都骗不到呀(悲)。

思想;想了半天想不到一种不用stl的方法,于是用了一个queue和multiset来实现,一旦插入,就同时向两个链表同时插入,一旦输出,就从双端队列尾部输出然后从对应的multiset中输出,每次取反就将指针r=0/1来取反,一旦输出,若r=0输出multiset的头,若r=1输出multiset的尾。

时间复杂度;O(n*log n)

总结;想不到一种不用stl的方法,要注意当r=1时输入应*-1,同时弹出时应判空,不然会段错误。

#include<stdio.h>
#include<set>
#include<queue>
using namespace std;
multiset<int,greater<int>>x;
deque<int>y;
int main()
{
    int m, r=0, a, b, c, d;
    char ch;
    scanf("%d", &m);
    for (a = 1; a <= m; a++)
    {
        scanf("%c", &ch);
        while(ch!='I'&& ch != 'D'&& ch != 'R'&& ch != 'M')
            scanf("%c", &ch);
        if (ch == 'I')
        {
            scanf("%d",&b);
            if (r == 1)
                b = b * -1;
            x.insert(b);
            y.push_front(b);
        }
        if (ch == 'D'&&x.size() != 0)
        {
            b = y.back();
            y.pop_back();
            x.erase(x.find(b));
        }
        if (ch == 'R')
        {
            if (r == 0)
                r = 1;
            else
                r = 0;
        }
        if (ch == 'M'&&x.size()!=0)
        {
            if (r == 0)
            {
                multiset<int>::iterator it = x.begin();
                printf("%d\n", *it);
            }
            else
            {
                multiset<int>::reverse_iterator its = x.rbegin();
                printf("%d\n", *its*-1);
            }
        }
    }
    return 0;
}
 

5  奖学金

孔姐姐的左偏树,看了孔姐姐的ppt又上了csdn上看了2个小时,眼睛说看懂了,脑子却全忘了,最后完全抄的孔姐姐ppt的代码。

思想;可合并堆,可以在log时间内完成两个堆的合并。merge(x,y):将堆顶x和y的两个堆合并,返回合并后的堆顶。删除操作,直接合并左右儿子。

时间复杂度;O(n*log n)

总结;看了半天看不懂,只能抄代码了,结果不愿意用swap函数,手写了个交换函数,结果只能交换形参,找了半天,我寻思着我不是照抄的么,怎么还能错,改了半天才找到时swap的问题。

#include<stdio.h>
#include <iostream>
using namespace std;
int n, m;
struct cell {
    int ls, rs, dist, val;
}tr[100100];
int fa[100100];
bool del[100100];
int find(int a)
{
    if (fa[a]==a)
        return a;
    return fa[a] = find(fa[a]);
}
int merge(int x, int y) {    //合并
    int t;
    if (!x || !y) return x | y;   //???不理解?????
    if (tr[x].val < tr[y].val || (tr[x].val == tr[y].val && x > y))
    swap(x,y);
    tr[x].rs = merge(tr[x].rs, y);
    if (tr[tr[x].ls].dist < tr[tr[x].rs].dist)  
     swap(tr[x].ls,tr[x].rs);
    tr[x].dist = tr[tr[x].rs].dist + 1;
    return x;
}
void erase(int x)
{
    del[x] = 1;
    fa[x] = fa[tr[x].ls] = fa[tr[x].rs] = merge(tr[x].ls, tr[x].rs);
}
int main()
{
    int a, b, c,d,e,f;
    scanf("%d %d", &n, &m);
    for (a = 1; a <= n; a++)
    {
        scanf("%d", &b);
        fa[a] = a;
        tr[a].val = b;
    }
    for (a = 1; a <= m; a++)
    {
        scanf("%d", &b);
        if (b == 1)
        {
            scanf("%d %d", &c, &d);
            if (find(c)==find(d)||del[c]||del[d])
                continue;
            c = find(c);
            d = find(d);
            fa[c] =fa[d]= merge(c,d);
        }
        if (b == 2)
        {
            scanf("%d", &c);
            if (del[c])
                continue;
            erase(find(c));
        }
        if (b == 3)
        {
            scanf("%d", &c);
            if (del[c]) {
                printf("-1\n");
                continue;
            }
            printf("%d\n", tr[find(c)].val);
        }
    }
    return 0;
}
 

  

  

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值