链表双指针技巧

125 阅读4分钟

21. 合并两个有序列表

将两个升序链表合并为一个新的 **升序** 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

// 双指针操作
var mergeTwoLists = function(list1, list2){
    let dummy = new ListNode();
    let p = dummy;
    while(list1 && list2) {
        if (list1.val > list2.val) {
            p.next = list2;
            list2 = list2.next;
        } else {
            p.next = list1;
            list1 = list1.next;
        }
        p = p.next;
    }
    if (list1 != null) {
        p.next = list1
    }
    if (list2 != null) {
        p.next = list2
    }
    return dummy.next
}

86. 分隔链表

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
你应当 保留 两个分区中每个节点的初始相对位置。

// 双指针
// 定义两个空链表,分别存放大于x,和小于x的值,最后再将两个节点连接起来
var partition = function(head, x) {
    let dummy1 = new ListNode();
    let dummy2 = new ListNode();
    let p1 = dummy1;
    let p2 = dummy2;
    let p = head;
    while(p) {
        if (p.val < x) {
            p1.next = p;
            p1 = p1.next;
        } else {
            p2.next = p;
            p2 = p2.next;
        }
        let temp = p.next;
        p.next = null;
        p = temp
    }
    p1.next = dummy2.next;
    return dummy1.next
}


22. 链表中的倒数第k个节点

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 123456。这个链表的倒数第 3 个节点是值为 4 的节点。

// 链表有n个节点,倒数第k个节点正好就是正数第n-k+1个节点
// 定义一个p1指针指向链表头节点,然后走k步,p1再走n-k步就能走到链表结尾
// 定义一个p2指针指向链表头节点
// 让p1和p2同时向前走,p1走到链表末尾,前进了n-k步,p2也走了n-k步,停留在第n-k+1个节点上,刚好就是链表的倒数第k个节点


var getKthFromEnd = function(head, k) {
    let p1 = head;
    for (let i = 0; i < k; i++) {
        p1 = p1.next;
    }
    let p2 = head;
    while(p1) {
        p1 = p1.next;
        p2 = p2.next;
    }
    return p2
}

19. 删除链表的倒数第n个节点

给你一个链表,删除链表的倒数第 `n` **个结点,并且返回链表的头结点。

// 想要删除链表的倒数第n个节点,首先得找到链表的倒数第n+1个节点
var removeNthFromEnd = function(head, n) {
    let dummy = new ListNode();
    dummy.next = head;
    let p = getKthFromEnd(dummy, n+1);
    p.next = p.next.next;
    return dummy.next
}
// 获取链表的倒数第k个节点
var getKthFromEnd = function(head, k) {
    let p1 = head;
    for (let i = 0; i < k; i++) {
        p1 = p1.next;
    }
    let p2 = head;
    while(p1) {
        p1 = p1.next;
        p2 = p2.next;
    }
    return p2
}

876. 链表的中间节点

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

如果有两个中间结点,则返回第二个中间结点。

var middleNode = function(head) {
    let slow = head;
    let fast = head;
    while(fast != null && fast.next != null) {
        slow = slow.next;
        fast = fast.next.next;
    }
    return slow
}

141. 环形链表

给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false
var hasCycle = function(head) {
    let fast = head;
    let slow = head;
    while(fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
        if (fast == slow) {
            return true
        }
    }
    return false
}


142. 环形链表2

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

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

不允许修改 链表。

var detectCycle = function(head) {
    let fast = head;
    let slow = head;
    while(fast && fast.next) {
        fast = fast.next.next;
        slow = slow.next;
        if (fast == slow) {
            break;
        }
    }
    if (fast == null || fast.next == null) {
        return null;
    }
    slow = head;
    while(slow != head) {
        slow = slow.next;
        fast = fast.next
    }
    return slow
}

160. 相交链表

给你输入两个链表的头结点 `headA` 和 `headB`,这两个链表可能存在相交。

如果相交,你的算法应该返回相交的那个节点;如果没相交,则返回 null
let getIntersectionNode(headA, headB) {
    let p1 = headA;
    let p2 = headB;
    while(p1 != p2) {
        if (p1 == null) {
            p1 = headB;
            
        } else {
            p1 = p1.next;
        }
        if (p2 == null) {
            p2 = headA;
            
        } else {
            p2 = p2.next;
        }
    }
    return p1
}