JavaScript数据结构——链表
链表:存储有序的元素集合,但不同于数组,链表中的元素在内存中不是连续放置的。每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成。
好处:可以添加或移除任意项,它会按需扩容,且不需要移动其他元素。
与数组的区别:
数组:可以直接访问任何位置的任何元素;
链表:想要访问链表中的一个元素,需要从起点(表头)开始迭代列表直到找到所需的元素。
现实案例:康佳舞队、寻宝游戏
单向链表:一个节点只有链向下一个节点的链接;
双向链表:链接是双向的,一个链向下一个元素,另一个链向前一个元素
优点:双向链表提供了两种迭代列表的方法:从头到尾,或者反过来。我们也可以访问一个特定节点的下一个或前一个元素。在单向链表中,如果迭代列表时错过了要找的元素,就需要回到列表起点,重新开始迭代。
-------------------------------------------------------------------------------------------------------------
链表方法声明:
1. 定义Node辅助类;
2. 内部属性 length;
2. 存储第一个节点(head)的引用
序号
|
方法
|
说明
|
1
|
append(element) | 向列表尾部添加一个新的项 |
2
|
insert(position, element) | 向列表的特定位置插入一个新的项 |
3
|
remove(element)
|
从列表中移除一项 |
4
|
indexOf (element )
|
返回元素在列表中的索引。如果列表中没有该元素则返回-1 |
5
|
removeAt(position) |
从列表的特定位置移除一项
|
6
|
isEmpty()
|
如果链表中不包含任何元素,返回 true,如果链表长度大于 0 则返回 false
|
7
|
size ( )
|
返回链表包含元素个数。与数组的 length 属性类似 |
8
|
toString ( )
|
由于列表项使用了 Node 类,就需要重写继承自 JavaScript 对象默认的 toString 方法,让其只输出元素的值 |
链表实现:
1 function LinkedList() { 2 // 定义辅助类Node 3 var Node = function(element) { 4 this.element = element; // element属性,即要添加到列表的元素 5 this.next = null; // next属性,即指向列表中下一个节点项的指针 6 } 7 8 var length = 0; // 内部属性/私有变量 9 var head = null; // 第一个节点的引用 10 11 // 向列表的尾部添加一个新的项 12 this.append = function(element) { 13 var node = new Node(element), // 把element作为值传入,创建Node项 14 current; 15 16 if (head === null) { // 列表中第一个节点,如果head元素为null,则意味着向列表添加第一个元素 17 head = node; // head指向node元素,下一个node将会自动生成null 18 } else { 19 current = head; 20 21 // 循环列表,直到找到最后一项 22 while(current.next) { 23 current = current.next; 24 } 25 26 // 找到最后一项,将其next赋为node,建立连接 27 current.next = node; // 列表中最后一个节点的下一个元素始终是null 28 } 29 30 length++; // 更新链表长度,这样就能控制它,轻松地得到列表的长度 31 }; 32 33 // 向列表的特定位置插入一个新的项 34 this.insert = function(position, element) { 35 // 检查越界值 36 if (position >= 0 && position <= length) { 37 var node = new Node(element), 38 current = head, 39 previous, 40 index = 0; 41 42 if (position === 0) { // 在第一个位置添加 43 node.next = current; 44 head = node; 45 } else { 46 while (index++ < position) { 47 previous = current; 48 current = current.next; 49 } 50 node.next = current; 51 previous.next = node; 52 } 53 54 length++; 55 56 return true; 57 58 } else { 59 return false; 60 } 61 }; 62 63 // 从列表的特定位置移除一项 64 this.removeAt = function(position) { 65 // 检查越界值 66 if (position > -1 && position < length) { 67 var current = head, // current变量总是为对所循环列表的当前元素的引用 68 previous, // previous变量为对当前元素的前一个元素的引用 69 index = 0; 70 71 // 移除第一项 72 if (position === 0) { 73 head = current.next; 74 } else { 75 while (index++ < position) { // 使用用于内部控制和递增的index变量来迭代列表 76 previous = current; 77 current = current.next; 78 } 79 80 // 将previous与current的下一项链接起来:跳过current,从而移除它 81 previous.next = current.next; 82 } 83 84 length--; 85 86 return current.element; 87 } else { 88 return null; 89 } 90 }; 91 92 // 从列表中移除一项 93 this.remove = function(element) { 94 var index = this.indexOf(element); 95 return this.removeAt(index); 96 }; 97 98 // 返回元素在列表中的索引。如果列表中没有该元素则返回-1 99 this.indexOf = function(element) { 100 var current = head, 101 index = -1; 102 103 while (current) { 104 if (element === current.element) { 105 return index; 106 } 107 index++; 108 current = current.next; 109 } 110 111 return -1; 112 }; 113 114 // 如果链表中不包含任何元素,返回 true,如果链表长度大于 0 则返回 false 115 this.isEmpty = function() { 116 return length === 0; 117 }; 118 119 // 返回链表包含元素个数。与数组的 length 属性类似 120 this.size = function() { 121 return length; 122 }; 123 124 // 由于列表项使用了 Node 类,就需要重写继承自 JavaScript 对象默认的 toString 方法,让其只输出元素的值 125 this.toString = function() { 126 var current = head, 127 string = ''; 128 129 while (current) { 130 string += current.element; 131 current = current.next; 132 } 133 134 return string; 135 }; 136 137 this.getHead = function () { 138 return head; 139 }; 140 }
双向链表实现:
1 function DoublyLinkedList() { 2 var Node = function(element) { 3 this.element = element; 4 this.next = null; 5 this.prev = null; 6 }; 7 8 var length = 0; 9 var head = null; 10 var tail = null; // 新增的 11 12 // 特定位置插入元素 13 this.insert = function(position, element) { 14 // 检查越界值 15 if (position >= 0 && position <= length) { 16 var node = new Node(element), 17 current = head, 18 previous, 19 index = 0; 20 21 if (position === 0) { // 在第一个位置添加 22 if (!head) { // 新增的 23 head = node; 24 tail = node; 25 } else { 26 node.next = current; 27 current.prev = node; 28 head = node; 29 } 30 } else if (position === length-1) { // 最后一项 //新增的 31 current = tail; 32 current.next = node; 33 node.prev = current; 34 tail = node; 35 } else { 36 while (index++ < position) { 37 previous = current; 38 previous.next = node; 39 } 40 41 node.next = current; 42 previous.next = node; 43 44 current.prev = node; //新增的 45 node.prev = previous; //新增的 46 } 47 48 length++; 49 return true; 50 51 } else { 52 return false; 53 } 54 }; 55 56 // 特定位置删除元素 57 this.removeAt = function(position) { 58 // 检查越界值 59 if (position > -1 && position < length) { 60 var current = head, 61 previous, 62 index = 0; 63 64 // 移除第一项 65 if (position === 0) { 66 head = current.next; 67 68 // 如果只有一项,更新tail //新增的 69 if (length === 1) { 70 tail = null; 71 } else { 72 head.prev = null; 73 } 74 } else if (position === length-1) { 75 current = tail; 76 tail = current.prev; 77 tail.next = null; 78 } else { 79 while (index++ < position) { 80 previous = current; 81 current = current.next; 82 } 83 // 将prvious与current的下一项链接起来——跳过current 84 previous.next = current.next; 85 current.next.prev = previous; // 新增的 86 } 87 88 length--; 89 90 return current.element; 91 } else { 92 return null; 93 } 94 }; 95 }
参考书籍:《学习JavaScript数据结构与算法》
Scoop It and Enjoy the Ride!