7-1 链表去重 (25 分)
给定一个带整数键值的链表 L,你需要把其中绝对值重复的键值结点删掉。即对每个键值 K,只有第一个绝对值等于 K 的结点被保留。同时,所有被删除的结点须被保存在另一个链表上。例如给定 L 为 21→-15→-15→-7→15,你需要输出去重后的链表 21→-15→-7,还有被删除的链表 -15→15。
输入格式:
输入在第一行给出 L 的第一个结点的地址和一个正整数 N(≤100000 ,为结点总数)。一个结点的地址是非负的 5 位整数,空地址 NULL 用 -1 来表示。
随后 N 行,每行按以下格式描述一个结点:
地址 键值 下一个结点
其中地址是该结点的地址,键值是绝对值不超过10000 的整数,下一个结点是下个结点的地址。
输出格式:
首先输出去重后的链表,然后输出被删除的链表。每个结点占一行,按输入的格式输出。
输入样例:
00100 5
99999 -7 87654
23854 -15 00000
87654 15 -1
00000 -15 99999
00100 21 23854
输出样例:
00100 21 23854
23854 -15 99999
99999 -7 -1
00000 -15 87654
87654 15 -1
我的思路是先用结构体a[ i ] 的下标来存放当前结点的键值Key和下一个结点的地址Next,接着定义一个flag数组来判断每个键值Key的绝对值是否重复,不是的话,就按格式输出当前结点,是的话,在实现定义好的数组b中,存放该已判断为重复的接结点的地址。
#include<bits/stdc++.h>
using namespace std;
const int max = 100100;
struct Node
{
int Key;
int Next;
}a[max];
int b[max]//b用来保存重复的结点
int main()
{
int flag[10005];//标记数组
memset(flag,0,sizeof(flag));//清0
int first ,N ,value, m ,add;
cin>>first>>N;
for(int i=0;i<N;i++)
{
cin>>add;
cin>>a[add].Key>>a[add].Next;
}
m=first;
value=abs( a[m].Key ) ;//判断绝对值
printf("%05d %d",first,a[m].Key);
flag[value]=1;//标记
int count=0;
while(1)
{
m=a[m].Next;
if(m==-1)//直到找到为-1的next域表示这个去重后的链表已经完成,结束死循环
{
cout<<" -1"<<endl;
break;
}
value=abs(a[m].Key);
if(flag[value]==0)
{//如果没有被标记过就输出
printf(" %05d\n%05d %d",m,m,a[m].Key);
flag[value]=1;
}
else
{//被标记过,则将此数暂时保存到一个新数组
b[count]=m;
count++;
}
}
if(count>0)
{//表示有重复的值
printf("%05d %d",b[0],a[b[0]].Key);//输出重复的链表
for(int i=1;i<count;i++)
{
printf(" %05d\n%05d %d",b[i],b[i],a[b[i]].Key);
}
cout<<" -1"<<endl;
}
return 0;
}
测试点 结果 耗时 内存
0 答案正确 2 ms 888KB
1 答案正确 2 ms 896KB
2 答案正确 2 ms 788KB
3 答案正确 139 ms 3588KB
4 答案正确 140 ms 3608KB
虽然是全AC了,但是时间有点久哈,就去网上参考大神们的代码,果然是高人一等,时间是之前的一半哈。。可见差距了!
测试点 结果 耗时 内存
0 答案正确 6 ms 1792KB
1 答案正确 5 ms 1792KB
2 答案正确 6 ms 1792KB
3 答案正确 53 ms 3584KB
4 答案正确 67 ms 3456KB
大神的思路应该如下:
用结构体数组存储这个链表,大小为maxn = 100000,
node[i]表示地址为i的结点。在结构体中定义一个num变量,
将num变量先初始化为2 * maxn。通过改变num变量的值
最后sort排序来改变链表的顺序。将没有删除的结点的num
标记为count1,count1为当前没有删除的结点的个数;
将需要删除的结点的num标记为maxn + count2,count2表示当前删除了
的结点的个数,因为一开始初始化为了2 * maxn,所以我们可以
通过对num排序达到:num = 0~maxn为不删除结点,
num = maxn~2maxn为删除结点,num = 2maxn为无效结点
这样sort后就会按照需要输出的顺序将结点排序,
我们只需要输出前count1+count2个结点即可
#include<stdio.h>
#include <cstdio>
#include <stdlib.h>
#include <algorithm>
using namespace std;
const int maxn = 100000;
struct NODE
{
int address;
int key;
int next;
int num;
}node[maxn];
bool exist[maxn];//判断绝对值是否重复
int cmp1(NODE a, NODE b)
{
return a.num < b.num;
}
int main()
{
int begin, n, count1 = 0, count2 = 0, add;
scanf("%d%d", &begin, &n);
for(int i = 0; i < maxn; i++)
{
node[i].num = 2 * maxn;
}
for(int i = 0; i < n; i++)
{
scanf("%d", &add);
scanf("%d%d", &node[add].key, &node[add].next);
node[add].address = add;
}
for(int i = begin; i != -1; i = node[i].next)
{
if(exist[abs(node[i].key)] == false) //如果未重复
{
exist[abs(node[i].key)] = true;
node[i].num = count1;
count1++;
}
else
{
node[i].num = maxn + count2;
count2++;
}
}
sort(node, node + maxn, cmp1);
int count = count1 + count2;
for(int i = 0; i < count; i++)
{
if(i != count1 - 1 && i != count - 1)
{
printf("%05d %d %05d\n", node[i].address, node[i].key, node[i+1].address);
}
else
{
printf("%05d %d -1\n", node[i].address, node[i].key);
}
}
return 0;
}