高频链表算法题

102 阅读4分钟

将用“寻宝猎人”的故事贯穿5大高频链表算法题,结合Java代码和复杂度分析,带你轻松掌握解题精髓!每个解法都附带​​记忆口诀​​和​​实战技巧​​,助你面试游刃有余。


🔍 ​​故事背景:寻宝猎人的链式冒险​

你是一名寻宝猎人,手中有一张神秘藏宝图(链表)。每个藏宝点(节点)标注着宝藏价值(val)和下一个藏宝点线索(next)。你需要完成以下任务:

  1. ​翻转路线​​(反转链表)
  2. ​识破循环陷阱​​(环形链表检测)
  3. ​合并藏宝图​​(合并有序链表)
  4. ​精准拆除炸弹​​(删除倒数第N节点)
  5. ​寻找交汇点​​(链表相交)

🔄 ​​一、翻转藏宝路线(反转链表)​

​剧情​​:藏宝图顺序被加密,需从尾到头重新连接线索。
​口诀​​:​​“三指针舞步,prev-cur-next 轮转”​

​1. 迭代法(O(n)时间,O(1)空间)​

java
Copy
// 三指针翻转:prev记录前驱,cur处理当前,next暂存后继[5](@ref)
ListNode reverseList(ListNode head) {
    ListNode prev = null;
    ListNode cur = head;
    while (cur != null) {
        ListNode next = cur.next; // 1. 暂存下一站线索
        cur.next = prev;         // 2. 当前节点指向前驱(翻转箭头)
        prev = cur;              // 3. 前驱指针前进
        cur = next;              // 4. 当前指针前进
    }
    return prev; // 新头节点是原链表末尾
}

​关键点​​:

  • 每次翻转一个节点的指针方向
  • 结束时prev指向新头部

​2. 递归法(O(n)时间,O(n)栈空间)​

java
Copy
ListNode reverseRecursive(ListNode head) {
    if (head == null || head.next == null) return head;
    ListNode newHead = reverseRecursive(head.next); // 递归到尾部
    head.next.next = head; // 后一节点指回当前节点
    head.next = null;      // 当前节点断开原指针
    return newHead;
}

💡 ​​适用场景​​:面试官要求多种解法时展示递归思维,但迭代法更优


⭕ ​​二、识破循环陷阱(环形链表检测)​

​剧情​​:藏宝图有循环路线,陷入则永无尽头,需快速检测。
​口诀​​:​​“龟兔赛跑,相遇即环”​

​弗洛伊德快慢指针法(O(n)时间,O(1)空间)​

java
Copy
boolean hasCycle(ListNode head) {
    if (head == null) return false;
    ListNode slow = head; // 乌龟每次走1步
    ListNode fast = head; // 兔子每次走2步
    while (fast != null && fast.next != null) {
        slow = slow.next;
        fast = fast.next.next;
        if (slow == fast) return true; // 相遇说明有环
    }
    return false;
}

​数学证明​​:

  • 设环前长度L,环长C
  • 相遇时慢指针走L + D,快指针走L + D + kC
  • 快指针速度是慢指针2倍 → 2(L + D) = L + D + kC → L = kC - D

进阶:找环入口点?相遇后重置slow=headslowfast同速走L步即入口


🧩 ​​三、合并藏宝图(合并两个有序链表)​

​剧情​​:两份藏宝图(升序排列),需合并成一张完整图。
​口诀​​:​​“哑节点护头,谁小连谁走”​

​迭代法(O(n+m)时间,O(1)空间)​

java
Copy
ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    ListNode dummy = new ListNode(-1); // 虚拟头节点(避免空指针)
    ListNode cur = dummy;
    while (l1 != null && l2 != null) {
        if (l1.val < l2.val) {
            cur.next = l1; // 连接较小节点
            l1 = l1.next;
        } else {
            cur.next = l2;
            l2 = l2.next;
        }
        cur = cur.next; // 新链指针前进
    }
    cur.next = (l1 != null) ? l1 : l2; // 接上剩余部分
    return dummy.next; // 返回真实头节点
}

​关键技巧​​:

  • 虚拟头节点(dummy)简化边界处理

  • 无需额外空间,直接复用原节点


💣 ​​四、精准拆除炸弹(删除倒数第N节点)​

​剧情​​:倒数第N个藏宝点有炸弹,需精准拆除且不触发警报。
​口诀​​:​​“快指针先走N步,同步移动慢指针”​

​快慢指针法(O(n)时间,O(1)空间)​

java
Copy
ListNode removeNthFromEnd(ListNode head, int n) {
    ListNode dummy = new ListNode(-1, head); // 虚拟头防删头节点
    ListNode fast = dummy, slow = dummy;
    // 快指针先走N+1步(让slow停在目标前驱)
    for (int i = 0; i <= n; i++) fast = fast.next; 
    while (fast != null) {
        slow = slow.next;
        fast = fast.next;
    }
    slow.next = slow.next.next; // 跳过目标节点
    return dummy.next;
}

​为什么是N+1步​​?

  • slow停在待删节点的​​前驱​​位置

  • 例:删除倒数第2个节点(链表 1→2→3→4

    Copy
    初始:dummy→1234
    fast先走3步:到3
    slow最终停在2(前驱),删除324
    

❤️ ​​五、寻找交汇点(链表相交)​

​剧情​​:两份藏宝图在某点后重合,需找到第一个交汇藏宝点。
​口诀​​:​​“算长度差,齐头并进找交点”​

​长度差+双指针法(O(n+m)时间,O(1)空间)​

java
Copy
ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    // 1. 计算两链长度
    int lenA = getLength(headA), lenB = getLength(headB);
    // 2. 长链指针先走 |lenA-lenB| 步
    ListNode pA = headA, pB = headB;
    if (lenA > lenB) {
        for (int i = 0; i < lenA - lenB; i++) pA = pA.next;
    } else {
        for (int i = 0; i < lenB - lenA; i++) pB = pB.next;
    }
    // 3. 双指针齐头并进找交点
    while (pA != pB) {
        pA = pA.next;
        pB = pB.next;
    }
    return pA; // 相交点或null
}

int getLength(ListNode head) {
    int len = 0;
    while (head != null) {
        len++;
        head = head.next;
    }
    return len;
}

​数学原理​​:

  • 两链相交后长度相同 → 对齐起点后同步遍历必相遇

  • 若不相遇则pApB同时走到null退出


💎 ​​高频链表算法思维导图​

image.png

⚠️ ​​避坑指南(面试致命陷阱)​

  1. ​空指针异常​​:

    • 总是检查head == null
    • 访问next前确认当前节点非空
  2. ​指针丢失​​:

    • 翻转链表时先存next = cur.next再改指针
  3. ​环检测误区​​:

    • 快指针需判断fast != null && fast.next != null

​最后挑战​​:尝试用快慢指针解决 ​​“判断回文链表”​​(先找中点,再翻转后半段比较)。掌握这5题,90%链表面试题迎刃而解!