Loading

刷题复习(一)链表-双指针

刷题复习(一)链表-双指针

https://labuladong.gitee.io/algo/di-ling-zh-bfe1b/shuang-zhi-0f7cc/

1、合并两个有序链表

思路清晰,双链表有个根节点记录开头


/**
 * Definition for singly-linked list.
 * public class ListNode {
 * int val;
 * ListNode next;
 * ListNode() {}
 * ListNode(int val) { this.val = val; }
 * ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode root = new ListNode();
        ListNode temp = root;
        while (list1 != null && list2 != null) {
            if (list1.val > list2.val) {
                temp.next = list2;
                list2 = list2.next;
            } else {
                temp.next = list1;
                list1 = list1.next;
            }
            temp = temp.next;
        }
        if (list1 == null) {
            temp.next = list2;
        } else {
            temp.next = list1;
        }
        return root.next;
    }
}

2、分隔链表

这题需要将链表断开,需要记录表头和表尾


/**
 * Definition for singly-linked list.
 * public class ListNode {
 * int val;
 * ListNode next;
 * ListNode() {}
 * ListNode(int val) { this.val = val; }
 * ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    //输入:head = [1,4,3,2,5,2], x = 3
    //输出:[1,2,2,4,3,5]
    //示例 2:
    //
    //输入:head = [2,1], x = 2
    //输出:[1,2]

    public ListNode partition(ListNode head, int x) {
        ListNode temp1 = new ListNode();
        ListNode temp2 = new ListNode();
        ListNode root1 = temp1;
        ListNode root2 = temp2;
        while (head != null) {
            if (head.val < x) {
                temp1.next = head;
                temp1 = temp1.next;
            } else {
                temp2.next = head;
                temp2 = temp2.next;
            }
            head = head.next;
        }
        temp1.next = root2.next;
        temp2.next = null;
        return root1.next;
    }

}

3、合并K个升序链表

不难的hard,优先队列 PriorityQueu要学会使用,用优先队列就不难了

image-20231126220339155

        PriorityQueue<ListNode> pq = new PriorityQueue<>(lists.length,(o1,o2)->(o1.val-o2.val));

通过lambda方式构造小顶堆,默认就是小顶堆

image-20231126220643061

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if(lists== null ||lists.length== 0){
            return null;
        }
        ListNode root = new ListNode(0);
        ListNode temp = root;
        PriorityQueue<ListNode> pq = new PriorityQueue<>(lists.length,(o1,o2)->(o1.val-o2.val));
        for(ListNode node: lists){
            if(node != null){
                pq.add(node);
            }
        }
        while(!pq.isEmpty()){
            ListNode cur = pq.poll();
            temp.next = cur;
            if(cur.next!=null){
                pq.add(cur.next);
            }
            temp = temp.next;
        }
        return root.next;
    }
}

4、删除链表的倒数第 N 个结点

这道题目有技巧,需要寻找倒数第k个节点

代码应该是如下

// 返回链表的倒数第 k 个节点
ListNode findFromEnd(ListNode head, int k) {
    ListNode p1 = head;
    // p1 先走 k 步
    for (int i = 0; i < k; i++) {
        p1 = p1.next;
    }
    ListNode p2 = head;
    // p1 和 p2 同时走 n - k 步
    while (p1 != null) {
        p2 = p2.next;
        p1 = p1.next;
    }
    // p2 现在指向第 n - k + 1 个节点,即倒数第 k 个节点
    return p2;
}

完整解答,尤其需要注意测试用例会出现k个数删除倒数第k个,由于算法是寻找倒数k+1的所以需要用虚拟节点root节点去防止出现这种情况

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode root = new ListNode(0);
        root.next= head;
        ListNode cur=help(root,n+1);
        cur.next = cur.next.next;
        return root.next;
    }

    public ListNode help(ListNode head ,int n){
        ListNode temp1 = head;
        for(int i=0;i<n;i++){
            temp1= temp1.next;
        }
        ListNode temp2 = head;
        while(temp1!=null){
            temp2=temp2.next;
            temp1=temp1.next;
        }
        return temp2;
    }
}

5、链表的中间结点

快慢指针可以轻松解决,注意增加fast.next不为空的判断

class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }
}

这个代码可以改成链表是否有环,有环的判断是快慢指针相遇

如果 fast 最终遇到空指针,说明链表中没有环;如果 fast 最终和 slow 相遇,那肯定是 fast 超过了 slow 一圈,说明链表中含有环。

boolean hasCycle(ListNode head) {
    // 快慢指针初始化指向 head
    ListNode slow = head, fast = head;
    // 快指针走到末尾时停止
    while (fast != null && fast.next != null) {
        // 慢指针走一步,快指针走两步
        slow = slow.next;
        fast = fast.next.next;
        // 快慢指针相遇,说明含有环
        if (slow == fast) {
            return true;
        }
    }
    // 不包含环
    return false;
}

6、环形链表 II

经典,先让俩个指针相交,再改变指向同时运动

class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast, slow;
        fast = slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) break;
        }
        // 上面的代码类似 hasCycle 函数
        if (fast == null || fast.next == null) {
            // fast 遇到空指针说明没有环
            return null;
        }

        // 重新指向头结点
        slow = head;
        // 快慢指针同步前进,相交点就是环起点
        while (slow != fast) {
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
}

7、相交链表

我们可以让 p1 遍历完链表 A 之后开始遍历链表 B,让 p2 遍历完链表 B 之后开始遍历链表 A,这样相当于「逻辑上」两条链表接在了一起。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode tempA=headA;
        ListNode tempB=headB;
        while(tempA!= tempB){
            if(tempA==null){
                tempA=headB;
            }else{
                tempA =tempA.next;
            }
            if(tempB==null){
                tempB=headA;
            }else{
                tempB =tempB.next;
            }
        }
        return tempA;
    }
}
ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    // p1 指向 A 链表头结点,p2 指向 B 链表头结点
    ListNode p1 = headA, p2 = headB;
    while (p1 != p2) {
        // p1 走一步,如果走到 A 链表末尾,转到 B 链表
        if (p1 == null) p1 = headB;
        else            p1 = p1.next;
        // p2 走一步,如果走到 B 链表末尾,转到 A 链表
        if (p2 == null) p2 = headA;
        else            p2 = p2.next;
    }
    return p1;
}

posted @ 2023-11-28 14:03  kopoo  阅读(6)  评论(0编辑  收藏  举报