前序
回过头看看以前用JS写了一半的链表,终于还是没忍住,实现了前段时间看着头疼的链表,其实链表也就那样。以前没细看数据结构,面试时被顺丰面试官要求手撕链表,怂的直说会用伪代码实现,就没然后了!面完后我就刻苦钻研,还是没搞懂大佬用JS实现的链表,虽然写了一半,但思路还是云里雾里的,昨天看了链表的相关数据结构细节问题,今天无意间看到以前留下的垃圾代码,就根据 把链表的基础属性与功能映射成JS代码,这个过程才真正理解到底层基础跟原理相当重要,在加上逻辑思维的体现,实现一个链表还是对以后学习有启发的。
有些人一开始就能得到正解,然而像我经历了很多才得到正解!
以上纯属瞎扯皮,我这个人爱思考、喜欢分享个人精神世界,有事没事咱一块吹吹水哈!
正文
链表分为单向链表、双向链表、循环链表 。
单向链表 :单向链表包含两个域,一个是信息域,一个是指针域。也就是单项链表的节点被分成两部分,第一部分保存或显示节点信息,第二部分存储下一个节点的地址,而最后一个节点则指向一个空值null。
双向链表 :双向链表每个节点两个指针,一个指向前一个节点的指针,另一个指向后一个节点的指针,而头节点和尾节点都指向空值null。
循环链表: 循环链表就是首节点和尾节点被连接在一起。
JS手撕单向链表:
// 定义节点
class Node{
constructor(v,next){
this.val=v;
this.next=next;
}
}
// 定义链表
class LinkList{
constructor(){
//链表长度
this.size=0;
//虚拟头部
this.dummyNode=new Node(null,null);
}
//查找节点
find(header,currentIndex,index){
if(index===currentIndex){
return header;
}
return this.find(header.next,currentIndex+1,index);
}
//查找某一节点的后继节点
getNode(index){
this.checkIndex(index);
if(this.isEmpty()){
return null
}
return this.find(this.dummyNode,0,index).next;
}
//插入节点
insertNode(v,index){
this.checkIndex(index);
let prev=this.find(this.dummyNode,0,index);
prev.next=new Node(v,prev.next);
this.size+1;
return prev.next;
}
//插入头节点
insertFirstNode(v){
return addNode(v,0);
}
//插入尾节点
insertLastNode(v){
return addNode(v,this.size);
}
//在链表中非首尾的任意位置插入节点
insertAnyNode(v,index){
return addNode(v,index);
}
//删除节点
removeNode(index, isLast){
this.checkIndex(index);
index=isLast ? index-1 : index;
let prev=this.find(this.dummyNode,0,index);
let node=prev.next;
prev.next=node.next;
node.next=null;
this.size--;
return node
}
//删除头节点
removeFirstNode(){
return this.removeNode(0)
}
//删除尾节点
removeLastNode(){
return this.removeNode(this.size,true)
}
//删除非首尾的任意位置节点
removeAnyNode(index){
return this.removeNode(index,false)
}
checkIndex(index){
if(index<0 || index>this.size){
throw Error('Index Error');
}
}
//链表长度
getSize(){
return this.size;
}
//链表判空
isEmpty(){
return this.size===0;
}
}