(新版)SJTU-OJ-1058. 小恶魔的单循环链表

题目描述

助教是个小恶魔,她不知道要出什么机考题,然后她发现大家已经学完了指针和结构体,欣喜若狂。她想让你写一个单循环链表,即最后一个结点的下一个结点是头结点的单链表。你需要实现以下六种操作,分别标号为操作0~5:(注意这里的 i i i都为0-base

  • 0 返回链表长度;
  • 1 在位置 i i i 插入一个数。注意这里是指插入的数在插入后处于位置ii,即插入在现在位置ii数字的前面;
  • 2 输出位置 i i i 的数。如果 i i i 超出链表长度,请输出-1;
  • 3 删除位置 i i i 的数,整个链表长度减1;
  • 4 删除位置 i i i 的数并将它插入至链表的尾端;
  • 5 输出链表中最大的数。如果表为空请输出 i i i

注意:为防止内存泄漏,你需要在程序最后删除整个列表。

sample.cpp

#include <iostream>
#include <cstdio>
using namespace std;

namespace LIST
{

    struct NODE {
        // TODO
    };

    NODE *head = nullptr;
    int len = 0;

    void init() {
        // TODO
    }
    NODE* move(int i) {
        // TODO
    }
    void insert(int i, int x) {
        // TODO
    }
    void remove(int i) {
        // TODO
    }
    void remove_insert(int i) {
        //TODO
    }
    void get_length() {
        // TODO
    }
    void query(int i) {
        // TODO
    }
    void get_max() {
        // TODO
    }
    void clear() {
        // TODO
    }

}
int n;
int main()
{
    cin >> n;
    int op, x, p;
    LIST::init();
    for (int _ = 0; _ < n; ++_)
    {
        cin >> op;
        switch(op) {
            case 0:
                LIST::get_length();
                break;
            case 1:
                cin >> p >> x;
                LIST::insert(p,x);
                break;
            case 2:
                cin >> p;
                LIST::query(p);
                break;
            case 3:
                cin >> p;
                LIST::remove(p);
                break;
            case 4:
                cin >> p;
                LIST::remove_insert(p);
                break;
            case 5:
                LIST::get_max();
                break;
        }
    }
    LIST::clear();
    return 0;
}

列表的要求如下:

  • 必须使用指针结构体
  • 在单链表中,每个结点由一个数据元素、一个后继指针组成,后继指针指向存储该元素直接后继的结点;
  • 存储一个单链表只需要一个指向头结点的指针;
  • 可以使用单独的变量记录链表长度;
  • 请不要修改给定的部分
  • 此题需要你注意提交代码的代码风格,极为糟糕的代码风格(例如改变语法的某些宏定义)会酌情扣分!!!

请严格按照以上要求完成此题。我们会检查你提交的代码。虽然有其他的实现方式,但如果你未按照要求写代码,我们不会给你分数。(比如,你没有使用指针和结构体或在每个结点中记录了要求以外的内容等都会判为0分)。

输入格式

第一行一个数 n n n表示操作数。

之后 n n n 行,每行第一个数 o p ( o p = 0 , 1 , . . . , 5 ) op(op = 0, 1, ..., 5) op(op=0,1,...,5)代表操作编号,分别对应前文中的编号。

如果 o p op op 为1,其后会输入两个整数 p p p, x x x,表示在pp位置插入的数值为 x x x

如果 o p op op 为2,其后会输入一个整数 p p p,表示你需要输出链表中 p p p 位置结点的值;

如果 o p op op 为3或4,其后会输入一个整数 p p p,表示你需要删除链表中 p p p 位置的值。

数据保证插入删除操作不会操作无效位置。

输出格式

对于操作1,3,4你不用输出任何东西;

对于操作0,输出一个整数,表示链表长度;

对于操作2,输出一个整数,表示第ii个数;

对于操作5,输出一个整数,表示链表中最大的数;

每一个操作输出后要求换行。

样例输入

12
0
1 0 1
1 0 2
1 2 3
5
4 1
2 1
5
3 0
3 0
5
0

样例输出

0
3
3
3
1
1

数据范围

n ≤ 1000 n \leq 1000 n1000

对于10%的数据,只会涉及0、1操作;

对于20%的数据,只会涉及0、1、2操作;

对于40%的数据,只会设计0、1、2、3操作;

对于60%的数据,只会涉及0、1、2、3、5操作;

对于100%的数据,会涉及所有操作。

保证链表中所有数字为int范围内正整数。保证所给部分和所给要求不会产生超时问题。

题目评价

      首先,这个题目真的是挺老了,明显是一个emm就是基础题目,这个题目的来源是:【程序设计2020 第3次机考】,机考满分的人数为0,有分数的有两人,分别是70、80,可见这个题目的威力。
      这里也想提一下之前的一道题,就是第0次机考的单链表的题目,如果好奇可以走这里的传送门:(新版)SJTU-OJ-1005. Linked List
      所以纯属旧题新放送,甚至我觉得这个助教就是为了送分数的一道题目,然鹅22333。
      这个题需要的知识点有以下内容:

  • 结构体、指针的概念、操作
  • 单链表的建立,动态变量的申请与回收
  • 单链表元素的删除
  • 单链表元素的添加
  • 对结构体内函数的理解,类与对象的理解

      所以说,如果是恰恰刚学习了单链表后,然后进行机考,这个题目显然是有点难的。从工科实验班的课程(程序设计思想与方法(C++))的要求来看,课本上所有的星号内容不要求掌握,此外单链表也不是考试的范围,所以还是说明这一部分是有一定的难度的,以至于程序设计的课不太想要涉及到这个层面的内容,想要放到数据结构里面让学生学习。但是从我个人的角度来说,这部分内容完全是可以学的会的,而且单链表的学习关键在于画图,最好是对照自己写的代码,然后画图,一步一步的指示运行。
      为什么要说单链表的学习关键在于画图?我个人看来,单链表与数组不同,数组的操纵,明显要比单链表简单。因为数组的下标是整数,直观明了,就是我们高中学的数列,而且在内存中,我们也都知道,是一块连续的空间,操作起来明显要简单,只需要注意不能下标越界,但是,单链表完全不一样,比如你要申请一个空间,这个空间是内存的一个小部分,你看不到,也摸不着,你唯一操作这个空间的方法就是寻找,通过上一个节点来寻找,通过指针来操作,每次你要找第 i i i 个元素,你必须从第一个元素开始寻找,从前往后,在删除节点的时候,回收整个链表的时候,往往因为+1,-1之类的问题,导致内存泄漏,当然泄漏的可能就是一个变量,但是日积月累呢?危害不可设想,所以回收链表,初学最好一边画图一边对照。
      最后,写到这里,也不得不感慨ACM班,ACM班不同于别的班级的地方。第0次机考单链表,大致学完数组就做广度优先搜索,深度优先搜索的题目,单链表,二叉树,后面还有什么我也不太清楚,反正就是学的难,快,要求高、严。真的很不容易。

题目解答

      由于之前那个文章单链表已经很详细的讲述了链表的知识,所以我这里只说一下单链表与循环链表的区别,首尾相连,就这,没有别的了。所以怎么做?按照之前的方法做。
      此外,如果还是不会请看课本的196面,约瑟夫问题的解决代码,通过这个案例你也许可以对循环链表有更深刻的理解。[最好可以自己和上书写一波代码]
      说一下这个题目很坑的地方,就是:申请的节点一定要赋值,不要空缺,否则OJ会认为内存泄漏!!上代码吧:head->data = -1; 这个赋值一定要写!
      补充一个题目小技巧,最好写一个函数,做一步就输出一个整个链表的结果,方便检测自己是否有哪个功能的代码有问题。下面的代码里面有一个函数叫void QueryAll()的作用就是直接输出此时的单链表的所有的数据,在考试的时候有一个技巧就是每次操作就输出一个当前链表的所有的数据,一旦出现错误也很方便发现问题然后修改。

#include <iostream>
#include <cstdio>
using namespace std;

namespace LIST
{
    // attention! this linkedlist is 0-based!
    // attention! this linkedlist has head node!
    // attention! head node doesn't contain any data for the convenience of data addition!

    struct NODE {
        // TODO
        int data;
        NODE *next;
    };

    NODE *head = nullptr;
    int len = 0;

    void init() 
    {
        // TODO
        head = new NODE;
        head->data = -1;
        head->next = head;
    }

    // this function is used for the movement of pointer!
    // attention!the pointer returned will be the address of NODE(i-1) instead of NODE-i!
    // num of NODE is also 0-based!
    // if i==0, then the pointer returned will be head!
    NODE* move(int i) 
    {
        // TODO
        NODE *p = head;
        for (int j = 0; j < i; j++)
        {
            p = p->next;
        }
        return p;
        // TODO END
    }
    void insert(int i, int x) 
    {
        // TODO
        NODE *a = move(i);
        NODE *b = new NODE;
        b->data = x;
        b->next = a->next;
        a->next = b;
        len++;
        // TODO END
    }
    void remove(int i) 
    {
        // TODO
        NODE *a = move(i);
        NODE *b;
        b = a->next;
        a->next = b->next;
        delete b;
        len--;
        // TODO END
    }
    void remove_insert(int i) 
    {
        //TODO
        NODE *p = move(i);
        int x = p->next->data;
        remove(i);
        insert(len, x);
        // TODO END
    }
    void get_length() 
    {
        // TODO
        cout << len << endl;
        // TODO END
    }
    void query(int i) 
    {
        // TODO
        if (i < 0 || i > len - 1)
            cout << -1 << endl;
        else
        {
            NODE *p = move(i);
            cout << p->next->data << endl;
        }
        // TODO END
    }
    void get_max() 
    {
        // TODO
        NODE *p = head;
        if (len > 0)
        {
            int maxnum = p->data;
            while (p->next != head)
            {
                p = p->next;
                maxnum = max(maxnum, p->data);
            }
            cout << maxnum << endl;
        }
        else
            cout << -1 << endl;
        // TODO END
    }
    void clear() 
    {
        // TODO
        NODE *q = head;
        NODE *p;
        while (q->next != head)
        {
            p = q->next;
            delete q;
            q = p;
        }
        delete q;
        // TODO END
    } 
    // void QueryAll()
    // {
    //     cout << "ALL:" <<endl;
    //     NODE *p = head;
    //     for (int i = 0; i < len; i++)
    //     {
    //         p = p->next;
    //         cout << p->data << "\t";
    //     }
    //     cout << endl;
    // }
}
int n;
int main()
{
    cin >> n;
    int op, x, p;
    LIST::init();
    for (int _ = 0; _ < n; ++_)
    {
        cin >> op;
        switch(op) {
            case 0:
                LIST::get_length();
                // LIST::QueryAll();
                break;
            case 1:
                cin >> p >> x;
                LIST::insert(p,x);
                // LIST::QueryAll();
                break;
            case 2:
                cin >> p;
                LIST::query(p);
                // LIST::QueryAll();
                break;
            case 3:
                cin >> p;
                LIST::remove(p);
                // LIST::QueryAll();
                break;
            case 4:
                cin >> p;
                LIST::remove_insert(p);
                // LIST::QueryAll();
                break;
            case 5:
                LIST::get_max();
                // LIST::QueryAll();
                break;
        }
    }
    LIST::clear();
    return 0;
}
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值