1.2 链表去重
给定一个带整数键值的链表 L,你需要把其中绝对值重复的键值结点删掉。即对每个键值 K,只有第一个绝对值等于 K 的结点被保留。同时,所有被删除的结点须被保存在另一个链表上。例如给定 L 为 21→-15→-15→-7→15,你需要输出去重后的链表 21→-15→-7,还有被删除的链表 -15→15。
输入格式:
输入在第一行给出 L 的第一个结点的地址和一个正整数 N(≤10 为结点总数)。一个结点的地址是非负的 5 位整数,空地址 NULL 用 -1 来表示。
随后 N 行,每行按以下格式描述一个结点:
地址 键值 下一个结点
其中地址是该结点的地址,键值是绝对值不超过10 的整数,下一个结点是下个结点的地址。
输出格式:
首先输出去重后的链表,然后输出被删除的链表。每个结点占一行,按输入的格式输出。
输入样例:
99999 -7 87654
23854 -15 00000
87654 15 -1
00000 -15 99999
00100 21 23854
输出样例:
23854 -15 99999
99999 -7 -1
00000 -15 87654
87654 15 -1
笔者提示:
核心思想是用数组模拟内存空间,“可视化”内存空间。
难点1:
题目给出的地址如何处理。
难点2:
将原链表去重容易,但是将去重后的新链表重新串起难,将删除的链表,以原顺序输出难
解决方案
num[k]:存储地址为k的结点的值
back[k]:存储地址为k的结点的后继地址
res1[0],res1[1],res1[2]....:新链表第一个结点的地址,新链表第二个结点的地址,新链表第三个结点的地址
Step1.遍历原链表,用isRepeat数组记录,结点元素的绝对值是否出现过,若未出现,赋值为1
Step2.赋值为1的同时,将第一次出现的绝对值,其原结点对应的地址存入res1数组(res1数组存储新链表的结点地址)。否则,将重复出现的绝对值,其原结点对应的地址存入res2数组。待遍历结束,链表结点的分组也完成
Step3.如何按顺序输出新链表?将res1数组的下标,天然提供了一个线性的顺序。
如:要输出res1[0]对应的结点A:A地址–res1[0],A值–num[res1[0]],A的下一结点地址:res[1]
利用数组自带的属性,就能巧妙解决问题
参考代码
#include<stdio.h>
#include<math.h>
const int N = 1e5 + 5;
int res1[N], res2[N]; //res1存储新序列结点地址,res2存储删除序列
int num[N], back[N]; //num存储值,back存储下一地址
bool isRepeat[N];//默认值为false
int main() {
int head, n, t;
scanf("%d%d", &head, &n);
for (int i = 0; i < n; i++) {
scanf("%d", &t);
scanf("%d%d", &num[t], &back[t]);
}
int i = head, l1 = 0, l2 = 0;
while (back[i] != -1) {
if (isRepeat[abs(num[i])] == false) { //若未出现过
isRepeat[abs(num[i])] = true;
res1[l1++] = i;
} else
res2[l2++] = i;
i = back[i]; //索引到下一地址
}
if (isRepeat[abs(num[i])] == false) { //处理最后一个
isRepeat[abs(num[i])] = true;
res1[l1++] = i;
} else
res2[l2++] = i;
//输出新序列
if (l1) { //若l1不为0
for (int i = 0; i < l1 - 1; i++) {
printf("%05d %d %05d\n", res1[i], num[res1[i]], res1[i + 1]);
}
printf("%05d %d -1\n", res1[l1 - 1], num[res1[l1 - 1]]);
}
//输出被删除序列
if (l2) { //若l2不为0
for (int i = 0; i < l2 - 1; i++) {
printf("%05d %d %05d\n", res2[i], num[res2[i]], res2[i + 1]);
}
printf("%05d %d -1", res2[l2 - 1], num[res2[l2 - 1]]);
}
return 0;
}
运行结果
在此感谢我滴舍友,jy同学~