助教是个小恶魔,她不知道要出什么机考题,然后她发现大家已经学完了指针和结构体,欣喜若狂。她想让你写一个单循环链表,即最后一个结点的下一个结点是头结点的单链表。你需要实现以下六种操作,分别标号为操作0~5:(注意这里的 i i i都为0-base)
注意:为防止内存泄漏,你需要在程序最后删除整个列表。
#include
#include
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 n≤1000
对于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
#include
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:" <
// 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;
}