js创建链表
首先要明确,我们为什么要创建链表呢?数组的大小是固定的,从数组的起点或中间插入或移除的成本很高,因为需要移动元素。尽管JS的Array类方法可以做这些,但是情况也是这样。链表存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的。每个元素由一个存储元素本身和指向下一个元素的指针组成。
相对于传统的数组,链表的一个好处在于,添加或移除元素的时候不需要移动其他元素。然而,链表需要使用指针,因此实现链表时需要额外注意。数组的另一个细节是可以直接访问任何位置的任何元素,而要想访问链表中间的一个元素,需要从起点(表头)开始迭代到列表直到找到所需元素。
好了,闲话少叙,正式创建链表!
我们使用动态原型模式来创建一个链表。列表最后一个节点的下一个元素始终是null。
function LinkedList() { //先创建一个节点 function Node(element) { this.element = element; this.next = null; } this.head = null; this.length = 0; //通过对一个方法append判断就可以知道是否设置了prototype if ((typeof this.append !== 'function') && (typeof this.append !== 'string')) { //添加元素 LinkedList.prototype.append = function(element) { var node = new Node(element); var current; if (this.head == null) { this.head = node; } else { current = this.head; while (current.next != null) { current = current.next; } current.next = node; } this.length++; }; //插入元素,成功true,失败false LinkedList.prototype.insert = function(position, element) { if (position > -1 && position < this.length) { var current = this.head; var node = new Node(element); var previous; var index = 0; if (position == 0) { node.next = current; this.head = node; } else { while (index++ < position) { previous = current; current = current.next; } node.next = current; previous.next = node; } this.length++; return true; } else { return false; } }; //根据位置删除指定元素,成功 返回元素, 失败 返回null LinkedList.prototype.removeAt = function(position) { if (position > -1 && position < this.length) { var current = this.head; var previous = null; var index = 0; if (position == 0) { this.head = current.next; } else { while (index++ < position) { previous = current; current = current.next; } previous.next = current.next; } this.length--; return current.element; } else { return null; } }; //根据元素删除指定元素,成功 返回元素, 失败 返回null LinkedList.prototype.remove = function(element) { var index = this.indexOf(element); return this.removeAt(index); }; //返回给定元素的索引,如果没有则返回-1 LinkedList.prototype.indexOf = function(element) { var current = this.head; var index = 0; while (current) { if (current.element === element) { return index; } current = current.next; index++; } return -1; }; LinkedList.prototype.isEmpty = function() { return this.length === 0; }; LinkedList.prototype.size = function() { return this.length; }; LinkedList.prototype.toString = function() { var string = ''; var current = this.head; while (current) { string += current.element; current = current.next; } return string; }; LinkedList.prototype.getHead = function() { return this.head; }; } }
链表的基本使用
var linkedList = new LinkedList(); console.log(linkedList.isEmpty());//true; linkedList.append('huang'); linkedList.append('du') linkedList.insert(1,'cheng'); console.log(linkedList.toString());//huangchengdu console.log(linkedList.indexOf('du'));//2 console.log(linkedList.size());//3 console.log(linkedList.removeAt(2));//du console.log(linkedList.toString());//huangcheng
创建双向链表
在双向链表中,链接是双向的:一个链向下一个元素, 另一个链向前一个元素。
双向链表和链表的区别就是有一个tail属性,所以必须重写insert、append、removeAt方法。每个节点对应的Node也多了一个prev属性。
//寄生组合式继承实现,详见javascript高级程序设计第七章 function inheritPrototype(subType, superType) { function object(o) { function F() {} F.prototype = o; return new F(); } var prototype = object(superType.prototype); prototype.constructor = subType; subType.prototype = prototype; } function DoublyLinkedList() { function Node(element) { this.element = element; this.next = null; this.prev = null; } this.tail = null; LinkedList.call(this); //与LinkedList不同的方法自己实现。 this.insert = function(position, element) { if (position > -1 && position <= this.length) { var node = new Node(element); var current = this.head; var previous; var index = 0; if (position === 0) { if (!this.head) { this.head = node; this.tail = node; } else { node.next = current; current.prev = node; this.head = node; } } else if (position == this.length) { current = this.tail; current.next = node; node.prev = current; this.tail = node; } else { while (index++ < position) { previous = current; current = current.next; } previous.next = node; node.next = current; current.prev = node; node.prev = previous; } this.length++; return true; } else { return false; } }; this.append = function(element) { var node = new Node(element); var current; if (this.head === null) { this.head = node; this.tail = node; } else { current = this.head; while (current.next !== null) { current = current.next; } current.next = node; node.prev = current; this.tail = node; } this.length++; }; this.removeAt = function(position) { if (position > -1 && position < this.length) { var current = this.head; var previous; var index = 0; if (position === 0) { this.head = current.next; if (this.length === 1) { this.tail = null; } else { this.head.prev = null; } } else if (position === (this.length - 1)) { current = this.tail; this.tail = current.prev; this.tail.next = null; } else { while (index++ < position) { previous = current; current = current.next; } previous.next = current.next; current.next.prev = previous; } this.length--; return current.element; } else { return false; } }; } inheritPrototype(DoublyLinkedList, LinkedList);
双向链表的基本使用
var doublyList = new DoublyLinkedList(); console.log(doublyList.isEmpty()); //true; doublyList.append('huang'); doublyList.append('du') doublyList.insert(1, 'cheng'); console.log(doublyList.toString()); //huangchengdu console.log(doublyList.indexOf('du')); //2 console.log(doublyList.size()); //3 console.log(doublyList.removeAt(2)); //du console.log(doublyList.toString()); //huangcheng