C#单链表(数据结构)
学习单链表的源码项目:https://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; }