什么是线性表?
零个或者多个数据元素的有限序列。线性表中的元素在位置上是有序的,前后两个元素存在一一对应关系
线性表的存储结构
顺序表
用一块地址连续的存储空间一次存储线性表中的数据元素,在顺序表中,逻辑上相邻的元素在物理地址上也是相邻的
顺序表之数组
数组是最基础的也是存取速度最快的一种集合类型,在.net中它是引用类型,即所需的内存空间将会在托管堆上进行分配,一旦数组被创建,其中的所有元素都会被初始化成它们的默认值
ps:当数组元素是值类型的时候,数组对象存放的是值类型对象本身,如果是引用类型的话,存放的是对象的指针
值类型 :
int[] arrInt = new int[5]; arrInt[2] = 5; arrInt[4] = 3;
所有元素都会被初始化为该类型的初始值
引用类型
Control[] arrCtrl = new Control[5]; arrCtrl[0] = new Button(); arrCtrl[3] = new Label();
所有元素都会被初始化为null,当元素被赋值的时候,则这个元素所在的内存区域会被存放一个指向实际对象存储区域的指针
数组的缺点:无法动态的改变集合的大小
动态顺序表之ArrayList与List<T>
ArrayList是动态数组,存储空间可以被动态的改变,同时具有添加、删除元素的功能
- 添加:在数组末尾顺序添加元素,首先检查长度是否溢出,如果溢出则调整空间大小,然后在末尾添加元素
- 删除:先判断被删除的索引是否溢出,然后把被删除元素后面的元素逐一向前移动一位(由此可见,如果删除的是最后一位,则耗时最少,如果删除的是头部元素,则整个数组都会被遍历一遍,可怕可怕)
基本上可以不讨论这个,因为ArrayList内的元素类型是Obj,进行存取的时候需要进行大量的装箱拆箱操作,降低程序性能。
同时ArrayList的扩容,实际上是一种类似于搬家的操作,内存空间开辟之后是无法更改大小的,所以ArrayList会在内存里面开辟一个空间为原来两倍大小的区域,将所有数据迁移过去,完成扩容。
List<T>
引入的泛型的概念,使得元素的存取不存在装箱拆箱操作,性能得以提升,其他的操作和ArrayList并无差别
可以看出,顺序表的缺点就是:必须事先占用一整块分配好的内存空间,在插入和删除的操作上需要移动大量的元素
链表
可以快速进行插入和删除操作的一种链表,有N个节点链接而成(一个指针域是单链表,两个指针域就是双链表)
单链表
每个元素有两个部分组成:数据域和指针域
第一个节点被称为头结点:可以存储线性表的长度等公共的数据
最后一个节点的指针域是null
单链表的实现
结点:
/// <summary> /// 单链表结点 /// </summary> /// <typeparam name="T"></typeparam> public class Node<T> { /// <summary> /// 数据域 /// </summary> public T Item { set; get; } /// <summary> /// 指针 /// </summary> public Node<T> Next { set; get; } public Node() { } public Node (T item) { Item = item; } }
单链表:
/// <summary> /// 手动实现单链表 /// </summary> /// <typeparam name="T"></typeparam> public class SingleLinkedList<T> { public SingleLinkedList () { count = 0; head = null; } /// <summary> /// 长度 /// </summary> private int count; /// <summary> /// 长度属性 /// </summary> public int Count { get { return this.count; } } /// <summary> /// 头结点 /// </summary> private Node<T> head; /// <summary> /// 索引 /// </summary> /// <param name="index"></param> /// <returns></returns> public T this[int index] { get { return GetNodeByIndex(index).Item; } set { } } /// <summary> /// 获取索引位置的元素 /// </summary> /// <param name="index"></param> /// <returns></returns> public Node<T> GetNodeByIndex (int index) { if (index < 0 || index >= count) throw new ArgumentOutOfRangeException( nameof(index), "超出索引长度"); Node<T> theNode = this.head; for (int i = 0; i < index; i++) { theNode = theNode.Next; } return theNode; } /// <summary> /// 在末尾添加一个元素 /// </summary> /// <param name="Item"></param> public void Add (T Item) { var newNode = new Node<T>(Item); if (this.head is null) { this.head = newNode; } else { var lastNode = this.GetNodeByIndex(this.count - 1); lastNode.Next = newNode; } this.count++; } /// <summary> /// 在指定位置添加元素 /// </summary> /// <param name="Item"></param> /// <param name="index"></param> public void InsertToIndex (T Item,int index) { Node<T> newNode = null; if (index < 0 || index >= count) throw new ArgumentOutOfRangeException( nameof(index), "超出索引长度"); else if (index == 0) { if (this.head is null) { newNode = new Node<T>(Item); this.head = newNode; } else { newNode = new Node<T>(Item); newNode.Next = this.head; this.head = newNode; } } else { newNode = new Node<T>(Item); Node<T> oldNode = this.GetNodeByIndex(index - 1); newNode.Next = oldNode.Next; oldNode.Next = newNode; } this.count++; } /// <summary> /// 移除指定位置的元素 /// </summary> /// <param name="index"></param> public void RemoveAtIndex (int index) { if (index < 0 || index >= count) throw new ArgumentOutOfRangeException( nameof(index), "超出索引长度"); else if (index == 0) { this.head = this.head.Next; } else { Node<T> beforeNode = this.GetNodeByIndex(index - 1);//前一个元素 Node<T> theNode = this.GetNodeByIndex(index);//当前元素 beforeNode.Next = theNode.Next; } this.count--; } }
运行试验一下:
static void Main (string[] args) { #region 单链表 SingleLinkedList<double> singleLinkedList = new SingleLinkedList<double>(); singleLinkedList.Add(1); singleLinkedList.Add(2); singleLinkedList.Add(3); singleLinkedList.Add(4); singleLinkedList.Add(5); Console.WriteLine("当前链表内容为:"); for (var i = 0; i < singleLinkedList.Count; i++) { Console.Write($"{singleLinkedList[i]}、"); } Console.WriteLine(); singleLinkedList.InsertToIndex(0, 0); Console.WriteLine("插入第一位 之后链表内容为:"); for (var i = 0; i < singleLinkedList.Count; i++) { Console.Write($"{singleLinkedList[i]}、"); } Console.WriteLine(); singleLinkedList.InsertToIndex(9, singleLinkedList.Count-1); Console.WriteLine("插入最后一位 之后链表内容为:"); for (var i = 0; i < singleLinkedList.Count; i++) { Console.Write($"{singleLinkedList[i]}、"); } Console.WriteLine(); singleLinkedList.InsertToIndex(2.5, 3); Console.WriteLine("执行插入之后链表内容为:"); for (var i = 0; i < singleLinkedList.Count; i++) { Console.Write($"{singleLinkedList[i]}、"); } Console.WriteLine(); singleLinkedList.RemoveAtIndex(0); Console.WriteLine("移除第一位 操作之后链表内容为:"); for (var i = 0; i < singleLinkedList.Count; i++) { Console.Write($"{singleLinkedList[i]}、"); } Console.WriteLine(); singleLinkedList.RemoveAtIndex(singleLinkedList.Count-1); Console.WriteLine("移除最后一位 操作之后链表内容为:"); for (var i = 0; i < singleLinkedList.Count; i++) { Console.Write($"{singleLinkedList[i]}、"); } Console.WriteLine(); singleLinkedList.RemoveAtIndex(3); Console.WriteLine("移除操作之后链表内容为:"); for (var i = 0; i < singleLinkedList.Count; i++) { Console.Write($"{singleLinkedList[i]}、"); } #endregion Console.ReadKey(); }
运行结果如下
双链表
双链表与单链表的不同之处在于,拥有两个指针域(一个指向前驱节点,一个指向后继节点),同时最后一个节点的后继是头结点
双链表的实现
结点:
/// <summary> /// 双链表结点 /// </summary> /// <typeparam name="T"></typeparam> public class DbNode<T> { /// <summary> /// 数据域 /// </summary> public T Item { set; get; } /// <summary> /// 前驱 /// </summary> public DbNode<T> Prev { set; get; } /// <summary> /// 后驱 /// </summary> public DbNode<T> Next { set; get; } public DbNode () { } public DbNode (T item) { Item = item; } }
双链表:
/// <summary> /// 手动实现双链表 /// </summary> /// <typeparam name="T"></typeparam> public class DoubleLinkedList<T> { public DoubleLinkedList () { count = 0; head = null; } /// <summary> /// 长度 /// </summary> private int count; /// <summary> /// 长度属性 /// </summary> public int Count { get { return this.count; } } /// <summary> /// 头结点 /// </summary> private DbNode<T> head; /// <summary> /// 索引 /// </summary> /// <param name="index"></param> /// <returns></returns> public DbNode<T> this[int index] { get { return GetNodeByIndex(index); } set { } } /// <summary> /// 获取索引位置的元素 /// </summary> /// <param name="index"></param> /// <returns></returns> public DbNode<T> GetNodeByIndex (int index) { if (index < 0 || index >= count) throw new ArgumentOutOfRangeException( nameof(index), "超出索引长度"); DbNode<T> theNode = this.head; for (int i = 0; i < index; i++) { theNode = theNode.Next; } return theNode; } /// <summary> /// 在末尾添加一个元素 /// </summary> /// <param name="Item"></param> public void AddAfter (T Item) { var newNode = new DbNode<T>(Item); if (this.head is null) { this.head = newNode; } else { var lastNode = this.GetNodeByIndex(this.count - 1); lastNode.Next = newNode; newNode.Prev = lastNode; newNode.Next = this.head; this.head.Prev = newNode; } this.count++; } /// <summary> /// 在末尾之前添加一个元素 /// </summary> /// <param name="Item"></param> public void AddBefore (T Item) { var newNode = new DbNode<T>(Item); if (this.head is null) { this.head = newNode; } else { var beforeNode = this.GetNodeByIndex(this.count - 1); newNode.Next = beforeNode; newNode.Prev = beforeNode.Prev; beforeNode.Prev.Next = newNode; beforeNode.Prev = newNode; } this.count++; } /// <summary> /// 在指定位置后继节点添加元素 /// </summary> /// <param name="Item"></param> /// <param name="index"></param> public void InsertAfter (T Item,int index) { DbNode<T> newNode = null; if (index < 0 || index >= count) throw new ArgumentOutOfRangeException( nameof(index), "超出索引长度"); else if (index == 0) { if (this.head is null) { newNode = new DbNode<T>(Item); this.head = newNode; } else { newNode = new DbNode<T>(Item); newNode.Next = this.head.Next; newNode.Prev = this.head; this.head.Next.Prev = newNode; this.head.Next = newNode; } } else { newNode = new DbNode<T>(Item); DbNode<T> oldNode = this.GetNodeByIndex(index); newNode.Prev = oldNode; newNode.Next = oldNode.Next; oldNode.Next.Prev = newNode; oldNode.Next = newNode; } this.count++; } /// <summary> /// 在指定位置前驱节点添加元素 /// </summary> /// <param name="Item"></param> /// <param name="index"></param> public void InsertBefore (T Item, int index) { DbNode<T> newNode = null; if (index < 0 || index >= count) throw new ArgumentOutOfRangeException(nameof(index), "超出索引长度"); else if (index == 0) { if (this.head is null) { newNode = new DbNode<T>(Item); this.head = newNode; } else { newNode = new DbNode<T>(Item); newNode.Next = this.head; this.head.Prev = newNode; var lastNode = this.GetNodeByIndex(this.count - 1); lastNode.Next = newNode; newNode.Prev = lastNode; this.head = newNode; } } else { newNode = new DbNode<T>(Item); DbNode<T> oldNode = this.GetNodeByIndex(index); newNode.Next = oldNode; newNode.Prev = oldNode.Prev; oldNode.Prev.Next = newNode; oldNode.Prev = newNode; } this.count++; } /// <summary> /// 移除指定位置的元素 /// </summary> /// <param name="index"></param> public void RemoveAtIndex (int index) { if (index < 0 || index >= count) throw new ArgumentOutOfRangeException( nameof(index), "超出索引长度"); else if (index == 0) { var secondNode = this.head.Next; secondNode.Prev = this.head.Prev; DbNode<T> lastNode = this.GetNodeByIndex(this.count -1);//当前元素 lastNode.Next = secondNode; this.head = secondNode; } else { DbNode<T> theNode = this.GetNodeByIndex(index);//当前元素 DbNode<T> beforeNode = theNode.Prev;//前驱节点 DbNode<T> nextNode = theNode.Next;//后继节点 beforeNode.Next = nextNode; nextNode.Prev = beforeNode; } this.count--; } }
运行试验一下:
static void Main (string[] args) { #region 双链表 DoubleLinkedList<double> doubleLinkedList = new DoubleLinkedList<double>(); doubleLinkedList.AddAfter(1); Console.WriteLine("当前双链表内容为:"); for (var i = 0; i < doubleLinkedList.Count; i++) { Console.Write($"{doubleLinkedList[i].Prev?.Item}-{doubleLinkedList[i].Item}-{doubleLinkedList[i].Next?.Item}、"); } Console.WriteLine(); doubleLinkedList.AddAfter(3); Console.WriteLine("当前双链表内容为:"); for (var i = 0; i < doubleLinkedList.Count; i++) { Console.Write($"{doubleLinkedList[i].Prev?.Item}-{doubleLinkedList[i].Item}-{doubleLinkedList[i].Next?.Item}、"); } Console.WriteLine(); doubleLinkedList.AddBefore(2); Console.WriteLine("当前双链表内容为:"); for (var i = 0; i < doubleLinkedList.Count; i++) { Console.Write($"{doubleLinkedList[i].Prev?.Item}-{doubleLinkedList[i].Item}-{doubleLinkedList[i].Next?.Item}、"); } Console.WriteLine(); doubleLinkedList.AddAfter(4); Console.WriteLine("当前双链表内容为:"); for (var i = 0; i < doubleLinkedList.Count; i++) { Console.Write($"{doubleLinkedList[i].Prev?.Item}-{doubleLinkedList[i].Item}-{doubleLinkedList[i].Next?.Item}、"); } Console.WriteLine(); doubleLinkedList.InsertBefore(0, 0); Console.WriteLine("插入第一位 之后双链表内容为:"); for (var i = 0; i < doubleLinkedList.Count; i++) { Console.Write($"{doubleLinkedList[i].Prev?.Item}-{doubleLinkedList[i].Item}-{doubleLinkedList[i].Next?.Item}、"); } Console.WriteLine(); doubleLinkedList.InsertAfter(9, doubleLinkedList.Count - 1); Console.WriteLine("插入最后一位 之后双链表内容为:"); for (var i = 0; i < doubleLinkedList.Count; i++) { Console.Write($"{doubleLinkedList[i].Prev?.Item}-{doubleLinkedList[i].Item}-{doubleLinkedList[i].Next?.Item}、"); } Console.WriteLine(); doubleLinkedList.InsertAfter(2.5, 3); Console.WriteLine("执行InsertAfter插入之后双链表内容为:"); for (var i = 0; i < doubleLinkedList.Count; i++) { Console.Write($"{doubleLinkedList[i].Prev?.Item}-{doubleLinkedList[i].Item}-{doubleLinkedList[i].Next?.Item}、"); } Console.WriteLine(); doubleLinkedList.InsertBefore(2.2, 3); Console.WriteLine("执行InsertBefore插入之后双链表内容为:"); for (var i = 0; i < doubleLinkedList.Count; i++) { Console.Write($"{doubleLinkedList[i].Prev?.Item}-{doubleLinkedList[i].Item}-{doubleLinkedList[i].Next?.Item}、"); } Console.WriteLine(); doubleLinkedList.RemoveAtIndex(0); Console.WriteLine("移除第一位 操作之后双链表内容为:"); for (var i = 0; i < doubleLinkedList.Count; i++) { Console.Write($"{doubleLinkedList[i].Prev?.Item}-{doubleLinkedList[i].Item}-{doubleLinkedList[i].Next?.Item}、"); } Console.WriteLine(); doubleLinkedList.RemoveAtIndex(doubleLinkedList.Count - 1); Console.WriteLine("移除最后一位 操作之后双链表内容为:"); for (var i = 0; i < doubleLinkedList.Count; i++) { Console.Write($"{doubleLinkedList[i].Prev?.Item}-{doubleLinkedList[i].Item}-{doubleLinkedList[i].Next?.Item}、"); } Console.WriteLine(); doubleLinkedList.RemoveAtIndex(3); Console.WriteLine("移除操作之后双链表内容为:"); for (var i = 0; i < doubleLinkedList.Count; i++) { Console.Write($"{doubleLinkedList[i].Prev?.Item}-{doubleLinkedList[i].Item}-{doubleLinkedList[i].Next?.Item}、"); } #endregion Console.ReadKey(); }
运行结果如下
.Net的实现
.net 实现了单链表和双链表,分别是