學習單鏈表的源碼項目:http://files.cnblogs.com/xmfdsh/CSharp%E5%8D%95%E9%93%BE%E8%A1%A8.rar
鏈表是用一組任意的存儲單元來存儲線性表中的數據元素(在存儲單元中可以是連續的,也可以是不連續的)。鏈表在存儲數據元素時,除了存儲數據元素本身的信息外,還要存儲與它相鄰的數據元素的存儲地址信息。這兩部分信息組成該數據元素的存儲映像,稱為節點。
節點的形象圖如下:
首先定義一個類Node來表示這些節點:
public class Node<T> { private T data; //數據域 private Node<T> next; //引用域 #region 構造函數 /// <summary> /// 構造函數 /// </summary> /// <param name="val"></param> /// <param name="p"></param> public Node(T val, Node<T> p) { data = val; next = p; } public Node(Node<T> p) { next = p; } public Node(T val) { data = val; next = null; } public Node() { data = default(T); next = null; } #endregion //數據域屬性 public T Data { get { return data; } set { data = value; } } //引用域屬性 public Node<T> Next { get { return next; } set { next = value; } } }
通常,我們把鏈表化成用箭頭相連接的節點序列,節點間的箭頭表示引用域中存儲的地址。具體單鏈表的形式表示如下:
網上找到 圖片,把循環列表 和 雙向鏈表都弄上去了,也好提前了解下
因此要定義一個類表示整個鏈表,其中中間當然運用到了之前寫Node類,類中的屬性如下:
private Node<T> head;//單鏈表頭引用 //頭引用屬性 public Node<T> Head { get { return head; } set { head = value; } } //構造函數 public LinkList() { head = null; }
接下來就是實現之前講的順序表中定義好的接口 http://www.cnblogs.com/xmfdsh/p/3698456.html
//求單鏈表長度 public int GetLength() { Node<T> p = head; int len = 0; while(p!=null) { len++; p = p.Next; } return len; } //清空單鏈表 public void Clear() { head = null; } public bool IsEmpty() { if(head==null) { return true; } else { return false; } } public bool IsFull() { throw new NotImplementedException(); } //在單鏈表末尾添加新元素 public void Append(T item) { Node<T> q = new Node<T>(item); Node<T> p = new Node<T>(); if(head==null) { head = q; return; } p = head; while(p.Next!=null) { p = p.Next; } p.Next = q; } //在單鏈表第i個節點位置插入一個item的節點 public void Insert(T item, int i) { if(IsEmpty()||i<1) { Console.WriteLine("鏈表為空或插入位置不允許"); return; } if (i == 1) { Node<T> q = new Node<T>(item); q.Next = head; head = q; return; } Node<T> p = head; Node<T> r = new Node<T>(); int j = 1; while (p.Next != null && j < i) { r = p; p = p.Next; j++; } if (j == i) { Node<T> q = new Node<T>(item); q.Next = p; r.Next = q; } } //刪除單鏈表第i節點 public T Delete(int i) { if(IsEmpty()||i<1) { Console.WriteLine("鏈表為空或刪除位置不允許"); return default(T); } Node<T> q = new Node<T>(); if (i == 1) { q = head; head = head.Next; return q.Data; } Node<T> p = head; int j = 1; while (p.Next != null && j < i) { j++; q = p; p = p.Next; } if (j == i) { q.Next = p.Next; return p.Data; } else { Console.WriteLine("刪除的節點不存在"); return default(T); } } //獲取單鏈表第i個數據元素 public T GetElem(int i) { if (IsEmpty() || i < 1) { Console.WriteLine("鏈表為空或者獲取位置不允許"); return default(T); } Node<T> p = new Node<T>(); p = head; int j = 1; while (p.Next != null && j < i) { p = p.Next; j++; } if (j == i) { return p.Data; } else { Console.WriteLine("要獲取的節點不存在"); return default(T); } } //在單鏈表中查找值為value的節點 public int Locate(T value) { if (IsEmpty()) { Console.WriteLine("鏈表為空"); return -1; } Node<T> p = new Node<T>(); p = head; int i = 1; while (!p.Data.Equals(value) && p.Next != null) { p = p.Next; i++; } return i; }
實現這些接口都不難,還是要理解鏈表的具體操作原理,只是記代碼是沒用的,不用多久就忘的差不多,p.Next等這些地方可以聯想到C中的指針,不過C中的指針有時挺危險的,C#中的也許就安全多了吧。
然后再玩玩一些經典題目,首先就是單鏈表倒置
//-----------------------------------單鏈表擴展方法--------------------------------------------- /// <summary> /// 單鏈表倒置 /// </summary> /// <param name="H"></param> public void ReversLinkList(LinkList<T> H) { Node<T> p = H.head; Node<T> q = new Node<T>(); H.head.Next = null; while (p != null) { q = p; p = p.Next; q.Next = H.head.Next; H.head.Next = q; } }
再做個題目:構造單鏈表Hb,要求Hb只包含Ha表中所有值不相同節點
/// <summary> /// 構造單鏈表Hb,要求Hb只包含Ha表中所有值不相同節點 /// </summary> /// <param name="Ha"></param> /// <returns></returns> public LinkList<T> Purge(LinkList<T> Ha) { LinkList<T> Hb = new LinkList<T>(); Node<T> p = Ha.head; Node<T> q = new Node<T>(); Node<T> s = new Node<T>(); s = p; p = p.Next; s.Next = null; Hb.head.Next = s; while (p != null) { s = p; p = p.Next; q = Hb.head; while(q!=null&&!(q.Data.Equals(s.Data))) { q = q.Next; } if(q==null) { s.Next = Hb.head; Hb.head = s; } } return Hb; }