1、概念理解
正如文章 Data Structures With JavaScript: Singly-Linked List and Doubly-Linked List 中所言,链表这种数据结构非常像电视节目里的 寻宝游戏 —— 而不是 火车。
假如你设计寻宝游戏,达到指定地点后(假如你当前在北京西单)在某个特定地方放置一张纸条,该纸条上还包含了下一个你要去的地点(下一站去帽儿胡同)…. 按照这种程序下去,将这些地点串接起来就形成了一个寻宝游戏。借助这个示例,就能从概念上帮你区分与数组、队列这些数据结构之间的差别。
single linked list
double
Node:节点类
主要是和数组的对比: 数组的优点是随机查找,但是在插入或者删除元素时后面的元素都会移动。而链表是由节点和数据组成,不具有随机查找的特性(链表的优缺点和数组的优缺点恰好相反)。也就是说链表查询性能比较差,修改操作比较优化。
链表中如果只是插入和删除操作,那么不会移动元素,所以会节省时间,数组的插入和删除是要移动元素的(插入和删除最后一个元素不移动);链表的查找操作是从第一个元素开始,所以相对数组要耗时间(数组直接就可以查找到)
优点:
缺点:
next
属性,耗费额外的内存prev
属性占用内存空间总而言之,链表的用处是在特定应用场景下省时间,基本上链表不能省空间。
参考现有的库:
同时还借鉴了上述参考文章的写法,最终自己编写了一个 ss-linked-list 库。该库实现了 单向链表、双向链表、单向循环链表 以及 双向循环链表。
单向链表典型的应用场合是 各类缓冲池 和 栈 的实现,稀疏矩阵也可以用单向列表实现。 双向链表典型应用场景是各种 不需要排序的数据列表 管理
循环单向链表最典型的应用比如是系统的时间切片,给每个程序都分配一定的执行时间,然后轮到下一个。
可以通过在线服务 The Josephus Game 验证程序的准确性
const {CircleDoublyList} = require("ss-linked-list");
// 使用双向循环链表实现
function Josephus(n, start, k){
var numberArr = Array.from(new Array(n),(val,index)=>index+1);
var list = new CircleDoublyList(...numberArr);
var currentNode = list.getNode(start - 1);
var result = [];
while(n--){
for(var i = 0; i < k - 1; i++){
currentNode = currentNode.next;
}
result.push(currentNode.value);
currentNode.prev.next = currentNode.next;
currentNode.next.prev = currentNode.prev;
currentNode = currentNode.next;
}
return result;
}
Josephus(9, 1, 5);
// 输出结果是:[5, 1, 7, 4, 3, 6, 9, 2, 8]
另外还有单向循环列表的实现,具体都放在:https://runkit.com/boycgit/ss-linked-list
在前端层面,应用链表的场景是比较少的,数组(Array)发挥的作用更大。链表在经常变更的大型集合(比如稀疏矩阵)中才会发挥其价值(然而这场场景是很少的),以下的场景也很合适:
正是因为上述特性,你会发现链表经常还是其他数据结构的基础。比如堆栈、队列是频繁操作节点的,虽然使用数组可以实现,但使用链表会更加地合适。
双向列表的优势在于可以正向、逆向迭代查询,使用起来稍微比单向列表要灵活一些,但同时也稍微多占用一些内存。
REFERENCE
参考文档
—END—