数组不总是组织数据的最佳数据结构,原因如下。在很多编程语言中,数组的长度是固定 的,所以当数组已被数据填满时,再要加入新的元素就会非常困难。在数组中,添加和删 除元素也很麻烦,因为需要将数组中的其他元素向前或向后平移,以反映数组刚刚进行了 添加或删除操作。然而,JavaScript 的数组并不存在上述问题,因为使用 split() 方法不需 要再访问数组中的其他元素了。 JavaScript 中数组的主要问题是,它们被实现成了对象,与其他语言(比如 C++ 和 Java) 的数组相比,效率很低(请参考 Crockford 那本书的第 6 章)。 如果你发现数组在实际使用时很慢,就可以考虑使用链表来替代它。除了对数据的随机访 问,链表几乎可以用在任何可以使用一维数组的情况中。如果需要随机访问,数组仍然是 更好的选择。-- by 数据结构与算法Javascript描述
链表的实现
节点
class Node {
constructor(data) {
this.data = data; // 存贮每个节点的数据
// this.prev = null;
this.next = null; // 指向下一个节点
}
}
单链表
// 单项链表
class SingleList {
constructor() {
this.size = 0;
this.head = new Node("head")
}
// 在单链表中寻找item元素
find(item) {
let curNode = this.head;
while (curNode && curNode.data != item) {
curNode = curNode.next;
}
return curNode
}
// 获取单链表的最后一个节点
findLast() {
var curNode = this.head;
while (curNode.next) {
curNode = curNode.next;
}
return curNode;
}
// 向单链表中插入元素(默认插入第一位)
insert(item = this.head.data, data) {
if (data == null) return false;
var targetNode = this.find(item);
if (!targetNode) return false;
let dataNode = new Node(data);
dataNode.next = targetNode.next;
targetNode.next = dataNode;
this.size++;
return true;
}
// 在单链表中删除一个节点
remove(item) {
if (item == this.head.data || item == null) return false;
// 先找到目标的前一个结点 curNode
let curNode = this.head;
while (curNode && curNode.next && curNode.next.data != item) {
curNode = curNode.next;
// 当curNode遍历到倒数第二个位置时,还没找到item,就直接设为null打断循环
if (curNode.next == null) {
curNode = null;
}
}
// 如果没有找到前一个节点
if (!curNode) return false;
// 找到前一个结点的话
let target = curNode.next;
curNode.next = target.next;
target.next = null;
this.size--;
return true;
}
// 在单链表的尾部添加元素
append(item) {
if (item == null) return false;
var lastNode = this.findLast();
let newNode = new Node(item);
lastNode.next = newNode;
this.size++;
return true
}
// 判断单链表是否为空
isEmpty() {
return this.size
}
// 单链表的遍历显示
display() {
if (!this.size) return;
let curNode = this.head;
while (curNode) {
console.log(curNode.data)
curNode = curNode.next;
}
}
// 清空单链表
clear() {
this.head.next = null;
this.size = 0;
}
}
此外,有些单链表可能有环结构,比如最后一个节点,指向中间的某一节点,构成闭环。此时需要一个方法来检测,我这里采用了快慢指针。 step1:构造环
var myList = new SingleList()
var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
arr.map(item => myList.append(item))
var C = myList.find('C')
var G = myList.findLast()
G.next = C
step2:使用快慢指针来判断是否有环,若快指针走到最后为null,说明没有环,若两个指针在某个时刻相等了,则说明有环。
function isLoop(list) {
// 使用快慢指针
var p = list.head
var q = list.head
while (q) {
p = p.next
if (q.next) {
q = q.next.next
} else {
q = null
}
if (p === q) {
console.log('有环')
return
}
}
console.log('没有环')
}
isLoop(myList) // 有环