首页 > 其他分享 >刷题复习(一)链表-双指针

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

时间:2023-11-28 14:13:25浏览次数:45  
标签:head ListNode 复习 val fast next 链表 刷题

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

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

标签:head,ListNode,复习,val,fast,next,链表,刷题
From: https://www.cnblogs.com/wusier/p/17861831.html

相关文章

  • (三十)C#编程基础复习——继承
    继承与封装和多态统称为面向对象编程的三大特性,在创建一个新类时,我们可以使用这个新定义的类继承一个已有的类,通过继承可以在创建新类时重用、扩展和修改被继承类中定义的成员。被继承的类称为“基类(父类)”,继承基类的类称为“派生来(子类)”。需要注意的是,C#中只支持单继承,也就是说......
  • (二十九)C#编程基础复习——static静态成员
    在C#中,我们可以使用static关键字声明属于类型本身而不是属于特定对象的静态成员,因此不需要使用对象来访问静态成员。在类、接口和结构体中可以使用static关键字修饰变量、函数、构造函数、类、属性、运算符和事件。注意:索引器和析构函数不能时静态的。若要定义某个成员时使用sta......
  • 前端歌谣的刷题之路-第一百零五题-监听对象
     前言我是歌谣我有个兄弟巅峰的时候排名c站总榜19叫前端小歌谣曾经我花了三年的时间创作了他现在我要用五年的时间超越他今天又是接近兄弟的一天人生难免坎坷大不了从头再来歌谣的意志是永恒的放弃很容易但是坚持一定很酷本题目源自于牛客网微信公众号前端小歌谣题目......
  • (二十八)C#编程基础复习——This关键字
    在C#中,可以使用this关键字来表示当前对象,日常开发中我们可以使用this关键字来访问类中的成员属性以及函数。不仅如此this关键字还有一些其他的用法,示例如下:一、使用this表示当前类的对象namespace_016{internalclassProgram{staticvoidMain(string[]a......
  • 单链表
    classNode{constructor(data){this.data=datathis.next=null}}classNodeList{constructor(){this.head=nullthis.length=0}appendNode(data){constnewNode=newNode(data)......
  • (二十八)C#编程基础复习——析构函数
    特此声明:本教程内容可能有部分参照其他博主的观点或描述,但始终不影响我学习的热情,代码全部自己手工敲打,编辑此教程目的不是为了博取大家眼球,也不是为利益所驱,只是纯属为了方便自己学习,编辑的过程中也让自己加深了对C#各个基础的印象,同时也让自己编码过程更加流畅顺利,最后还能帮助......
  • 链表K个节点的组内逆序调整问题
    链表K个节点的组内逆序调整问题作者:Grey原文地址:博客园:链表K个节点的组内逆序调整问题CSDN:链表K个节点的组内逆序调整问题题目描述LeetCode25.ReverseNodesink-Group本题的followup是:Follow-up:CanyousolvetheprobleminO(1)extramemoryspace?即用\(O(......
  • 前端歌谣的刷题之路-第一百题-控制动画
     前言我是歌谣我有个兄弟巅峰的时候排名c站总榜19叫前端小歌谣曾经我花了三年的时间创作了他现在我要用五年的时间超越他今天又是接近兄弟的一天人生难免坎坷大不了从头再来歌谣的意志是永恒的放弃很容易但是坚持一定很酷本题目源自于牛客网微信公众号前端小歌谣题目......
  • 前端歌谣的刷题之路-第一百零二题-全选
     前言我是歌谣我有个兄弟巅峰的时候排名c站总榜19叫前端小歌谣曾经我花了三年的时间创作了他现在我要用五年的时间超越他今天又是接近兄弟的一天人生难免坎坷大不了从头再来歌谣的意志是永恒的放弃很容易但是坚持一定很酷本题目源自于牛客网微信公众号前端小歌谣题目......
  • 前端歌谣的刷题之路-第一百零三题-回文字符串
     前言我是歌谣我有个兄弟巅峰的时候排名c站总榜19叫前端小歌谣曾经我花了三年的时间创作了他现在我要用五年的时间超越他今天又是接近兄弟的一天人生难免坎坷大不了从头再来歌谣的意志是永恒的放弃很容易但是坚持一定很酷本题目源自于牛客网微信公众号前端小歌谣题目......