leetcode系列第二弹 💥「141 - 环形链表 」 🥒

225 阅读3分钟

hi, 我是小黄瓜没有刺。一枚菜鸟瓜🥒,期待关注➕点赞,共同成长~

接下来会进行链表系列的题目,数量未知😁 ~

前置知识

1. 什么是链表?

链表是一种数据结构,链表中的每个节点至少包含两个部分:数据域指针域。其中数据域用来存储数据,而指针域用来存储指向下一个数据的地址,如下图所示:

因为js中并没有链表这种数据结构,所以我们用类来实现它:

首先定义一个Node

class Node{
    constructor(val) {
        // 数据域
        this.val = val
        // 指针域
        this.next = null
    }
}

接下来实现添加和打印功能:

class LinkNodeList{
    constructor() {
        this.head = null
        this.length = 0
    }
    // 添加节点
    append(val) {
        // 使用数据创建一个节点node
        let node = new Node(val)
        let p = this.head
        if(this.head) {
            // 找到链表的最后一个节点,把这个节点的.next属性赋值为node
            while(p.next) {
                p = p.next
            }
            p.next = node
        } else {
            // 如果没有head节点,则代表链表为空,直接将node设置为头节点
            this.head = node
        }
        this.length++
    }
    // 打印链表
    print() {
        if(this.head) {
            let p = this.head
            let ret = ''

            do{
                ret += `${p.val} --> `
                p = p.next
            }while(p.next)
            ret += p.val
        } else {
            console.log('empty')
        }
    }
}

let linkList = new LinkNodeList()

linkList.append(1)
linkList.append(2)
linkList.append(3)
linkList.append(4)

linkList.print() // 1 --> 2 --> 3 --> 4

console.log(linkList.length) // 4

链表这种结构与数组的区别是啥?

首先数组和链表是两种不同的数据结构,数组访问某个成员可以直接通过Array[index]的方式直接进行访问,而链表需要遍历每个成员的指针域来进行查找。其次链表的插入速度要更快,直接将指针域指向要插入的节点,然后将新节点的指针域指向下一个成员即可,而数组还需要考虑到index下标移动以及length属性的因素。

题目

141. 环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

  • 链表中节点的数目范围是 [0, 104]
  • -105 <= Node.val <= 105
  • pos 为 -1 或者链表中的一个 有效索引 。

思路

方式一:

根据题目已知环形链表是肯定会有相交的节点,所以我们可以定义两个快慢指针,快指针比慢指针永远快走一步,如果是环形链表,那么终究会相交,如果不会遍历完整个链表依然没有找到相交节点,说明不是环形链表。

/**
* @param  {ListNode} head 
* @return  {boolean} 
*/
var hasCycle = function(head) {
  // 判断head节点是否为空,为空直接返回false
  if(!head) return false
  // 定义快慢指针
  let pre = head, cur = head
    while(cur && cur.next) {
      // 快慢指针同时出发,快指针先走一步
      pre = pre.next
      cur = cur.next.next
      
      // 如果快指针与慢指针相等,说明两者相交
      if(pre === cur) {
      return true
    }
  }
  return false
};

方式二:

设置一个缓存对象,每次遍历到一个对象就将其缓存起来,如果查找到相同的值,则说明为环形链表。

// 方式二
/**
* @param  {ListNode} head 
* @return  {boolean} 
*/
var hasCycle = function(head) {
  // 设置缓存对象
  let cache = new Set()
  while(head) {
    // 如果节点已存在于cache中,说明为环形链表
    if(cache.has(head)) {
      return true
    } else {
      cache.add(head)
    }
    // 遍历
    head = head.next
  }
  return false
};

写在最后

目前的打算是leetcode系列会一直更新下去,未来可能会更新实现mini-vue3js基础知识系列,希望能一直坚持下去,期待多多点赞🤗🤗,一起进步!🥳🥳