L2-008 链表去重(两种方法)

本文介绍了一种针对带有整数键值的单链表进行去重处理的算法实现,重点在于如何高效地删除键值及其绝对值重复的节点,并分别输出去重后的链表与被删除节点组成的链表。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链接 :https://www.patest.cn/contests/gplt/L2-002

题目内容:

给定一个带整数键值的单链表L,本题要求你编写程序,删除那些键值的绝对值有重复的结点。即对任意键值K,只有键值或其绝对值等于K的第一个结点可以被保留。同时,所有被删除的结点必须被保存在另外一个链表中。例如:另L为21→-15→-15→-7→15,则你必须输出去重后的链表21→-15→-7、以及被删除的链表-15→15。

输入格式:

输入第一行包含链表第一个结点的地址、以及结点个数N(<= 105 的正整数)。结点地址是一个非负的5位整数,NULL指针用-1表示。

随后N行,每行按下列格式给出一个结点的信息:

Address Key Next

其中Address是结点的地址,Key是绝对值不超过104的整数,Next是下一个结点的地址。

输出格式:

首先输出去重后的链表,然后输出被删除结点组成的链表。每个结点占一行,按输入的格式输出。

输入样例:
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

用数组下标存放 结点的id,可以迅速访问到该结点。因为题目的结点id在100000之内,所以开数组这个方法还是很有效的
*方法一 2017-3-23 9:59
#include<iostream>
#include<cstdio>
#include<set>
using namespace std;
struct Node
{
	int key;
	int next;
	Node ()
	{
		key=-1;
		next=-1;
	}
}a[110001];//开一个数组存放结点,题目要求的id在0~99999之间,所以这个数组的大小110001足够放了
int node1[110001],node2[110002];//node1存放主链表中结点的id,node2存放删除的结点id 
int main()
{
	set<int>v;//存放主链表中的key值,用于检测key是否重复 
	int begin,n;//头结点的Id,以及结点总个数 
	int pos1=0,pos2=0; //记录主链表和被删除结点链表的大小 
	scanf("%d %d",&begin,&n);
	int i;
	int tmp;
	for(i=0;i<n;i++)
	{
		scanf("%d",&tmp); //得到id 
		scanf("%d %d",&a[tmp].key,&a[tmp].next); //将id作为数组下标,存放key和next 
	}
	//下标及是结点的id 
	tmp=a[begin].key;//第一个结点的key
	v.insert(tmp);  //集合中添加key 
	v.insert(-tmp); //再添加-key 
	node1[pos1++]=begin; //主链表添加一个结点 
	int next=a[begin].next; //下一个结点的id 
	while(next!=-1)
	{
		tmp=a[next].key;//得到它的key 
		if(v.find(tmp)==v.end())//主链表中没有这个key,可以添加 
		{ 
			v.insert(tmp); //插入key 和-key 
			v.insert(-tmp);
			node1[pos1++]=next;  //该结点可以进入主链表 
			next=a[next].next; //下一个结点的id  
		}else{    //链表中没有这个key,或者-key,主链表不能添加 
			node2[pos2++]=next;  //把结点放进node2[] 中 
			next=a[next].next;
		}
	}
	//按题目指定格式输出 
	printf("%05d %d",node1[0],a[node1[0]].key);
	for(i=1;i<pos1;i++)
	{
		printf(" %05d\n%05d %d",node1[i],node1[i],a[node1[i]].key);
	}
	printf(" -1\n");
	if(pos2>0)
	{
		printf("%05d %d",node2[0],a[node2[0]].key);
		for(i=1;i<pos2;i++)
		{
			printf(" %05d\n%05d %d",node2[i],node2[i],a[node2[i]].key);
		}
		printf(" -1\n");	
	}
	return 0;
}
  

//
一开始的写法: 2017-03-19 20:08
//
方法二: 运行时间太长,占内存太大,不过还是贴在这里,毕竟也是劳动成果,爱自己的代码
这道题做了很久,虽然最后通过了,但觉得方法很笨,不过过程中还是有几点细节值得注意
1.在map中用char *作为key,在查找find()的过程中,虽然存在,但一直找不到值,最后百度后说用自定义结构体cmp可以解决,加在map的定义map<char*,Node,cmp>中才解决
2.用头文件<cstdio>,用printf(),scanf(),在数据多的时候可以省下不少时间,一开始用cin,cout超时
3.对map和stl 中的类有点生疏,要重新回去翻一翻。
#include<iostream>
#include<cstdio>
#include<map>
#include<set>
#include<string.h>
using namespace std;
struct Node
{
	char id[6];
	int key;
	char next[6];
}f[100001],list1[100001],list2[100001];
struct cmp
{
	bool operator ()(char *a,char *b)// 一开始忘记加bool,编译出错
	{
		return strcmp(a,b)<0;	
	}	
};
map<char *,Node,cmp>m;
map<char *,Node,cmp>::iterator it;
set<int>s;
set<int>::iterator itt;
int main()
{
	char a[6];
	cin>>a;
	int n;
	cin>>n;
	int i;
	for(i=0;i<n;i++)
	{
		scanf("%s %d %s",f[i].id,&f[i].key,f[i].next);
		m.insert(pair<char*,Node>(f[i].id,f[i])); 
	}
	it=m.find(a);
	int j=0,k=0;
	while(1)
	{
		if(s.find(it->second.key)==s.end())
		{
			s.insert(it->second.key);
			s.insert(-(it->second.key));
			list1[j]=(it->second);
			j++;
		}else{
			list2[k]=(it->second);
			k++;
		}
		if(strcmp(it->second.next,"-1")==0)
		{
			break;
		}
		it=m.find(it->second.next);
	}
	if(j>1)
	{
		printf("%s %d ",list1[0].id,list1[0].key);
		for(i=1;i<j-1;i++)
		{
			printf("%s\n%s %d ",list1[i].id,list1[i].id,list1[i].key);
		}
		printf("%s\n%s %d -1\n",list1[i].id,list1[i].id,list1[i].key);
	}else if(j==1){
		printf("%s %d -1\n",list1[0].id,list1[0].key);
	}
	if(k>1)
	{
		printf("%s %d ",list2[0].id,list2[0].key);
		for(i=1;i<k-1;i++)
		{
			printf("%s\n%s %d ",list2[i].id,list2[i].id,list2[i].key);
		}
		printf("%s\n%s %d -1\n",list2[i].id,list2[i].id,list2[i].key);
	}else if(k==1){
		printf("%s %d -1\n",list2[0].id,list2[0].key);
	}
	return 0;
}
下标也可以作为某种信息的存储,在有些时候可以节省内存,也可以节省搜索时间。
加油,渣科






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值