Java链表反转方法详解

一、理解链表结构

假设链表节点定义为:

复制代码
class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { val = x; }
}

二、迭代法反转链表

核心思路

逐步反转每个节点的指针方向,最终使整个链表反向。

步骤拆解
  1. 初始化三个指针:

    • prev:指向已反转部分的头节点(初始为 null)。

    • current:指向待反转的当前节点(初始为头节点)。

    • next:临时保存 current.next,防止断链。

  2. 循环操作

    • 保存 current 的下一个节点:next = current.next

    • 反转指针:current.next = prev

    • 移动 prevcurrentprev = currentcurrent = next

  3. 终止条件 :当 currentnull 时,prev 就是新链表的头节点。

代码实现
复制代码
public ListNode reverseList(ListNode head) {
    ListNode prev = null;
    ListNode current = head;
    while (current != null) {
        ListNode next = current.next; // 保存下一个节点
        current.next = prev;          // 反转指针
        prev = current;               // prev 前移
        current = next;               // current 前移
    }
    return prev; // 新头节点
}
示意图
复制代码
初始状态:1 -> 2 -> 3 -> null
反转过程:
Step 1: prev=null, current=1 → 1.next=null → prev=1, current=2
Step 2: prev=1, current=2 → 2.next=1 → prev=2, current=3
Step 3: prev=2, current=3 → 3.next=2 → prev=3, current=null
最终结果:3 -> 2 -> 1 -> null

三、递归法反转链表

核心思路

假设头节点后的子链表已反转,只需处理头节点与子链表的关系。

步骤拆解
  1. 终止条件:链表为空或只有一个节点,直接返回头节点。

  2. 递归反转子链表newHead = reverseList(head.next)

  3. 调整指针

    • 将头节点的下一个节点的 next 指向自己:head.next.next = head

    • 断开原头节点的 next 指针:head.next = null

代码实现
复制代码
public ListNode reverseList(ListNode head) {
    if (head == null || head.next == null) {
        return head; // 终止条件
    }
    ListNode newHead = reverseList(head.next); // 递归反转子链表
    head.next.next = head; // 反转指针
    head.next = null;      // 断开原指针
    return newHead; // 始终返回新头节点
}
示意图
复制代码
初始链表:1 -> 2 -> 3 -> null
递归过程:
递归到最深层:处理节点3 → 返回3
回归时处理节点2:2.next.next=2 → 3 -> 2,断开2.next → 3 -> 2 -> null
回归时处理节点1:1.next.next=1 → 2 -> 1,断开1.next → 3 -> 2 -> 1 -> null

四、分组反转链表

问题分析

目标:每 k 个节点为一组 反转链表,剩余不足 k 个的节点保持原顺序。

示例:

  • 原链表:1 → 2 → 3 → 4 → 5k=2 → 反转后:2 → 1 → 4 → 3 → 5

  • 原链表:1 → 2 → 3 → 4 → 5k=3 → 反转后:3 → 2 → 1 → 4 → 5


核心思路
  1. 分段处理:将链表按 k 个一组切割,逐组反转。

  2. 边界控制

    • 检查剩余节点是否足够 k 个。

    • 反转后正确连接各组头尾节点。

  3. 递归或迭代:两种方法均可实现,迭代法更直观。


迭代法实现

步骤拆解
  1. 辅助节点 :创建虚拟头节点 dummy,简化头节点处理。

  2. 指针定义

    • pre:指向当前组的前一组的尾节点(初始为 dummy)。

    • start:当前组的起始节点。

    • end:当前组的结束节点。

  3. 循环处理

    • 定位每组起点 start 和终点 end

    • 若剩余节点不足 k 个,直接结束。

    • 反转当前组,并调整 prestartend 的指针。

代码实现
复制代码
public ListNode reverseKGroup(ListNode head, int k) {
    if (head == null || k == 1) return head;
    
    ListNode dummy = new ListNode(0);
    dummy.next = head;
    ListNode pre = dummy;  // 前一组反转后的尾节点
    ListNode start = head; // 当前组的起始节点
    
    while (start != null) {
        ListNode end = pre; // 定位当前组的 end 节点
        for (int i = 0; i < k; i++) {
            end = end.next;
            if (end == null) return dummy.next; // 不足 k 个,直接返回
        }
        
        // 保存下一组的起始节点,并断开当前组
        ListNode nextGroup = end.next;
        end.next = null;
        
        // 反转当前组,并连接前一组的尾节点
        pre.next = reverse(start); // pre → 新头节点
        start.next = nextGroup;    // 新尾节点 → 下一组
        
        // 移动 pre 和 start 到下一组
        pre = start;
        start = nextGroup;
    }
    return dummy.next;
}

// 反转单链表(基础迭代法)
private ListNode reverse(ListNode head) {
    ListNode prev = null;
    ListNode curr = head;
    while (curr != null) {
        ListNode next = curr.next;
        curr.next = prev;
        prev = curr;
        curr = next;
    }
    return prev;
}

递归法实现

核心思路
  1. 递归反转每组:先反转前 k 个节点,再递归处理剩余链表。

  2. 边界检查:剩余节点不足 k 个时直接返回原链表。

代码实现
复制代码
public ListNode reverseKGroup(ListNode head, int k) {
    if (head == null || k == 1) return head;
    
    // 检查剩余节点是否足够 k 个
    ListNode curr = head;
    int count = 0;
    while (curr != null && count < k) {
        curr = curr.next;
        count++;
    }
    if (count < k) return head; // 不足 k 个,不反转
    
    // 反转前 k 个节点
    ListNode newHead = reverse(head, k);
    
    // 递归处理剩余链表,并连接已反转的部分
    head.next = reverseKGroup(curr, k);
    
    return newHead;
}

// 反转前 k 个节点
private ListNode reverse(ListNode head, int k) {
    ListNode prev = null;
    ListNode curr = head;
    while (k-- > 0) {
        ListNode next = curr.next;
        curr.next = prev;
        prev = curr;
        curr = next;
    }
    return prev;
}

关键边界条件

  1. 链表长度不足 k:直接返回原链表。

  2. k=1:无需反转。

  3. 头尾连接:反转后需要将前一组的尾节点连接到当前组的新头节点,当前组的尾节点连接到下一组的头节点。

代码对比

我自己对于迭代法进行优化,减少了一个linknode start。

java 复制代码
 public static ListNode reverseKGroup(ListNode head, int k) {

        if (head==null || k==1)return head;
        ListNode dummy = new ListNode(0);
        ListNode prev = dummy;  // 用来记录每个分组的前节点,用于连结分组,赋值end移动等
        dummy.next = head; // 初始化虚拟头节点


        while (head!=null){ /// 
            ListNode end=prev; // end节点 反转的最后一个节点
            for (int i = 0; i < k; i++) {
                end=end.next;
                if (end==null)return dummy.next;  // 保持返回链表的最初头节点
            }
            ListNode next = end.next; // 用于保存下一组起始
            end.next=null;
            prev.next=reverse(head);// pre--> 新头节点     head用来反转的头节点,prev作为上一组来连结
            head.next=next; // 新尾点--> 下一组

            // 对pre重新赋值
            prev=head; // 上一组的尾节点
            head=next; //下一组的头节点 或者说往下移一步
        }
        return dummy.next;
    }

    public static ListNode reverse(ListNode head) {
        ListNode prev=null;
        ListNode curr=head;
        ListNode next=head.next;
        while(curr!=null){
            next=curr.next;
            curr.next=prev;
            prev=curr;
            curr=next;
        }
        return prev;
    }

力扣上官方代码

java 复制代码
class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode hair = new ListNode(0);
        hair.next = head;
        ListNode pre = hair;

        while (head != null) {
            ListNode tail = pre;
            // 查看剩余部分长度是否大于等于 k
            for (int i = 0; i < k; ++i) {
                tail = tail.next;
                if (tail == null) {
                    return hair.next;
                }
            }
            ListNode nex = tail.next;
            ListNode[] reverse = myReverse(head, tail);
            head = reverse[0];
            tail = reverse[1];
            // 把子链表重新接回原链表
            pre.next = head;
            tail.next = nex;
            pre = tail;
            head = tail.next;
        }

        return hair.next;
    }

    public ListNode[] myReverse(ListNode head, ListNode tail) {
        ListNode prev = tail.next;
        ListNode p = head;
        while (prev != tail) {
            ListNode nex = p.next;
            p.next = prev;
            prev = p;
            p = nex;
        }
        return new ListNode[]{tail, head};
    }
}
相关推荐
Dobby_051 小时前
【Go】C++转Go:数据结构练习(一)排序算法
数据结构·golang
熬了夜的程序员1 小时前
【LeetCode】90. 子集 II
数据结构·算法·leetcode·链表·职场和发展·排序算法
熬了夜的程序员1 小时前
【LeetCode】91. 解码方法
算法·leetcode·链表·职场和发展·排序算法
大数据张老师1 小时前
数据结构——内部排序算法的选择和应用
数据结构·算法·排序算法
緈福的街口3 小时前
gps的定位图,在车的位置去寻找周围20x20的区域,怎么确定周围有多少辆车,使用什么数据结构
数据结构·算法
风筝在晴天搁浅4 小时前
代码随想录 701.二叉搜索树中的插入操作
数据结构
星空露珠4 小时前
数独解题算法lua脚本
开发语言·数据结构·算法·游戏·lua
小猪咪piggy4 小时前
【算法】day14 链表
数据结构·算法·链表
yy_xzz5 小时前
【数据结构】队列(Queue)详解——数据结构的“先进先出”
开发语言·数据结构
承渊政道5 小时前
算法复杂度
c语言·数据结构·c++·算法·visual studio