[路飞]_141. 环形链表

142 阅读3分钟

链表的基础知识

单向链表

链表1.png

分为数据域和指针域

  • 指针域在C语言中是地址
  • 可以存储数组下标,相对地址的概念
  • 引用(js,puthon,java)
  • 在相应的结构中增加一个指针域,就能串成一个链表结构
  • 链表中的每个节点至少包含两个部分:数据域与指针域
  • 链表中的每个节点,通过指针域的值,形成一个线性结构
  • 查找节点O(n),插入节点O(1),删除节点O(1)
  • 不适合快速的定位数据,适合动态的插入和删除数据的应用场景

链表的典型应用场景

场景一:操作系统内部的动态内存分配

链表1.png
场景二:LRU缓存淘汰算法

缓存命中.png

141. 环形链表

题目描述

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

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

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

circularlinkedlist.png

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

js中的!!是什么意思呢

  • js代码中有时会用到"!",这个是取反的意思,对于布尔类型会将其值true和false互换。
  • 对于非布尔类型,js会将值先转换成布尔类型,而后取反。
  • 字符串类型,会将空值("")转换成false,其余转换成true。
  • 数字类型,会将0转换成false,其余为true。
  • null undefined会转换成false。 总结:一个"!"是将对象转为布尔型并取反,两个"!!"是将取反后的布尔值再取反,相当于直接将非布尔值类型转为布尔类型值。

解题思路

1.快慢指针,设置一个慢指针pre,pre指向head,和一个快指针cur,cur指向head.next。pre每次都走一步,cur每次都走两步。如果链表有环,那么在n次追赶后,cur快指针一定能在环内某个节点上追上pre慢指针。

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {boolean}
 */
var hasCycle = function(head) {
    if(!head) return false;
    // 申明一个慢指针pre指向head,快指针cur指向head.next
    let pre = head, cur = head.next;
    // 循环cur,且cur不为null或者cur.next不为null。且cur和pre不相等
    while(cur && cur.next && pre !== cur){
        pre = pre.next;
        cur = cur.next.next;
    }
    // 跳出循环,说明cur为null或者cur === pre
    return pre === cur;
    // 如果返回值是这样去写的话,是会报错的。力扣没处理好
    // return pre && pre.next
    // 可以改为
    // return !!pre && !!pre.next
};

2.ES6的Map数据结构,JS的对象(Object),本质上是键值对的集合(Hash结构),但是传统上只能用字符串当做键。ES6提供了Map数据结构。它类似于对象,也是键值对的集合,但是"键"的范围不限于字符串,各种类型的值(包含对象)都可以当作键。也就是说,Object结构提供了"字符串--值"的对应,Map结构提供了"值--值"的对应,是一种更完善的Hash结构实现。

const m = new Map();
const o = {p: 'Hello World'};

m.set(o, 'content');
m.get(o); // 'content'

m.has(o) // true
m.delete(o) // true
m.has(o); // false
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {boolean}
 */
var hasCycle = function(head) {
    if(!head) return false;
    // 构造一个map对象
    let map = new Map();
    // 遍历链表
    while(head){
        // 判断当前节点是否在map对象里面,在就说明有环,返回true
        if(map.has(head)) return true;
        // 不在就把当前节点set进map对象内
        map.set(head);
        // 当前节点往后走一步
        head = head.next;
    }
    // 跳出循环,说明链表没有环,返回false
    return false;
};