链表重排序问题

链表重排序问题(1→2→…→n 变为 1→n→2→n-1→…)

问题分析

这道题目要求我们将一个链表从 1→2→...→n 重排为 1→n→2→n-1→... 的形式,并且要求空间复杂度为 O(1)。

例如:

  • 输入:1→2→3→4 → 输出:1→4→2→3
  • 输入:1→2→3→4→5 → 输出:1→5→2→4→3
解题思路

由于空间复杂度限制为 O(1),我们不能使用额外的数据结构(如数组)来存储节点。可以通过以下步骤实现:

  1. 找到链表中点:使用快慢指针法,慢指针每次走一步,快指针每次走两步,当快指针到达末尾时,慢指针正好在中点。

  2. 反转后半部分链表:从中点开始,将后半部分链表反转。例如,原链表 1→2→3→4→5,中点为 3,反转后变为 1→2→3→5→4

  3. 合并两个链表:将前半部分链表和反转后的后半部分链表交替合并。例如,1→2→35→4 合并后变为 1→5→2→4→3

Golang代码实现

下面是Golang语言的实现方案:

package main

import "fmt"

// ListNode 定义链表节点结构
type ListNode struct {
    Val  int
    Next *ListNode
}

// reorderList 重排链表
func reorderList(head *ListNode) {
    if head == nil || head.Next == nil {
        return
    }

    // 1. 找到链表中点
    slow, fast := head, head
    for fast.Next != nil && fast.Next.Next != nil {
        slow = slow.Next
        fast = fast.Next.Next
    }

    // 2. 反转后半部分链表
    secondHalf := slow.Next
    slow.Next = nil // 断开前半部分和后半部分
    reversedSecondHalf := reverseList(secondHalf)

    // 3. 合并两个链表
    mergeLists(head, reversedSecondHalf)
}

// reverseList 反转链表
func reverseList(head *ListNode) *ListNode {
    var prev *ListNode
    curr := head
    for curr != nil {
        nextTemp := curr.Next
        curr.Next = prev
        prev = curr
        curr = nextTemp
    }
    return prev
}

// mergeLists 合并两个链表
func mergeLists(l1, l2 *ListNode) {
    for l1 != nil && l2 != nil {
        l1Next := l1.Next
        l2Next := l2.Next

        l1.Next = l2
        l2.Next = l1Next

        l1 = l1Next
        l2 = l2Next
    }
}

// 辅助函数:创建链表
func createList(nums []int) *ListNode {
    dummy := &ListNode{}
    curr := dummy
    for _, num := range nums {
        curr.Next = &ListNode{Val: num}
        curr = curr.Next
    }
    return dummy.Next
}

// 辅助函数:打印链表
func printList(head *ListNode) {
    for head != nil {
        fmt.Print(head.Val)
        if head.Next != nil {
            fmt.Print(" -> ")
        }
        head = head.Next
    }
    fmt.Println()
}

func main() {
    // 测试示例
    nums := []int{1, 2, 3, 4, 5}
    head := createList(nums)
    fmt.Println("原始链表:")
    printList(head)

    reorderList(head)
    fmt.Println("重排后链表:")
    printList(head)
}
    
算法复杂度分析
  • 时间复杂度:O(n),其中n是链表的长度。我们需要遍历链表三次:一次找中点,一次反转后半部分,一次合并两个链表。
  • 空间复杂度:O(1),只使用了常数级别的额外空间。
代码解析
  1. 找到链表中点

    • 使用快慢指针法,慢指针每次移动一步,快指针每次移动两步
    • 当快指针到达末尾时,慢指针正好在中点位置
  2. 反转后半部分链表

    • 从中点的下一个节点开始,将链表反转
    • 使用三个指针(prev、curr、next)实现链表反转
  3. 合并两个链表

    • 将前半部分链表和反转后的后半部分链表交替合并
    • 例如,前半部分 1→2→3,后半部分 5→4,合并后为 1→5→2→4→3
测试用例

在main函数中,我们使用了一个包含5个元素的链表进行测试:

  • 原始链表:1→2→3→4→5
  • 重排后链表:1→5→2→4→3
总结

这个算法题主要考察链表操作和空间复杂度优化。通过快慢指针找到中点,反转后半部分链表,然后合并两个链表,我们可以在O(1)空间复杂度内完成链表重排。这种方法不需要额外的数据结构,是一种高效的实现方式。

你可能感兴趣的:(算法,链表,数据结构,后端)