链表-单向链表双向链表循环链表
  xWYnr39PTA9E 2023年11月22日 18 0

什么是链表

链表和数组类似,是一种线性的数据结构,与数组不同的是,链表中的数据在内存中并不是顺序存储的,而是通过在链表的每个元素中,保存指向下一个元素的引用,来找到下一个元素。

链表-单向链表双向链表循环链表_动画演示


链表元素(构成)

把元素叫做节点,节点后面的叫后继节点,节点前面的叫前置节点。

链表-单向链表双向链表循环链表_链表_02


访问节点

通过.next来访问下一个节点。

链表-单向链表双向链表循环链表_动画演示_03


应用场景

P2P网络(分布式网络)、文件系统、基础数据结构(队列)

常见链表种类

1.单链表

2.双向链表

3.循环链表

4.双向循环链表

链表-单向链表双向链表循环链表_if语句_04

链表-单向链表双向链表循环链表_while循环_05

链表-单向链表双向链表循环链表_while循环_06


创建链表

创建一个节点

可以使用一个类来表示一个节点。
class Node{
     constructor(value, next = null){
         this.value = value;
         this.next  = next;
     }
 }
 /*
 这里的Node Class 包含两个属性,value为节点的数据,next为对后继节点的引用。
 这里的next的值也是一个Node对象,所以这是一个递归结构。
 */

链表-单向链表双向链表循环链表_if语句_07

创建一个链表链表为例子。

/*
 以单链表为例子。
 对于单链表来说,只需要头节点的引用就行了。所有的操作(增删改查)都需要通过头节点来操作。 
 */
class LinkedList{
     constructor(value){
         this.head = new Node(value);
     }
 }
 /*
 在LinkedList链表类中,通过构造函数接收头部节点的值。创建一个Node对象,保存在head属性中。
 head就是头部节点的引用。
 注意:头部节点代表整个链表。
 */

链表-单向链表双向链表循环链表_#数据结构_08

查找节点

/*
 这个findNode方法接收要查找的值做为参数,返回查找到的节点,如果没有找到就返回null。
 方法中我们先定义一个临时变量currentNode(局部变量let)来存储当前遍历到的节点(默认为头节点)。
 在循环中让currentNode与value值进行对比:
     如果值不相等,就把currentNode的值设置为CurrentNode的下一个节点。(currentNode = currentNode.next;)
     如果值相等,while循环就会退出。
  如果遍历到了链表的尾部节点next还没有找到所对应的值,那就返回null。
 */
findNode(value){
     let currentNode = this.head;
     while(currentNode.value != value){
         currentNode = currentNode.next;
     }
     if(!currentNode){
         return null;
     }
     return currentNode;
 }
/*
     while(currentNode.value != value){
         currentNode = currentNode.next;
     }
 为在while循环的条件判断中,currentNode.value != value,
 当当前节点的值不等于要查找的值时,会继续执行currentNode = currentNode.next;,即移动到下一个节点继续查找。
 当遍历到链表的尾部节点时,currentNode.next为null,此时currentNode.value != value条件不成立,循环结束。
 */
 /*
     if(!currentNode){
         return null;
     }
     return currentNode;
 if语句会判断!currentNode是否为真。
 如果为真,说明已经遍历到了链表的尾部节点,没有找到要查找的值,此时返回null。
 如果为假,说明找到了要查找的值,此时返回currentNode。
 */

链表-单向链表双向链表循环链表_if语句_09

这段代码中的if语句为什么不在while循环里面?

这段JavaScript代码中的if语句不在while循环里面,是因为在while循环中已经有一个条件判断currentNode.value != value。如果这个条件成立,说明当前节点的值不等于要查找的值,那么就会执行currentNode = currentNode.next;,即移动到下一个节点继续查找。如果这个条件不成立,说明已经找到了要查找的节点,此时应该跳出循环,而不是继续执行if语句。因此,将if语句放在while循环外面是正确的。

链表-单向链表双向链表循环链表_链表_10

这段代码中我如果传入了一个在链表中没有的值value,那么在while循环中,是不是会一直遍历到链表的尾部节点,然后呢?怎么退出这个while循环,然后if语句是判断什么?

这段代码中,如果传入了一个在链表中没有的值value,那么在while循环中,会一直遍历到链表的尾部节点。因为在while循环的条件判断中,currentNode.value != value,当当前节点的值不等于要查找的值时,会继续执行currentNode = currentNode.next;,即移动到下一个节点继续查找。当遍历到链表的尾部节点时,currentNode.next为null,此时currentNode.value != value条件不成立,循环结束。

退出这个while循环后,if语句会判断!currentNode是否为真。如果为真,说明已经遍历到了链表的尾部节点,没有找到要查找的值,此时返回null。如果为假,说明找到了要查找的值,此时返回currentNode

链表-单向链表双向链表循环链表_if语句_11


指定位置插入节点

/*
 方法insertAfter接收两个参数, 第一个value是要在那个节点的后面插入,接收的是节点的value值,第二个newValue接收新节点的值。
 在insertAfter方法中先创建一个新节点的对象,然后调用findNode方法找到insertAfter方法接收的第一个值value所在的节点。
 然后把新节点的next指向value所在节点的next,value所在的节点的next指向新节点(注意:要先将新节点的next指向value所在节点的next,然后才能将value节点的next指向新节点,要不然不行报错的)
 这样新的节点就插入到链表当中了,其实就是新的节点指向之前位置节点的指向,之前的节点指向新的节点。
 */
insertAfter(value,newValue){
     const newNode = new Node(newValue);
     const currentNode = this.findNode(value);
     
     newNode.next = currentNode.next;
     currentNode.next = newNode;
 }

链表-单向链表双向链表循环链表_链表_12

链表-单向链表双向链表循环链表_动画演示_13


在尾部插入节点

/*
 append方法接收一个参数value,作为新节点的值。
 方法中,根据valude然后创建一个新节点(new Node)。
 定义一个临时变量currentNode(局部变量let)来存储当前遍历到的节点(默认为头节点)。
 然后遍历整个链表,找到尾部节点。(在while循环中判断currentNode.next是否为空null)
 也就是说遍历到的节点没有后继节点了,那当前的节点就是尾部节点。
 最后在currentNode(此时已经为尾部节点了)的next指向值指向新节点。
 这样就完成了在尾部插入一个新的节点的操作了。
 */
append(value){
     const newNode = new Node(value);
     let currentNode = this.head;
     while(currentNode.next){
         currentNode = currentNode.next;
     }
     currentNode.next = newNode;
 }

链表-单向链表双向链表循环链表_while循环_14


在头部插入节点

/*
 perpend方法接收一个参数value做为节点的值。
 根据value创建一个新节点对象。
 然后让新节点的head指向现在的头部节点。
 然后让现在的头部节点指向新节点。让现有的head指向新的节点。(不加这一步的话head还是指向之前的head)
 */
perpend(value){                         //perpend方法接收一个参数value做为节点的值。
     const newNode = new Node(value);    //根据value创建一个新节点对象。
     newNode.next = this.head;           //然后让新节点的head指向现在的头部节点。
     this.head = newNode;                //然后让现在的头部节点指向新节点。让现有的head指向新的节点。(不加这一步的话head还是指向之前的head)
 }
perpend(value){
     const newNode = new Node(value);
     newNode.next = this.head;
     this.head = newNode;
 }

链表-单向链表双向链表循环链表_if语句_15


删除指定节点

/*
 remove方法接受一个参数value,要删除的节点值。
 定义两个临时变量:一个保存查找到的要删除的节点;一个保存要删除的节点的前置节点。(因为删除一个节点,要让要删除的节点的前置节点指向要删除的节点的后继节点)
 定义一个临时变量currentNode(局部变量let)来存储当前遍历到的节点(默认为头节点)。
 利用while循环找到要删除的节点,和他的前置节点。(perviousNode在while循环里赋值为了当前节点即为要删除的节点,currentNode在while循环里面赋值为要删除的节点的next)
 加个if判断:如果删除的是头节点,那么就让head直接指向他后面的节点就可以了。
 如果不是头节点,那么就让要删除的节点的前置节点指向要删除的节点的后继节点。
 */
remove(value){ //remove方法接受一个参数value,要删除的节点值。
     let currentNode = this.head;//定义一个临时变量currentNode(局部变量let)来存储当前遍历到的节点(默认为头节点)。
     let perviousNode = null;//定义两个临时变量:一个保存查找到的要删除的节点;一个保存要删除的节点的前置节点。(因为删除一个节点,要让要删除的节点的前置节点指向要删除的节点的后继节点)
     
     while(currentNode.value != value){
         perviousnode = currentNode;
         currentNode = currentNode.next;
     }
     //利用while循环找到要删除的节点,和他的前置节点。(perviousNode在while循环里赋值为了当前节点即为要删除的节点,currentNode在while循环里面赋值为要删除的节点的next)
     
     if(currentNode == this.head){
         this.head = currentNode.next;
     }else{
         perviousNode.next = currentNode.next;
     }
     //加个if判断:如果删除的是头节点,那么就让head直接指向他后面的节点就可以了。如果不是头节点,那么就让要删除的节点的前置节点指向要删除的节点的后继节点。
 }
remove(value){
     let currentNode = this.head;
     let perviousNode = null;
     
     while(currentNode.value != value){
         perviousnode = currentNode;
         currentNode = currentNode.next;
     }
     
     if(currentNode == this.head){
         this.head = currentNode.next;
     }else{
         perviousNode.next = currentNode.next;
     }
 }

链表-单向链表双向链表循环链表_动画演示_16


删除头部节点

明天写太困了

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月22日 0

暂无评论

xWYnr39PTA9E
最新推荐 更多