概念:链表是计算机的一种存储结构。链表由一系列结点组成,每个结点里包含了本结点的数据域和指向下一个结点的指针。如下图item存储数据,next存储下一个节点的引用,其中最后一个节点的指针指向了null,表示链表结束的位置。
作用:按一定顺序储存数据,允许在任意位置插入和删除结点。
分类:单向链表、双向链表、单向循环链表和双向循环链表
应用场景:适用于需要频繁进行插入和删除操作的数据存储。
我们需要定义两个类,一个是Node类表示节点,一个是LinkedList类表示链表;LinkedList类里有head(头节点)和length(链长)两个属性,有几个操作方法,如append(添加),insert(插入),remove(移除),removeAt(根据下标移除),isEmpty(链表是否为空),size(链表大小),display(查看链表)。
定义Node类,表示节点
class Node{
constructor(element){
this.element = element; // 数据
this.next = null; // 指针
}
}
定义LinkedList类,表示链表(为了便于阅读,已把LinkedList类的方法提取出来展示)
class LinkedList{
constructor(){
this.head = null; // 列表头
this.length = 0; // 链表长度
}
}
当链表为空时,即链表头head为null,下面实现链表的各操作方法
append(element) 新增元素
新增元素分为两种情况,链表为空和链表不为空。链表为空时,新增的元素作为链表头(直接赋值);链表不为空时,从链表尾添加,即原链表尾的指针next指向新增元素。
append(element){
let node = new Node(element); // 调用辅助类创建一个新结点
// 链表为空
if(this.head == null){
this.head = node; // 把第一个添加的结点作为链表头
}
// 已存在结点
else{
let current = this.head; // 当前正在查找的节点(从头部开始查找)
while(current.next!==null){ // 当前查找项的指针不为null(即不是最后一项),继续往后找,直到next=null循环结束
current = current.next; // 把下一项赋值给当前查找项,即查找下一项(循环的经典方法)
}
// while循环结束后,current已经到了最后一项(current.next=null)
current.next = node; // 插入新元素
}
length++; // 长度加1
}
insert(position,element) 插入元素
根据下标找到目标节点,从目标节点之前插入新元素,插入元素分为两种情况,头部插入和非头部插入。头部插入(下标等于0),即插入的元素作为链表头;非头部插入,找到目标节点和目标节点的前一个节点(我们称之为前节点),使前节点的指针指向插入节点,而插入节点的指针指向目标节点,完成插入。如下图是非头部插入示意图,node2为目标节点,node1为前节点,node3为插入节点,使node1的指针指向node3,而node3的指针指向node2,即完成了插入操作。
insert(element,position){
let node = new Node(element); // 定义一个新结点
// 越界
if(position>-1 && position
removeAt(position) 根据下标移除元素
根据下标移除元素也分为两种情况,移除链表头和移除非链表头节点。移除链表头节点(下标等于0),即使原链表头的指针指向的节点作为新链表头;移除非链表头节点,根据下标找到需要移除的目标节点和它的前一个节点,使前结点的指针 指向 目标节点的指针所指向的节点,即完成了移除目标节点。如下图是移除非链表头节点示意图,node1为目标节点,head为前节点,node2为node1指针指向的节点,使head的指针指向node2,即完成了移除node1节点的操作。
removeAt(position){
if(position>-1 && position
indexOf(element) 查找某元素的下标
从头部开始查找,如果当前查找节点等于目标节点就返回对应的下标,否则继续往后查找;若链表循环结束了还没找到,则返回-1(不存在)。
indexOf(element){
let index = 0; // 当前下标
let current = this.head; // 从头部开始查找
while(current !== null){ // 当前项不等于null,表示还不是最后一项,继续循环
if(element === current.element){ // 如果当前节点的值等于目标项
return index; // 返回对应下标
}
current = current.next; // 否则继续往后查找
index++; // 下标加1
}
return -1; // 若循环结束还没找到,返回-1,链表里不存在该元素
}
remove(element) 移除元素(代码复用)
1)调用indexOf(element)方法找到目标项的下标;
2)调用removeAt(position)方法根据目标项的下标移除目标项
remove(element){
return this.removeAt(this.indexOf(element));
}
isEmpty() 判断链表是否为空
isEmpty(){
return length === 0;
}
size() 查询链表的大小
size(){
return length;
}
查看完整源码
更多数据结构相关,请查看专栏:《JavaScript数据结构与算法》
(完)