- 问题引入
- 单向循环链表节点的定义:
1 public class CirNode<T> 2 { 3 public T Item { get; set; } 4 public CirNode<T> Next { get; set; } 5 6 public CirNode() 7 { 8 } 9 10 public CirNode(T item) 11 { 12 this.Item = item; 13 } 14 }View Code
2.单向循环链表的模拟实现:
1 /// <summary> 2 /// 单向循环链表的模拟实现 3 /// </summary> 4 public class MyCircularLinkedList<T> 5 { 6 private int count; // 字段:记录数据元素个数 7 private CirNode<T> tail; // 字段:记录尾节点的指针 8 private CirNode<T> currentPrev; // 字段:使用前驱节点标识当前节点 9 10 // 属性:指示链表中元素的个数 11 public int Count 12 { 13 get 14 { 15 return this.count; 16 } 17 } 18 19 // 属性:指示当前节点中的元素值 20 public T CurrentItem 21 { 22 get 23 { 24 return this.currentPrev.Next.Item; 25 } 26 } 27 28 public MyCircularLinkedList() 29 { 30 this.count = 0; 31 this.tail = null; 32 } 33 34 public bool IsEmpty() 35 { 36 return this.tail == null; 37 } 38 39 // Method01:根据索引获取节点 40 private CirNode<T> GetNodeByIndex(int index) 41 { 42 if (index < 0 || index >= this.count) 43 { 44 throw new ArgumentOutOfRangeException("index", "索引超出范围"); 45 } 46 47 CirNode<T> tempNode = this.tail.Next; 48 for (int i = 0; i < index; i++) 49 { 50 tempNode = tempNode.Next; 51 } 52 53 return tempNode; 54 } 55 56 // Method02:在尾节点后插入新节点 57 public void Add(T value) 58 { 59 CirNode<T> newNode = new CirNode<T>(value); 60 if (this.tail == null) 61 { 62 // 如果链表当前为空则新元素既是尾头结点也是头结点 63 this.tail = newNode; 64 this.tail.Next = newNode; 65 this.currentPrev = newNode; 66 } 67 else 68 { 69 // 插入到链表末尾处 70 newNode.Next = this.tail.Next; 71 this.tail.Next = newNode; 72 // 改变当前节点 73 if (this.currentPrev == this.tail) 74 { 75 this.currentPrev = newNode; 76 } 77 // 重新指向新的尾节点 78 this.tail = newNode; 79 } 80 Console.WriteLine(this.currentPrev.Next.Item); 81 this.count++; 82 } 83 84 // Method03:移除当前所在节点 85 public void Remove() 86 { 87 if (this.tail == null) 88 { 89 throw new NullReferenceException("链表中没有任何元素"); 90 } 91 else if (this.count == 1) 92 { 93 // 只有一个元素时将两个指针置为空 94 this.tail = null; 95 this.currentPrev = null; 96 } 97 else 98 { 99 if (this.currentPrev.Next == this.tail) 100 { 101 // 当删除的是尾指针所指向的节点时 102 this.tail = this.currentPrev; 103 } 104 // 移除当前节点 105 this.currentPrev.Next = this.currentPrev.Next.Next; 106 } 107 108 this.count--; 109 } 110 111 // Method04:获取所有节点信息 112 public string GetAllNodes() 113 { 114 if (this.count == 0) 115 { 116 throw new NullReferenceException("链表中没有任何元素"); 117 } 118 else 119 { 120 CirNode<T> tempNode = this.tail.Next; 121 string result = string.Empty; 122 for (int i = 0; i < this.count; i++) 123 { 124 result += tempNode.Item + " "; 125 tempNode = tempNode.Next; 126 } 127 128 return result; 129 } 130 } 131 }View Code
其中循环链表新节点的插入实现方法中,用了对象赋值给另一个对象:
1 public void Add(T value) 2 { 3 CirNode<T> newNode = new CirNode<T>(value); 4 if (this.tail == null) 5 { 6 // 如果链表当前为空则新元素既是尾头结点也是头结点 7 this.tail = newNode;//@1 8 this.tail.Next = newNode;//@2 9 this.currentPrev = newNode;//@3 10 } 11 else 12 { 13 // 插入到链表末尾处 14 newNode.Next = this.tail.Next; 15 this.tail.Next = newNode; 16 // 改变当前节点 17 if (this.currentPrev == this.tail) 18 { 19 this.currentPrev = newNode; 20 } 21 // 重新指向新的尾节点 22 this.tail = newNode; 23 } 24 Console.WriteLine(this.currentPrev.Next.Item); 25 this.count++; 26 }
- 问题原因
其中@1处,把新new的一个对象newNode赋值给this.tail,此时这两个对象引用的是同一个内存地址空间,newNode.Next=null,this.tail.Next也是为Null,而到@2这一步时,给this.tail.Next赋值为newNode时,同时也会对newNode进行操作既给newNode.Next赋值,因为这两个对象引用的是同一个地址空间。
- 问题引申
浅复制和深复制:
浅复制:
1 public class Test 2 { 3 public string A { get; set; } 4 public int B { get; set; } 5 public override string ToString() 6 { 7 return $"A:{A},B:{B}"; 8 } 9 } 10 static void Main(string[] args) 11 { 12 var list = new List<Test>() 13 { 14 new Test(){A="A1",B=1 }, 15 new Test(){ A="A2",B=2} 16 }; 17 var list_Copy = list.ToList(); 18 list_Copy[0].A = "这是修改后的值"; 19 foreach (var i in list) 20 { 21 Console.WriteLine(i); 22 } 23 Console.Read(); 24 }
执行结果:
由上图可以看出,这里与循环链表里面的插入方法中的@1是一样的,修改list_Copy的元素的属性值,list中的元素的属性也跟着变了。
深复制:
1 public class Test:ICloneable 2 { 3 public string A { get; set; } 4 public int B { get; set; } 5 6 public object Clone() 7 { 8 return this.MemberwiseClone(); 9 } 10 11 public override string ToString() 12 { 13 return $"A:{A},B:{B}"; 14 } 15 } 16 static void Main(string[] args) 17 { 18 var list = new List<Test>() 19 { 20 new Test(){A="A1",B=1 }, 21 new Test(){ A="A2",B=2} 22 }; 23 var list_Copy = new List<Test>(); 24 list.ForEach(x => list_Copy.Add(x.Clone() as Test)); 25 list_Copy[0].A = "这是修改后的值"; 26 foreach (var i in list) 27 { 28 Console.WriteLine(i); 29 } 30 Console.Read(); 31 }
运行结果:
可以看出,修改list_Copy的任何元素任何属性,都不会影响到list,但是list与list_Copy中的元素值是相同的,这就是深复制。
深复制还可以借用json:
1 public static void Copy<T>(T source, ref T destination) where T : class 2 { 3 string jsonStr = Newtonsoft.Json.JsonConvert.SerializeObject(source); 4 destination = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(jsonStr); 5 }
参考文章链接:
- https://www.cnblogs.com/edisonchou/p/4614934.html
- https://blog.csdn.net/lishuangquan1987/article/details/86085971