您的位置:首页 > 编程语言 > C#

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

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



 

 
 

 

 

 

    注意,上面图中,使用的是Add(Node),而非使用Add(Value),两者有重大区别,前者,三条链表类共同修改一个链表,内存占用少,但是集合一旦有重叠就会引起错乱,后者每Add(Value)一次,就会重新new一个新的结点(查看Add的方法就知道了,里面有一个new Node()的动作),于是就会形成三条独立的链表,互不相干,也不会出现上述错误,但是同一个对象就拥有了多个克隆体,多占用了不少内存,而且,如果对其中一个链表的某个node中的value进行修改的话,就必须需要考虑同时修改其他链表中的值相同的value对象,C#双链表提供了Find(value)函数,用于在链表中找到引用value(非值相等,而是引用相等)的Node,然后使用Remove(node)将该结点删除。由于每个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的修改更加方便,只需要修改一次即可。

 

 

我们假设使用的是Add(node)方法添加链表,而非使用Add(value)方法。第一步,往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链表尾部加入value是indi2的结点(此时indi2成为链表All和链表Male的尾结点),然后在Male链表尾部(尾部结点的value已经是indi2)再次加入这一结点(因为这个人是男的),这是就会产生这样一个后果:node2的next属性等于他自己,也就是形成了死循环,此时计算All和Male链表的长度,则陷入死循环,无法得出结果。程序如下:
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的修改更加方便,只需要修改一次即可。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c#
相关文章推荐