链表:特点、操作和实战解析

200 阅读4分钟

链表特点

1. 有序的列表

2. 线性的结构

3. 所有的结点都是离散分布的

如图:

image.png

咱把结点单独拿出来,状态是这样的:

image.png

  • 表里有数字的为val,是结点当中的值

  • 有箭头的为next,是指向下一个结点的指针 **

4. 访问链表中任何一个结点,都要从头结点开始逐个查找** 这个我们看链表在JavaScript中的表示:

let lb = {
  val: 1,
  next: {
    val: 2,
    next: {
      val: 3,
      next: {
        val: 4,
        next: nul
      }
    }
  }
}

跟洋葱一样一层一层的

  • 插入一个结点

当我们想在结点3,4中添加一个结点6,步骤应该为:

1.删除3指向4的指针

2.让3的指针指向6

3.让6的指针指向4

image.png

用代码表示为:

function ListNode(val) {
  this.val = val
  this.next = null
}//定义链表

const node = new ListNode(1)
const node2 = new ListNode(2)
const node3 = new ListNode(3)
const node4 = new ListNode(4)
const node5 = new ListNode(5)//创建结点

//添加结点
const node6 = new ListNode(6)
node6.next = node4
node3.next = node6
  • 移除一个节点

比如我们想删掉结点2,步骤为:

1.删掉指向2的指针,也就是1的next

2.把原本指向2的指针(1的next)指向2之后的结点3

node.next = node2
node2.next = node3
node.next = node3

代码为:

  • 头部增加节点:

创建一个节点,新创建的结点指针指向链表第一位

  • 尾部增加节点:

一路找,找到链表尾部的结点然后再让它的指针指向这个新结点

  • 遍历数组查值:
const index = 10
let node = head
for (let i = 0; i < index && node; i++) {
  node = node.next
}

链表VS数组

数组

数组在绝大多数计算机语言中,数组都是一段连续的内存,如果我们想要移除一个元素的话,增删元素就会变得很麻烦。随着数组的长度(n)增加,增删操作所带来的需要移动的元素个数也会随之增加。(可以想象一下打麻将时候的摸牌和出牌)

这个操作的时间复杂度为O(n)

链表

对于增删操作,链表就显得简单得多,它甚至没有时间复杂度(为O(1)),只需要改一下对应指针就可以了。

所以我们可以总结出:

5. 相比数组,数组中增加或者删除一个元素,会导致需要移动n个元素,时间复杂度为O(n)。链表的增加和删除元素不需要移动其他元素O(1)

6. 链表查找元素的时间复杂度为O(n),而数组查找元素的时间复杂度为O(1)

实战

打开力扣咱们来写题目

21. 合并两个有序链表 - 力扣(LeetCode)

image.png

代码:

var mergeTwoLists = function (list1, list2) {
  let head = new ListNode()
  let cur = head //保留head结点

  while (list1 && list2) {//两链表都有值
    //比较节点的值
    if (list1.val <= list2.val) {
      //list1的值小于list2
      cur.next = list1//头结点用list1的
      list1 = list1.next//list1 现在从第二个结点开始
    } else {//list2更小
      cur.next = list2//头结点用list2的
      list2 = list2.next
    }
    cur = cur.next //每循环一次,cur都要代表下一个结点,cur最后代表的是结尾的结点
  }
  
  if(list1){//最后两个链表可能会有一个链表还没有合并到,还有的结点没走到
      cur.next = list1//因为链表是有序的,后面的结点不用再判断了
  }else{
    cur.next = list2
  }//这段可以用三元运算符写成cur.next = list1 !== null ? list1 : list2
  return head.next
};

LCR 136. 删除链表的节点 - 力扣(LeetCode)

image.png

var deleteNode = function(head, val) {
   let cur = head;
  if (cur.val === val) {//要移除的是头结点
    head = head.next
  }
  while (cur && cur.next) {
    if (cur.next.val === val) {
      // cur就是要被移除的节点
      cur.next = cur.next.next;
    } 
      // 当前节点的下一个节点与当前节点的值不相等,继续向后遍历
      cur = cur.next;
    
  }
  return head;
};

83. 删除排序链表中的重复元素 - 力扣(LeetCode)

image.png

var deleteDuplicates = function(head) {
   let cur = head;

  while (cur && cur.next) {//有值
    if (cur.val === cur.next.val) {//有重复的元素
      cur.next = cur.next.next; //更改指针
    } else {
      cur = cur.next;//没有重复值咱就继续看下一个
    }
  }

希望本文对读者理解链表的特点、操作方法以及实际应用有所帮助!如果有任何其他问题,欢迎随时提问。