7-1 链表去重

13 篇文章 1 订阅
13 篇文章 0 订阅

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;
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值