最详细,最轻松的力扣(leetcode)hot 100 链表(上)讲解

这里是阳明Coding,这次带来的是关于力扣 hot 100的链表问题的做法。由于这部分内容足足有15道题目,因此按照题目的顺序拆分为上,下两部分来进行讲解。如果这边文章能够帮到你们的话,可以点赞,收藏加关注。

目录

相交链表

反转链表

回文链表

环形链表

环形链表 II

合并两个有序链表

两数相加


相交链表

160. 相交链表

思考方向

假设交点前:A链表长a,B链表长b,公共部分长c。指针A路径:a + c + b。指针B路径:b + c + a。长度相等,必然在交点相遇

因此,我们只需要让两个链表的指针走到末端后,从另外一条链表开始继续仔细行走即可。

代码流程

  1. 初始化:两个指针 pa 和 pb 分别指向两个链表的头节点 headA 和 headB

  2. 同步移动

    • 每次循环,pa 和 pb 各走一步。

    • 如果 pa 走到链表 A 的末尾,则将其重定向到链表 B 的头节点。

    • 如果 pb 走到链表 B 的末尾,则将其重定向到链表 A 的头节点。

  3. 相遇或结束

    • 如果两个链表相交,pa 和 pb 会在交点相遇。

    • 如果两个链表不相交,pa 和 pb 会同时走到 null,循环结束并返回 null

代码如下

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode pa = headA, pb = headB;
        // 双指针遍历,若不相遇则继续
        while (pa != pb) {
            // pa走完A则转B,否则继续走
            pa = (pa == null) ? headB : pa.next;
            // pb走完B则转A,否则继续走
            pb = (pb == null) ? headA : pb.next;
        }
        // 返回交点(若无交点则返回null)
        return pa;
    }
}


反转链表

206. 反转链表

思考方向

原本链表中的节点顺序是 head -> node1 -> node2 -> ... -> nodeN,我们需要将它反转成 nodeN -> nodeN-1 -> ... -> node1 -> head

我们通常可以用两种方式来实现链表反转:迭代方式:通过遍历链表,将每个节点的指针反转。递归方式:将链表分成两部分,递归反转子链表,最后将当前节点的指针反转

代码流程

通过递归反转链表,逐步将每个节点的指向反转,最终返回新的头节点。

代码如下

class Solution {
    public ListNode reverseList(ListNode head) {
        return reverse(head); // 调用递归函数
    }
    // 递归反转链表
    public ListNode reverse(ListNode head) {
        // 递归终止条件:空节点或最后一个节点
        if (head == null || head.next == null) return head;
        // 递归反转后续链表
        ListNode newHead = reverse(head.next);
        // 反转当前节点的指向
        head.next.next = head; // 让下一个节点指向自己
        head.next = null;      // 断开当前节点原指向
        return newHead; // 返回新的头节点
    }
}

下面以示例1作为例子,花了一下示例图

上面的图说明了如何实现 5 , 4 两个节点之间指向的转换。其他节点也是同理的,可以自己画图尝试理解一下


回文链表

234. 回文链表

思考方向

通过将链表的一半反转并与另一半进行比较,可以有效判断链表是否是回文。关键在于如何找到链表的中点,并将后半部分反转

代码流程

  1. 空链表或单节点链表是回文,直接返回true

  2. 使用快慢指针,慢指针到达中点时,快指针到达链表末尾。从中点开始,递归反转后半部分链表。

  3. 逐个比较前半部分和反转后的后半部分节点值,若有不同则返回false。若所有节点值相同,返回true,否则返回false

代码如下

class Solution {
    public boolean isPalindrome(ListNode head) {
        // 空链表或单节点必然是回文
        if (head == null || head.next == null) return true;
        // 快慢指针找到中点
        ListNode fast = head, slow = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        // 反转后半部分链表
        ListNode second = reverse(slow);
        // 比较前后两部分
        while (second != null) {
            if (head.val != second.val) {
                return false;
            }
            head = head.next;
            second = second.next;
        }
        return true;
    }
    // 递归反转链表
    public ListNode reverse(ListNode head) {
        if (head == null || head.next == null) return head;
        ListNode newHead = reverse(head.next);
        head.next.next = head;
        head.next = null;
        return newHead;
    }
}

下面以示例一作为例子,开始演示一下,代码的实际执行流程

首先先用两个快慢指针,使得slow能够到达一半长度后面第一个位置

接下来我们要翻转链表,开始比较


环形链表

141. 环形链表

思考方向

最直接的方法是使用快慢指针。如果链表存在环,快指针(每次走两步)和慢指针(每次走一步)最终会相遇。如果链表没有环,快指针会先到达链表末尾(fastfast.nextnull

代码流程

  1. 特殊情况:链表为空或只有一个节点,无环,返回false

  2. 快慢指针:初始化快指针fast和慢指针slow,快指针每次走两步,慢指针走一步。

  3. 检测环:遍历链表,若fastslow指针相遇,返回true(有环);若fast到达末尾,返回false(无环)。

代码如下

public class Solution {
    public boolean hasCycle(ListNode head) {
        // 空链表或单节点无环
        if (head == null || head.next == null) return false;
        // 快慢指针初始化
        ListNode slow = head, fast = head;
        // 遍历链表
        while (fast != null && fast.next != null) {
            fast = fast.next.next;  // 快指针每次走两步
            slow = slow.next;       // 慢指针每次走一步
            // 如果快慢指针相遇,说明有环
            if (fast == slow) return true;
        }
        // 快指针到达末尾,无环
        return false;
    }
}


环形链表 II

142. 环形链表 II

思考方向

检测链表是否有环时,可以使用快慢指针方法,如果链表有环,快指针和慢指针会在环中相遇。问题的进一步扩展是如何找到环的入口节点。当快慢指针相遇时,意味着存在环。此时,令一个指针从链表头开始,另一个指针从相遇点开始。两个指针再次相遇时,即为环的入口

推导公式

  • L1:链表头到环入口的距离(节点数)

  • L2:环入口到快慢指针相遇点的距离(节点数)

  • C:环的长度(节点数)

设相遇时:

  • 慢指针走了 S=L1+L2S=L1+L2 步

  • 快指针走了 F=L1+L2+nCF=L1+L2+nC 步(n为快指针在环内多绕的圈数)

由于快指针速度是慢指针的2倍:

F==2S

代入:

L1+L2+nC=2(L1+L2)

nC=L1+L2

L1=nC−L2

代码流程

  1. 初始化快慢指针fastslow指针都从头开始,快指针走两步,慢指针走一步。

  2. 找环的相遇点:快慢指针相遇时,说明链表有环。

  3. 找环入口:一个指针从头开始,另一个从相遇点开始,两个指针同步走,首次相遇即为环的入口。

  4. 返回环入口:返回环的入口节点,如果无环则返回null

代码如下

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head, slow = head;
        // 快慢指针找相遇点
        while (fast != null && fast.next != null) {
            fast = fast.next.next;  // 快指针走两步
            slow = slow.next;       // 慢指针走一步
            // 发现环
            if (fast == slow) {
                // 一个指针从头开始,一个从相遇点开始
                ListNode curr = head;
                // 两者相遇点即为环入口
                while (curr != slow) {
                    curr = curr.next;
                    slow = slow.next;
                }
                return curr;  // 返回环入口
            }
        }
        return null;  // 无环
    }
}


合并两个有序链表

21. 合并两个有序链表

思考方向

  • 可以利用两个指针(一个指向list1,另一个指向list2),按顺序比较两个链表的节点,依次将较小的节点插入到结果链表中。为了简化操作(如处理链表头部插入时的特殊情况),使用一个虚拟头节点来作为最终结果链表的起始点。

代码流程

  1. 创建虚拟头节点dummy,简化操作,curr指向当前合并链表的最后节点。

  2. 使用两个指针分别遍历list1list2,将较小的节点接到curr.next,并移动指针。

  3. 当一个链表遍历完后,直接将另一个链表连接到curr.next

  4. 最终返回dummy.next,即合并后的链表头节点。

代码如下

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        // 虚拟头节点,简化操作
        ListNode dummy = new ListNode(0);
        ListNode curr = dummy;
        // 双指针比较并合并
        while (list1 != null && list2 != null) {
            if (list1.val < list2.val) {
                curr.next = list1;
                list1 = list1.next;
            } else {
                curr.next = list2;
                list2 = list2.next;
            }
            curr = curr.next;
        }
        // 连接剩余部分
        curr.next = (list1 == null) ? list2 : list1;
        return dummy.next;
    }
}


两数相加

2. 两数相加

思考方向

题目要求将两个链表表示的数字相加,链表的每个节点存储一个数字的个位,低位在前,高位在后。需要模拟逐位相加的过程,考虑进位的情况。每位相加时,可能出现进位(和大于等于10),因此要处理进位的传递。最终的结果可能会多出一个进位位,因此链表的长度可能比原链表长。

代码流程

  1. 创建dummy节点,curr指针指向当前结果链表的最后节点。

  2. 同时遍历l1l2,每次取当前位的数字并计算和。

  3. 计算当前位的和,更新进位carry。创建新节点并连接到curr,移动curr指针。

  4. 更新l1l2指针,直到所有节点和进位处理完。

  5. 返回dummy.next,即合并后的链表。

代码如下

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(0); // 虚拟头节点
        ListNode curr = dummy;
        int carry = 0; // 进位值
        // 遍历两个链表,直到都为空且无进位
        while (l1 != null || l2 != null || carry != 0) {
            // 获取当前位的值,链表为空则视为0
            int num1 = (l1 == null ? 0 : l1.val);
            int num2 = (l2 == null ? 0 : l2.val);
            int sum = num1 + num2 + carry; // 计算和
            ListNode node = new ListNode(sum % 10); // 当前位结果
            carry = sum / 10; // 更新进位
            curr.next = node; // 连接节点
            curr = curr.next;
            // 移动链表指针
            if (l1 != null) l1 = l1.next;
            if (l2 != null) l2 = l2.next;
        }
        return dummy.next; // 返回结果链表
    }
}


以上就是本篇文章对于力扣 hot 100链表章节的上班部分的全部的内容。希望能够帮助到看博客的各位。下半部分会加紧写出来。大家有什么疑问或者建议,可以在评论区进行留言。

posted @ 2026-01-30 11:35  gccbuaa  阅读(6)  评论(0)    收藏  举报