C#单链表
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#单链表相关的知识,希望对你有一定的参考价值。
顺序表是用地址连续的存储单元顺序存储线性表中的各个数据元素, 逻辑上相邻的数据元素在物理位置上也相邻。因此,在顺序表中查找任何一个位置上的数据元素非常方便, 这是顺序存储的优点。 但是, 在对顺序表进行插入和删除时,需要通过移动数据元素来实现,影响了运行效率。线性表的另外一种存储结构——链式存储(Linked Storage), 这样的线性表叫链表(Linked List)。 链表不要求逻辑上相邻的数据元素在物理存储位置上也相邻,因此,在对链表进行插入和删除时不需要移动数据元素,但同时也失去了顺序表可随机存储的优点。
把存储据元素本身信息的域叫结点的数据域(Data Domain), 把存储与它相邻的数据元素的存储地址信息的域叫结点的引用域(Reference Domain)。如果结点的引用域只存储该结点直接后继结点的存储地址, 则该链表叫单链表(Singly Linked List)。把该引用域叫 next。把单链表结点看作是一个类,类名为 Node<T>。
单链表结点类的实现如下:
//节点类 public class Node<T> { private T data;//存储数据域 private Node<T> next;//存储引用域 public Node(T val, Node<T> node) { data = val; next = node; } public Node(T val) { data = val; next = null; } public Node(Node<T> node) { next = node; } public Node() { data = default(T); next = null; } public T Data { get { return data; } set { data = value; } } public Node<T> Next { get { return next; } set { next = value; } } }
单链表的接口实现如下:
public interface IListDS<T>//单链表的接口 { int GetLength(); //求长度 void Clear(); //清空操作 bool IsEmpty(); void Append(T item); //附加操作 void Insert(T item, int i); //插入操作 T Delete(int i); //删除操作 T GetElem(int i); //取表元 int Locate(T value); //按值查找 void Reverse();//倒置 }
单链表如图所示:
由图可知,单链表由头引用 H 唯一确定。头引用指向单链表的第一个结点,也就是把单链表第一个结点的地址放在 H 中,所以,H 是一个 Node 类型的变量。头引用为 null 表示一个空表。把单链表看作是一个类,类名叫 LinkList<T>。LinkList<T>类也实现了接口IListDS<T>。LinkList<T>类有一个字段 head,表示单链表的头引用,所以 head的类型为 Node<T>。由于链表的存储空间不是连续的,所以没有最大空间的限制,在链表中插入结点时不需要判断链表是否已满。
单链表类 LinkList<T>的实现说明如下所示:
public class LinkList<T> : IListDS<T>//单链表类 { private Node<T> head; public Node<T> Head { get { return head; } set { head = value; } } public LinkList() { head = null; } public int GetLength() { int i = 0; while (head != null) { head = head.Next; ++i; } return i; } public void Clear() { head = null; } public bool IsEmpty() { return head == null; } 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(" List is empty or Position is error! "); 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 = 0; while (p.Next != null && j < i) { ++j; r = p; p = p.Next; } if (i == j) { Node<T> q = new Node<T>(item); q.Next = p; r.Next = q; } } //在单链表的第i个结点的位置后插入一个值为item的结点 public void InsertPost(T item, int i) { if (IsEmpty() || i < 1) { Console.WriteLine(" List is empty or Position is error! "); return; } Node<T> p = head; int j = 0; while (p.Next != null && j < i) { p = p.Next; ++j; } if (i == j) { Node<T> q = new Node<T>(item); q.Next = p.Next; p.Next = q; } } public T Delete(int i) { if (IsEmpty() || i < 1) { Console.WriteLine(" List is empty or Position is error! "); return default(T); } Node<T> p = head; Node<T> q = new Node<T>(); int j = 0; while (p.Next != null && j < i) { q = p; p = p.Next; ++j; } if (i == j) { q.Next = p.Next; return p.Data; } else { Console.WriteLine("The ith node is not exist!"); return default(T); } } public T GetElem(int i) { if (IsEmpty() || i < 1) { Console.WriteLine(" List is empty or Position is error! "); return default(T); } Node<T> p = head; int j = 0; while (p.Next != null && j < i) { p = p.Next; ++j; } if (i == j) { return p.Data; } else { Console.WriteLine("The ith node is not exist!"); return default(T); } } public int Locate(T value) { if (IsEmpty()) { Console.WriteLine("List is Empty!"); return -1; } Node<T> p = head; int i = 0; while (!p.Data.Equals(value) && p.Next != null) { p = p.Next; ++i; } return i; } public void Reverse() { Node<T> p = head.Next; Node<T> q = new Node<T>(); head.Next = null; while (p != null) { q = p; p = p.Next; q.Next = head.Next; head.Next = q; } } //单链表的建立 LinkList<int> CreateListHead()//头部插入结点建立单链表 { LinkList<int> L = new LinkList<int>(); int d = Int32.Parse(Console.ReadLine()); while (d != -1) { Node<int> p = new Node<int>(d); p.Next = L.Head; L.Head = p; d = Int32.Parse(Console.ReadLine()); } return L; } LinkList<int> CreateListTail()//在尾部插入结点建立单链表 { LinkList<int> L = new LinkList<int>(); Node<int> R = L.Head; int d = Int32.Parse(Console.ReadLine()); while (d != -1) { Node<int> p = new Node<int>(d); if (L.Head == null) { L.Head = p; } else { R.Next = p; } R = p; d = Int32.Parse(Console.ReadLine()); } if (R != null) { R.Next = null; } return L; } }
但是有一个问题:前面介绍的单链表允许从一个结点直接访问它的后继结点,所以, 找直接后继结点的时间复杂度是 O(1)。但是,要找某个结点的直接前驱结点,只能从表的头引用开始遍历各结点。如果某个结点的 Next 等于该结点,那么,这个结点就是该结点的直接前驱结点。也就是说,找直接前驱结点的时间复杂度是 O(n),n是单链表的长度。当然,我们也可以在结点的引用域中保存直接前驱结点的地址而不是直接后继结点的地址。这样,找直接前驱结点的时间复杂度只有 O(1),但找直接后继结点的时间复杂度是 O(n)。如果希望找直接前驱结点和直接后继结点的时间复杂度都是 O(1),那么,需要在结点中设两个引用域,一个保存直接前驱结点的地址,叫 prev,一个直接后继结点的地址,叫 next,这样的链表就是双向链表(Doubly Linked List)。此引出了双链表。
以上是关于C#单链表的主要内容,如果未能解决你的问题,请参考以下文章