一句话说透数据结构里面的如何反转一个单链表

160 阅读2分钟

一句话总结:
反转单链表就像把一列火车倒着开——原本车头变车尾,每节车厢都要调头指向前面的车厢!


一、迭代法(步步为营)

核心思路:  用三个指针边走边改方向,像翻链条一样逐个反转。

Kotlin 代码:

class Node(var data: Int, var next: Node? = null)

fun reverseListIterative(head: Node?): Node? {
    var prev: Node? = null   // 前一个节点(初始为空)
    var curr = head          // 当前节点(从头开始)
    
    while (curr != null) {
        val nextTemp = curr.next // 暂存下一个节点(防止断链)
        curr.next = prev         // 调头指向前一个
        prev = curr             // 前节点前进
        curr = nextTemp         // 当前节点前进
    }
    return prev // 最后prev就是新头
}

图解过程:
原链表:1 → 2 → 3 → null
反转步骤:

  1. prev=null, curr=1 → 暂存2,1指向null → prev=1, curr=2
  2. 暂存3,2指向1 → prev=2, curr=3
  3. 暂存null,3指向2 → prev=3, curr=null
    结果:3 → 2 → 1 → null

二、递归法(从尾到头)

核心思路:  递归到链表末端,返回时逐个反转指针方向。

Kotlin 代码:

fun reverseListRecursive(head: Node?): Node? {
    if (head?.next == null) return head // 终止条件:只剩一个节点
    
    val newHead = reverseListRecursive(head.next) // 递归到最深处
    head.next?.next = head // 反转指向(让后一个节点指回自己)
    head.next = null       // 断开原方向
    
    return newHead
}

图解过程:
递归到3时返回 → 处理2:3.next = 2 → 2.next = null → 处理1:2.next = 1 → 1.next = null
最终链表:3 → 2 → 1 → null


三、性能对比

方法时间复杂度空间复杂度适用场景
迭代法O(n)O(1)常规使用,省内存
递归法O(n)O(n)代码简洁,小链表

四、测试用例

fun main() {
    // 创建链表 1 → 2 → 3
    val node3 = Node(3)
    val node2 = Node(2, node3)
    val node1 = Node(1, node2)

    // 反转并打印
    var newHead = reverseListIterative(node1)
    printList(newHead) // 输出:3 → 2 → 1 → null

    // 递归法测试(需重新构建链表)
    val node3Rec = Node(3)
    val node2Rec = Node(2, node3Rec)
    val node1Rec = Node(1, node2Rec)
    newHead = reverseListRecursive(node1Rec)
    printList(newHead) // 输出同上
}

// 辅助打印函数
fun printList(head: Node?) {
    var curr = head
    while (curr != null) {
        print("${curr.data} → ")
        curr = curr.next
    }
    println("null")
}

五、常见错误

  1. 断链忘暂存:直接修改 curr.next 前未保存 next 节点

    // 错误写法!
    while (curr != null) {
        curr.next = prev // 此时curr.next已经被覆盖,无法继续遍历
        prev = curr
        curr = curr.next // 这里curr.next已经是prev了,导致死循环
    }
    
  2. 递归忘记终止条件:导致无限递归栈溢出


六、终极口诀

反转链表两板斧,
迭代递归任你选。
三个指针步步移,
递归到底再回头!

小数据用递归爽,
大数据迭代保平安! 🔄