leetcode算法学习-链表

138 阅读3分钟

1.链表定义

链表是一组节点组成的集合,每个节点都使用一个对象的引用来指向它的后一个节点。指向另一节点的引用讲做链。 链表与数组都属于物理结构。

2.链表操作

1.查询

通过循环一直查找每一个元素的next,知道内容匹配。

2.插入

1.尾部插入
直接把最后一个node 的next指向 新元素。

2.头部插入 直接把新元素插的next 指向 head的元素。

3.中间插入 现在新节点的next指向插入节点,然后把插入节点的上一个指向新节点。

3.删除

与插入逻辑类似。

3.代码实现

class Node {
    constructor(element) {
        this.element = element
        this.next = null
    }
}
class LinkNodeList {
    constructor() {
        this.length = 0
        this.head = null
    }
    append(element) {
        let node = new Node(element)
        if (this.head === null) {
            this.head = node
        }else {
            let cur = this.head
            while(cur.next) {
                cur = cur.next
            }
            cur.next = node
        }
        this.length++
        console.log("this.length",this.length)
    }
    print() {
        let cur = this.head
        let arr = []
        while(cur) {
            arr.push(cur.element)
            cur = cur.next
        }
        console.log(arr.join("=>"))
    }
    getHead() {
        // console.log(this.head)
        return this.head
    }
    deleteAt(index) {
        let cur = this.head
        let i = 0
        let prev = null
        if (index == 0) {
            this.head = cur.next
            
        }else {
            while(i < index) {
                prev = cur
                cur = cur.next
                i++
            }
            prev.next = cur.next
            cur.next = null
        }
        this.length--
        return cur.element
    }
}

let linkNodeList = new LinkNodeList()
linkNodeList.append("111")
linkNodeList.append("222")
linkNodeList.append("333")
console.log(linkNodeList.deleteAt(0)) 
console.log(linkNodeList.length)
linkNodeList.print()

4.leetcode刷题

203. 移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。 示例 1:

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例 2:

输入:head = [], val = 1
输出:[]

答题

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} val
 * @return {ListNode}
 */
//  [1,2,6,3,4,5,6]  6 
// [1,2,3,4,5]
// 使用哨兵 + while 
// 时间复杂度O(N),空间复杂度O(1)
var removeElements = function(head, val) {
    let top = {
        val:"",
        next:head 
    }
    let cur = top
    while (cur.next) {
        if(cur.next.val === val) {
            cur.next = cur.next.next
        } else {
            cur = cur.next
        }
    }
    return top.next
};
//判断head+ while + 递归 
//时间复杂度O(N),空间复杂度O(1)
var removeElements = function(head, val) {
    if(!head) {
        return head
    }
    if (head.val == val) {
        head = head.next 
        return removeElements(head,val)
    }else {
        let cur = head
        while(cur.next) { 
            if (cur.next.val === val) {
                cur.next = cur.next.next
            }else{
                cur = cur.next
            }
        }
        return head
    }
};

206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 示例 1:

输入: head = [1,2,3,4,5]
输出: [5,4,3,2,1]

示例 2:

输入: head = [1,2]
输出: [2,1]

答题:

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
 //解构版本-优化
 // 时间复杂度O(N),空间复杂度O(1)
  var reverseList = function(head) {
    let cur = head
    let prev = null
    while (cur) {
        [cur.next,prev,cur] = [prev,cur,cur.next]
    }
    return prev
};
 
 //正常while 反向prev
 // 时间复杂度O(N),空间复杂度O(n)
 var reverseList = function(head) {
    let cur = head
    let prev = null
    while (cur) {
       let next = cur.next
       cur.next = prev
       prev = cur
       cur = next
    }
    return prev
};

141. 环形链表

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

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

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

示例 1:

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

示例 2:

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

答题

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @return {boolean}
 */
 
 //快慢指针 最优
 // 时间O(N),空间 O(0)
 // 注意 快慢指针如果有环一定会相遇,
 // 因为慢一直往前走一格,快走两个格,两者算下来,快的每次只是递进1格。
 // 要么差1格时候,slow=5,fast=4, 再循环后是 slow=6,fast=6刚好相遇
 // 要么差2格时候,slow=5,fast=3, 再循环后是 slow=6,fast=5刚好相遇,变回上面一格的情况所以还是相遇。
var hasCycle = function(head) {
    let slow = head
    let fast = head
    while (fast && fast.next) {
        slow = slow.next
        fast = fast.next.next 
        if(slow === fast) {
            return true
        }
    }
    return false
};
 
// while + Set 判断
//时间O(N),空间 O(N)
 var hasCycle = function(head) {
    const cache = new Set()
    let cur = head
    while (cur) {
        if (cache.has(cur)) {
            return true
        } else {
            cache.add(cur) 
        }
        cur = cur.next
    }
    return false
};

// while i++ 循环
//时间O(N2),空间 O(1)
var hasCycle = function(head) {
    let i = 0
    let cur = head
    while (cur) {
        if(i > 10000) {
            return true
        }
        cur = cur.next
        i++
    }
    return false
};

//日期循环
//时间O(N2),空间 O(1)
var hasCycle = function(head) {
    let cur = head
    let now = new Date().getTime()
    while (cur) {
        if( (new Date().getTime()) > now + 100) {
            return true
        }
        cur = cur.next
    }
    return false
};

142. 环形链表 II

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

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

不允许修改 链表。 示例 1:

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

示例 2:

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

问题分析

image.png

image.png

已知:slow和fast可以相遇,在K点相遇,求X点的位置?

fast的速度是slow的2倍,两者使用的时间相同
距离 = 速度 * 时间 s = v * t ,
得出相等公式 S(slow) * 2 = S(fast) 

由于slow走到K点时,S(slow) = a + b  
fast 走到K点时,S(fast) = a + b + c + b
最终的到公式   (a+b) * 2 = a + b + c + b , 得出 a = c
等价于 从0到X点 = K点到X点\

结论

相当于slow和fast相遇后,slow 继续走只要与从新开始的start相遇, 如果判断是相等,那当前的slow的下标就是X的下标。

答题

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

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
 //使用快慢指针+  ( start 与 slow 交集)
 // 时间 O(N),空间  O(1)
var detectCycle = function(head) {
    let slow = head
    let fast = head
    let start = head
    while (fast && fast.next) {
        slow = slow.next
        fast = fast.next.next
        if(slow === fast) {//当相遇的时候 再把start 和 slow 一起转,直到相遇,此时的slow就是出现环的下标
            while (slow && start) {
                if(slow === start) {
                    return slow
                }
                start = start.next 
                slow = slow.next
            }
        }
    }
    return null
};
 
//使用Set
 // 时间 O(N),空间  O(N)
 var detectCycle = function(head) {
    let cache = new Set()
    while (head) {
        if (cache.has(head)) {
           return head
        } else {
           cache.add(head)
        }
        head = head.next
    }
    return null
}