开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
前言
今天来简单聊聊关于javascript进阶知识 —— 链表
链表数据结构
要存储多个元素,数组(或列表)可能是最常用的数据结构。正如本书之前提到过的,每种语言都实现了数组。这种数据结构非常方便,提供了一个便利的 [] 语法来访问它的元素。然而这种数据结构有一个缺点:(在大多数语言中)数组的大小是固定的,从数组的起点或中间插入或移除项的成本很高,因为需要移动元素(尽管我们已经学过的JavaScript的array类方法可以帮我们做这些事,但背后的情况同样是这样 )。
链表存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的。每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成。下图展示了一个链表的结构:
相对于传统的数组,链表的一个好处在于,添加或移除元素的时候不需要移动其他元素。然而,链表需要使用指针,因此实现链表时需要额外注意。数组的另一个细节是可以直接访问任何位置的任何元素,而要想访问链表中间的一个元素,需要从起点(表头开始迭代列表直到找到所需的元素。
现实中也有一些链表的例子。第一个例子就是康加舞队。每个人是一个元素,手就是链向下个人的指针。可以向队列中增加人一-只需要找到想加人的点,断开连接,插人一个人,再重新连接起来。 另一个例子是寻宝游戏。你有一条线索,这条线索是指向寻找下一条线索的地点的指针。你顺着这条链接去下一个地点,得到另一条指向再下一处的线索。得到列表中间的线索的唯一办法就是从起点(第一条线索)顺着列表寻找。
还有一个可能是用来说明链表的最流行的例子,那就是火车。一列火车是由一系列车厢(也称车皮)组成的。每节车厢或车皮都相互连接。你很容易分离一节车皮,改变它的位置,添加或移除它。下图演示了一列火车。每节车皮都是列表的元素,车皮间的连接就是指针:
创建链表
理解了链表是什么之后,现在就要开始实现我们的数据结构了。以下是我们的LinkedList类的骨架:
function LinkedList(){
let Node = function (element){
this.element = element;
this.next = null;
}
let length = 0;
let head = null;
this.append = function (element){};
this.insert = function (position, element){};
this.removeAt = function(position){};
this.remove = function(element){};
this.indexO = function(element){};
this.isEmpty = function(){};
this.size = function() {};
this.getHead = function(){};
this.tostring = function(){}
ithis.print = function(){};
LinkedList数据结构还需要一个Node辅助类(行{1})。Node类表示要加人列表的项。它包含一个element属性,即要添加到列表的值,以及一个next属性,即指向列表中下一个节点项的指针。
LinkedList类也有存储列表项的数量的length属性(内部/私有变量)(行{2})。 另一个重要的点是,我们还需要存储第一个节点的引用。为此,可以把这个引用存储在一个称为head的变量中(行{2})。
然后就是LinkedList类的方法。在实现这些方法之前,先来看看它们的职责
· append(element): 向列表尾部添加一个新的项。
· insert(position,element): 向列表的特定位置插入一个新的项 remove(element): 从列表中移除一项。
· indexof(element): 返回元素在列表中的索引。如果列表中没有该元素则返回-1。
· removeAt(position): 从列表的特定位置移除一项。
· isEmpty():如果链表中不包含任何元素,返回true,如果链表长度大于0则返回false。
· size(): 返回链表包含的元素个数。与数组的length属性类似。
· tostring():由于列表项使用了Node类,就需要重写继承自JavaScript对象默认的tostring方法,让其只输出元素的值。