链表是一种基础数据结构,它是集合类的抽象数据结构类型中表示数据的合适类型。与数字结构不同之处在于,在链表中插入元素和删除元素都更加方便。
定义:
链表表示的一列元素,由一系列的节点(Node)构成,是一种递归数据结构。节点是一个能够包含任何类型数据的抽象实体,它所包含的指向节点的应用体现了他在链表中的作用。
构造:
private class Node { public T Data; public Node Next; public Node(T data) { Data = data; } }
上面的是单向链表,优点是简单高效,但任意删除和增加节点并不方便。这种情况下,就可以使用双向链表等数据结构了。今天我给大家演示的主要是使用单向链表实现栈和队列这两种数据结构。
应用:
1.实现一种泛型栈数据结构
栈的特点是先进后出(FILO),根据这一特点,我们设计时,Push()方法在链表头部插入新节点,Pop()方法取出链表头结点。实现起来非常简单,比起上一篇博文中使用数组来结构化存储数据,使用链表则灵活的多,不再需要我们手动调整栈容量,能够自动的灵活变化大小。
实现代码如下:
class NodeStack<T>:IEnumerable { private int N; private Node first; public T firstData() { return first.Data; } public int Size() { return N; } public void Push(T item) { N++; var oldFirst = first; first = new Node(item); first.Next = oldFirst; } public T Pop() { --N; var oldFirst = first; first = first.Next; return oldFirst.Data; } private class Node { public T Data; public Node Next; public Node(T data) { Data = data; } } public IEnumerator GetEnumerator() { var oldFirst = first; while (first!=null) { var node = first; first = first.Next; yield return node.Data; } first = oldFirst; } }
2.实现一种泛型队列
基于链表数据结构实现队列(Queue)也很简单,根据其先进先出特性,设计时,实例变量first指向它的表头,实例变量last指向它的尾节点。enqueue()方法将一个元素添加到表尾,dequeu()方法返回并删除表头。
实现代码如下:
class NodeQueue<T>:IEnumerable { private int N; private Node first; private Node last; public bool isEmpty() { return first == null; } public int Size() { return N; } public void enqueue(T item) { var oldLast = last; last = new Node(item); if (isEmpty()) first = last; else oldLast.Next = last; N++; } public T dequeue() { N--; var oldFirst = first; first = first.Next; if (isEmpty()) last = null; return oldFirst.Data; } private class Node { public T Data; public Node Next; public Node(T data) { Data = data; } } public IEnumerator GetEnumerator() { var oldFirst = first; while (first != null) { var node = first; first = first.Next; yield return node.Data; } first = oldFirst; } }
总结:
链表这种数据结构是数组的重要替代方式,这种替代方案已由数十年的历史了。相对而言,链表数据结构更加灵活,后续随着学习的不断加深,会继续补充双向链表和树等相关内容。通过学习链表,对常见的API 类型的实现,能够理解的更灵活,这种简单但灵活高效的结构,让人映像深刻。