【la】单链表的六⼤题型

这篇博客介绍了如何处理链表数据结构的各种操作,包括合并两个有序链表、合并k个有序链表、寻找单链表的倒数第k个节点、寻找中点、判断是否存在环并找到环的起点,以及判断两个链表是否相交。这些操作都涉及到链表的遍历和比较策略,如使用快慢指针和优先队列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、合并两个有序链表
2、合并 k 个有序链表
3、寻找单链表的倒数第 k 个节点
4、寻找单链表的中点
5、判断单链表是否包含环并找出环起点
6、判断两个单链表是否相交并找出交点

21. 合并两个有序链表


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

示例 1:

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-two-sorted-lists
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 

/**
 * 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) {

        if (list1 == null){
            return list2;
        }else if (list2 == null){
            return list1;
        }else if (list1 == null && list2 == null){
            return null;
        }
        ListNode head,p1,p2,p;
        if (list1.val < list2.val){
            head = list1;
            p1 = head.next;
            p2 = list2;
        }else {
            head = list2;
            p2 = head.next;
            p1 = list1;
        }
        p = head;
        while (p1 != null && p2 != null){
            if (p1.val < p2.val){
                p.next = p1;
                p1 = p1.next;
            }else {
                p.next = p2;
                p2 = p2.next;
            }
            p = p.next;
        }
        if (p1 != null){
            p.next = p1;
        }
        if (p2 != null){
            p.next = p2;
        }
        return head;

    }
}

23. 合并K个升序链表

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

 

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2:

输入:lists = []
输出:[]

示例 3:

输入:lists = [[]]
输出:[]

 

提示:

    k == lists.length
    0 <= k <= 10^4
    0 <= lists[i].length <= 500
    -10^4 <= lists[i][j] <= 10^4
    lists[i] 按 升序 排列
    lists[i].length 的总和不超过 10^4

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-k-sorted-lists
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


/**
 * 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) return null;
        // 1. 创建优先队列
        PriorityQueue<ListNode> queue = new PriorityQueue<>((l1, l2) -> {
            return l1.val - l2.val;
        });

        // 2. 遍历 所有链表数组节点 入 queue
        ListNode p = null;
        for (ListNode node:lists){
            // node ,拿到每一个数组的头节点
            p = node;
            while (p!=null){
                // 入队
                queue.offer(p);
                p = p.next;
            }
        }
        // 3. 出队,拿到升序链表
        ListNode head = new ListNode(-1); // 虚头节点 ,返回的升序链表的头节点是 head.next
        p = head;

        while (!queue.isEmpty()){
             // 出队
            p.next = queue.poll();
            p = p.next;
        }

        p.next = null; 
        return head.next;

    }
}

PriorityQueue,具有优先级别的队列,优先级队列的元素按照它们的自然顺序排序,或者由队列构造时提供的 Comparator 进行排序这取决于哪个构造器。

构造函数描述
PriorityQueue()使用默认的容量(11) 创建一个优先队列,元素的顺序规则采用的是自然顺序
PriorityQueue(int initialCapacity)使用默认指定的容量创建一个优先队列,元素的顺序规则采用的是自然顺序
PriorityQueue(Comparator<? super E> comparator)使用默认的容量(11) 创建一个优先队列,元素的顺序规则采用的是 comparator

19. 删除链表的倒数第 N 个结点


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

示例 1:

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

示例 2:

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

示例 3:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

寻找倒数第 k 个节点:
在这里插入图片描述

注意对虚拟头结点的使用,会减少很多边界问题。
使⽤了虚拟头结点的技巧,也是为了防⽌出现空指针的情况,⽐如说链表总共有 5 个节点,
题⽬就让你删除倒数第 5 个节点,也就是第⼀个节点,那按照算法逻辑,应该⾸先找到倒数第 6 个节点。但
第⼀个节点前⾯已经没有节点了,这就会出错。


/**
 * 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) {

        // 1. 创建 虚头节点
        ListNode dummy = new ListNode(-1);
        dummy.next = head;

        // 2. 寻找倒数 第 n+1 个节点
        ListNode node = findFromEnd(dummy, n + 1);
        // 3. 删除 第 n+1 个节点的下一个节点
        node.next = node.next.next;

        return dummy.next;

    }

    /**
     * 返回链表的倒数第k个节点
     * @param head
     * @param k
     * @return
     */
    public ListNode findFromEnd(ListNode head, int k){

        ListNode p1 = head,p2 = head;

        // 1. p1 走 k 步
        for (int i = 0;i < k;i++){
            p1 = p1.next;
        }

        // 2. p2 和 p1 一起走,直到 p1 指向空
        while (p1 != null){
            p1 = p1.next;
            p2 = p2.next;
        }

        // 此时 p2 就指向 倒数第k个数
        return p2;
    }
}

876. 链表的中间结点


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

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

示例 1:

输入:[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.

示例 2:

输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 34,我们返回第二个结点。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/middle-of-the-linked-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方法:使用快慢指针,快指针 fast 走两步,slow 慢指针 走一步。
如果链表⻓度为偶数,也就是说中点有两个的时候,返回的节点是靠后的那个节点。


/**
 * 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 middleNode(ListNode head) {

        ListNode slow = head,fast = head;
        while (fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }
}

判断链表是否包含环

方法:快慢指针
每当慢指针 slow 前进⼀步,快指针 fast 就前进两步。
如果 fast 最终遇到空指针,说明链表中没有环;如果 fast 最终和 slow 相遇,那肯定是 fast 超过了
slow ⼀圈,说明链表中含有环。

分析:假设链表是一个环,有 n 个节点,慢指针 一次走1步,快指针 一次走 2步,假设快慢指针 x 次后相遇,(2x-x)%n = 0, x % n = 0,x 最小值 取 n,如果有环快慢指针经过 x 次后一定会相遇。(有问题)

public ListNode middleNode(ListNode head) {

        ListNode slow = head,fast = head;
        while (fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if (slow == fast) { // 快慢指针相遇,说明含有环
							return true;
					 }
        }
        return false;
    }

141. 环形链表


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

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

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

示例 1:

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

示例 2:

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

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/linked-list-cycle
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

例1图:
在这里插入图片描述
例2图:
在这里插入图片描述
例3图:
在这里插入图片描述


public class Solution {
    public boolean hasCycle(ListNode head) {
         ListNode slow = head,fast = head;

        while (fast != null && fast.next != null){
            // 慢指针走一步,快指针走两步
            fast  = fast.next.next;
            slow = slow.next;
            // 快慢指针 相遇,包含环
            if (slow == fast){
                return true;
            }
        }
        return false;
    }
}

已知链表中含有环,返回这个环的起始位置

在这里插入图片描述前面一题 判断链表是否包含环,快慢指针相遇后,返回 true,在本题 求环的起点 问题中,快慢指针相遇 不需要返回,退出 while 循环,让 慢指针 重新指向 head,然后 快慢指针 同时一次走一步,再次相遇时的点就是 环的起点。


ListNode cycleStart(ListNode head){
        
        ListNode slow = head,fast = head;
        
        while (fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
            if (fast == slow){
                break;
            }
        }
        
        slow = head;
        while (slow != fast){
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }
    

142. 环形链表 II


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

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

不允许修改 链表。

示例 1:

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

示例 2:

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

示例 3:

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/linked-list-cycle-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

图:
1
在这里插入图片描述2
在这里插入图片描述
3
在这里插入图片描述


public class Solution {
    public ListNode detectCycle(ListNode head) {

        ListNode slow = head,fast = head;
        int k = 0;
        while (fast != null && fast.next != null){
            // 慢指针走一步,快指针走两步
            fast  = fast.next.next;
            slow = slow.next;
            // 快慢指针 相遇,包含环
            if (slow == fast){
                k = 1; // 有环的标志
                break;
            }
        }
        if (k == 0){
            // 没有环
            return null;
        }else {
            // 寻找环的起点 并返回
            slow = head;
            while (slow != fast){
                slow = slow.next;
                fast = fast.next;
            }
            return slow;
        }
    }
}

两个链表是否相交

在这里插入图片描述解决办法:
用 p1,p2 分别遍历 A,B 。
p1 遍历完链表 A 之后开始遍历链表 B,让 p2 遍历完链表 B 之后开始遍历链表 A
请添加图片描述在这里插入图片描述p1,p2 相等时就到达相交节点c1。
如果A,B,没有交点,要返回 null,上述方法同样适用。
在这里插入图片描述

160. 相交链表


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

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/intersection-of-two-linked-lists
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

在这里插入图片描述


public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {

        ListNode p1 = headA,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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值