leetcode——双指针(新)

93 阅读5分钟

theme: smartblue

141. 环形链表

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

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

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

image.png

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

方法一:map

var hasCycle = function(head) {
    let map = new Map();
    while(head){
        if(!map.has(head)){
            map.set(head, true)
        }else{
            return true;
        }
        head = head.next;
    }
    return false
};

方法二:快慢指针

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
};

142. 环形链表 II

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

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

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

这道题和上面的区别就是:这题要求的是入环的第一个节点!!!我们设置的快慢指针不是相遇的节点不一定是在入环的第一个节点。
方法一:map

var detectCycle = function(head) {
    let map = new Map();
    while(head){
        if(map.has(head)){
            return head;
        }else{
            map.set(head,true)
        }
        head = head.next
    }
    return null
};

方法二:快慢指针

var detectCycle = function(head) {
    if (head === null) {
        return null;
    }
    let fast = head;
    let slow = head;
    while(fast && fast.next){
        slow = slow.next;
        fast = fast.next.next;
        if(slow == fast){
            fast = head;
          	//快指针指向头节点,然后每次快慢指针各走一步直到相遇,相遇的节点就是入环节点
            while (fast !== slow) {
                fast = fast.next;
                slow = slow.next;
            }
            return fast;
        }
    }
    return null
};

原理:

  • 假设从起点到入环节点的距离为 L
  • 环的周长为 C
  • 两指针相遇时相对于头节点的距离为 X image.png 设fast 的速度为 2 slow 的速度为 1
    两指针同时出发 fast指针行驶的距离应该为slow指针的两倍
    L+X+nc = 2(L+X)
    推导一下 L = C-X +(N-1)C
    所以俩个指针,一个从起点出发,一个从相遇点出发(走的路程相同),速度都是1。

15. 三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 ab,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
输入: nums = [-1,0,1,2,-1,-4]
输出: [[-1,-1,2],[-1,0,1]]
var threeSum = function(nums) {
    let len = nums.length;
    let res = [];
    if(len < 3 || nums === null){return res}
    // 先对nums排序 时间复杂度nlogn
    nums.sort((a,b) => a-b);
    for(let i =0; i < len;i++){
        //如果nums[i]>0,最小的数字都>0,没必要再往下找了
        if(nums[i] > 0) break;
        // 我们是先确定最小的数,如果这个最小数已经出现过了没必要再算一遍
        // -4 -1 -1 0 1 2
        if(i > 0 && nums[i-1] === nums[i]) continue;
        // 开始确定第二第三个数
        let left = i+1, right = len - 1;
        while(left < right){
            const sum = nums[i] + nums[left] + nums[right];
            if(sum === 0){
                res.push([nums[i],nums[left],nums[right]]);
                //中间数字去重  -1 1 1 0
                while(left < right && nums[left] === nums[left+1]){left++} 
                //后面数字去重
                while(left < right && nums[right] === nums[right-1]){right--}
                left++;
                right--;
            }else if(sum < 0){
                left++;
            }else{
                right--;
            }
        }
    }
    return res
};

11. 盛最多水的容器

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明:你不能倾斜容器。

image.png

输入:[1,8,6,2,5,4,8,3,7]
输出:49 
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49

image.png

var maxArea = function(height) {
    let max = 0;
    for(let i = 0, j = height.length - 1; i < j;){
        let minHeight = height[i] < height[j] ? height[i++] : height[j--];
        let res = (j - i + 1) * minHeight;
        max = Math.max(res,max)
    }
    return max
};

160. 相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

图示两个链表在节点 c1 开始相交:

image.png

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A[4,1,8,4,5],链表 B[5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
var getIntersectionNode = function(headA, headB) {
    let pA = headA;
    let pB = headB;
    while(pA !== pB){
        pA = pA === null ? headB : pA.next;
        pB = pB === null ? headA : pB.next
    }
    return pA
};

876. 链表的中间结点

给定一个头结点为 `head` 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
var middleNode = function(head) {
    let slow = head;
    let fast = head;
    while(fast && fast.next){
        slow = slow.next;
        fast = fast.next.next
    }
    return slow
};