数组不总是组织数据的最佳数据结构,在很多编程语言中,数组的长度是固定的,所以当数组已被数据填满时,再要加入新的元素就会非常困难。在数组中,添加和删除元素也很麻烦,因为需要将数组中的其他元素向前或向后平移。
链表是由一组节点组成的集合。每个节点都使用一个对象的引用指向它的后继。许多链表的实现都在链表最前面有一个特殊节 点,叫做头节点。如下图:
基于对象的链表的实现
Node 类用来表示节点,包含两个属性:element 用来保存节点上的数据,next 用来保存指向下一个节点的链接。
function Node(element) { this.element = element; this.next = null;}
LList 类提供了对链表进行操作的方法。该类的功能包括插入删除节点、在列表中查找给定的值。
function LList() { this.head = new Node("head"); this.find = find; this.insert = insert; this.display = display; this.findPrevious = findPrevious; this.remove = remove;}
find() 方法遍历链表,查找给定数据。如果找到数据,该方法就返回保存该数据的节点。如果查找成功,该方法返回包含该数据的节点;否则,返回 null。
function find(item) { var currNode = this.head; while (currNode.element != item) { currNode = currNode.next; } return currNode;}
insert() 方法在一个已知节点后面插入元素
function insert(newElement, item) { var newNode = new Node(newElement); var current = this.find(item); newNode.next = current.next; current.next = newNode;}
display() 方法用来显示链表中的元素
function display() { var currNode = this.head; while (!(currNode.next == null)) { print(currNode.next.element); currNode = currNode.next; }}
findPrevious() 方法找到待删除节点前面的节点。找到这个节点后,修改它的 next 属性,使其不再指向待删除节点,而是指向待删除节点的下一个节点。
function findPrevious(item) { var currNode = this.head; while (!(currNode.next == null) && (currNode.next.element != item)) { currNode = currNode.next; } return currNode;}
remove() 方法从链表中删除节点。
function remove(item) { var prevNode = this.findPrevious(item); if (!(prevNode.next == null)) { prevNode.next = prevNode.next.next; }}
双向链表
给 Node 对象增加一个属性,该属性存储指向前驱节点的链接,这使得链表从后向前遍历变得简单
function Node(element) { this.element = element; this.next = null; this.previous = null;}
insert() 方法
function insert(newElement, item) { var newNode = new Node(newElement); var current = this.find(item); newNode.next = current.next; current.next.previous = newNode; newNode.previous = current; current.next = newNode;}
remove() 方法比单向链表的效率更高,因为不需要再查找前驱节点。
function remove(item) { var currNode = this.find(item); if (!(currNode.next == null)) { currNode.previous.next = currNode.next; currNode.next.previous = currNode.previous; currNode.next = null; currNode.previous = null; }else { currNode.previous.next = null; currNode.next = null; currNode.previous = null; }}
findLast() 方法查找链表中的最后一个节点,避免了从前往后遍历链表
function findLast() { var currNode = this.head; while (!(currNode.next == null)) { currNode = currNode.next; } return currNode;}
dispReverse() 方法反序显示双向链表中的元素。
function dispReverse() { var currNode = this.head; currNode = this.findLast(); while (!(currNode.previous == null)) { console.log(currNode.element); currNode = currNode.previous; }}
循环链表
循环链表和单向链表相似,节点类型都是一样的。唯一的区别是,在创建循环链表时,让其头节点的 next 属性指向它本身,即链表的尾节点指向头节点,形成了一个循环链表。如下图: