不同集合含有相同元素时不能使用链表

本文探讨了在处理具有重叠元素的集合时,使用链表和顺序表的不同效果。通过具体实例,展示了使用链表可能导致的错误及死循环问题,并提出使用顺序表作为解决方案。

如果集合有重叠元素,比如总人口集合、男性集合、女性集合,那么对这三个集合的表示,要谨慎使用链表。如下图:

注意,上面图中,使用的是AddNode),而非使用AddValue),两者有重大区别,前者,三条链表类共同修改一个链表,内存占用少,但是集合一旦有重叠就会引起错乱,后者每AddValue)一次,就会重新new一个新的结点(查看Add的方法就知道了,里面有一个new Node()的动作),于是就会形成三条独立的链表,互不相干,也不会出现上述错误,但是同一个对象就拥有了多个克隆体,多占用了不少内存,而且,如果对其中一个链表的某个node中的value进行修改的话,就必须需要考虑同时修改其他链表中的值相同的value对象,C#双链表提供了Findvalue)函数,用于在链表中找到引用value(非值相等,而是引用相等)的Node,然后使用Removenode)将该结点删除。由于每个value可能有多个克隆,为了保持一致性,修改某个集合中的某一value的属性时,还必须要修改其他集合中的相同值的value的属性。举个例子,比如要在三个集合中添加人,第一个是男的,第二个男的,第三个是女的,第四个是男的,分别输出三个集合的人数,然后把第一个人删除,再输出三个集合的人数,程序如下:

LinkedList<Individual> listAll = new LinkedList<Individual>(); LinkedList<Individual> listMale = new LinkedList<Individual>(); LinkedList<Individual> listFemale = new LinkedList<Individual>(); Individual indi1 = new Individual();//Individual是个人类 indi1.Gender = GenderType.M;//男 Individual indi2 = new Individual(); indi2.Gender = GenderType.M; Individual indi3 = new Individual(); indi3.Gender = GenderType.F;//女 Individual indi4 = new Individual(); indi4.Gender = GenderType.M; //根据性别向集合中添加 listAll.AddLast(indi1); listMale.AddLast(indi1); listAll.AddLast(indi2); listMale.AddLast(indi2); listAll.AddLast(indi3); listFemale.AddLast(indi3); listAll.AddLast(indi4); listMale.AddLast(indi4); //分别输出总人数,男性数,女性数,结果为4,3,1 Console.WriteLine("{0}, {1}, {2}", listAll.Count, listMale.Count, listFemale.Count); Console.ReadLine(); //删除集合中的一个男人indi1,这需要即删除listMale和listAll中的两个相同对象 LinkedListNode<Individual> n = listMale.Find(indi1);//首先查到到在listMale中的引用indi1的node(不是对值的查找,而是对引用的查找) listAll.Remove(indi3);//Remove(value) listMale.Remove(n);//Remove(node) //分别输出删除indi1后的总人数,男性数,女性数,结果为3,2,1 Console.WriteLine("{0}, {1}, {2}", listAll.Count, listMale.Count, listFemale.Count); Console.ReadLine();

改进方法,使用三个顺序表来完成,这样对各自顺序表增加要素或者删除要素都不影响其他顺序表,因为顺序表基于数组,对于任何一个value,都有对应的若干引用,value没有链表那种链式结构,它们是靠数组的Index来获得的。更重更要的是,内存中,任何一个value只需要一个拷贝,而不需要多个拷贝,这样就节省了内存,而且对value的修改更加方便,只需要修改一次即可。

我们假设使用的是Addnode)方法添加链表,而非使用Addvalue)方法。第一步,往All链表中添加一个男性,同时Male链表中也要加入该男性,第二步,往All链表中添加一个男性,同时Male链表也要加入该男性(重复了),第三步,往All链表加入一个女性,同时Female链表也加入该女性要素,此时,加入的女性成员同时也加入了Male链表中,造成错误。根本原因在于,链表的前后引用关系,三个链表类对其有三个引用,但是操作的确是统一链表。当集合重叠的时候,一定出错。但是要注意的是,这种“同一个结点加入不同链表”的添加方法,只有在自定义的链表泛型类中才能使用,C#LinkedList<>是禁止向链表中加入一个已经属于其他链表的结点的,下面这段程序因为违法了这一规定而报错:

LinkedList<Individual> listAll = new LinkedList<Individual>(); LinkedList<Individual> listMale = new LinkedList<Individual>(); LinkedList<Individual> listFemale = new LinkedList<Individual>(); Individual indi1 = new Individual();//Individual是个人类 indi1.Gender = GenderType.M;//男 LinkedListNode<Individual> node1 = new LinkedListNode<Individual>(indi1); Individual indi2 = new Individual(); indi2.Gender = GenderType.M; LinkedListNode<Individual> node2 = new LinkedListNode<Individual>(indi2); Individual indi3 = new Individual(); indi3.Gender = GenderType.F;//女 LinkedListNode<Individual> node3 = new LinkedListNode<Individual>(indi3); Individual indi4 = new Individual(); indi4.Gender = GenderType.M; LinkedListNode<Individual> node4 = new LinkedListNode<Individual>(indi4); //根据性别向集合中添加 listAll.AddLast(node1); listMale.AddLast(node1);//error listAll.AddLast(node2); listMale.AddLast(node2); listAll.AddLast(node3); listFemale.AddLast(node3); listAll.AddLast(node4); listMale.AddLast(node4);

若使用自定义自定义链表类(可以加入属于另外一个链表的结点),按上面顺序加入这几个人会有什么后果呢?后果是,第一步,indi1加入了All链表和Male链表;第二步,在All链表尾部加入valueindi2的结点(此时indi2成为链表All和链表Male的尾结点),然后在Male链表尾部(尾部结点的value已经是indi2)再次加入这一结点(因为这个人是男的),这是就会产生这样一个后果:node2next属性等于他自己,也就是形成了死循环,此时计算AllMale链表的长度,则陷入死循环,无法得出结果。程序如下:

SingleLinkedList<Individual> listAll = new SingleLinkedList<Individual>(); SingleLinkedList<Individual> listMale = new SingleLinkedList<Individual>(); SingleLinkedList<Individual> listFemale = new SingleLinkedList<Individual>(); Individual indi1 = new Individual();//Individual是个人类 indi1.Gender = GenderType.M;//男 Node<Individual> node1 = new Node<Individual>(indi1); Individual indi2 = new Individual(); indi2.Gender = GenderType.M; Node<Individual> node2 = new Node<Individual>(indi2); Individual indi3 = new Individual(); indi3.Gender = GenderType.F;//女 Node<Individual> node3 = new Node<Individual>(indi3); Individual indi4 = new Individual(); indi4.Gender = GenderType.M; Node<Individual> node4 = new Node<Individual>(indi4); //根据性别向集合中添加 listAll.Add(node1); listMale.Add(node1); listAll.Add(node2); listMale.Add(node2);//这一步导致node2.Next = node2;死循环 listAll.Add(node3); listFemale.Add(node3); listAll.Add(node4); listMale.Add(node4); //分别输出总人数,男性数,女性数,无法输出 Console.WriteLine("{0}, {1}, {2}", listAll.GetLength(), listMale.GetLength(), listFemale.GetLength()); Console.ReadLine();

示意图为:

改进方法,使用三个顺序表来完成,这样对各自顺序表增加要素或者删除要素都不影响其他顺序表,因为顺序表基于数组,对于任何一个value,都有对应的若干引用,value没有链表那种链式结构,它们是靠数组的Index来获得的。更重更要的是,内存中,任何一个value只需要一个拷贝,而不需要多个拷贝,这样就节省了内存,而且对value的修改更加方便,只需要修改一次即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值