1. 简介

在本文中,我们将介绍如何对一个数值数组进行重排,使得其中的负数元素全部出现在正数元素之前(包括零)。同时,我们要求保持原数组中同符号元素的相对顺序不变。

2. 问题描述

给定一个数组 a = [a₁, a₂, ..., aₙ],我们的目标是将其重排为 a' = [a₁', a₂', ..., aₖ', aₖ₊₁', ..., aₙ'],满足:

  • 所有 aⱼ'(j ≤ k)为负数;
  • 所有 aⱼ'(j > k)为非负数(包括零);
  • 同符号元素的相对顺序与原数组一致。

例如,输入数组为:

a = [10, -2, 30, -4, 1, -20, 3, -40]

期望输出为:

a' = [-2, -4, -20, -40, 10, 30, 1, 3]

3. 使用两个队列的解法

我们可以使用两个队列来分别保存负数和非负数。遍历数组时,根据元素的符号分别入队。最后将两个队列拼接,即可得到结果。

✅ 优点

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

✅ 实现伪代码

algorithm SolutionWithQueues(a):
    queue_negative = new Queue()
    queue_positive = new Queue()

    for i from 0 to length(a) - 1:
        if a[i] < 0:
            queue_negative.enqueue(a[i])
        else:
            queue_positive.enqueue(a[i])

    result = queue_negative.concatenate(queue_positive)
    return result

⚠️ 注意

虽然实现简单,但需要额外的 O(n) 空间,适用于对空间不敏感的场景。

4. 原地合并解法(类似 MergeSort)

如果我们希望减少空间使用,可以采用一种类似 MergeSort 的方法,称为 MergeArrange

✅ 核心思想

  • 将数组划分为子数组;
  • 每次合并相邻子数组,并调整内部顺序;
  • 最终将整个数组调整为负数在前、正数在后,同时保持相对顺序。

✅ 时间与空间复杂度

  • 时间复杂度:O(n log n)
  • 额外空间复杂度:**O(1)**(原地操作)

✅ 合并两个子数组的步骤

假设我们有两个子数组:

[L_- L_+] [R_- R_+]

我们需要将 L_+R_- 交换位置,并保持它们各自的顺序。具体操作如下:

  1. 反转 L_+
  2. 反转 R_-
  3. 再次整体反转这两个子数组拼接后的部分

✅ 示例

以数组 [10, -2, 30, -4, 1, -20, 3, -40] 为例:

  1. 初始分割为 [10] [-2] [30] [-4] [1] [-20] [3] [-40]
  2. 第一次合并后变为 [-2, 10] [-4, 30] [-20, 1] [-40, 3]
  3. 第二次合并后变为 [-2, -4, 10, 30] [-20, -40, 1, 3]
  4. 第三次合并后变为最终结果:[-2, -4, -20, -40, 10, 30, 1, 3]

✅ 伪代码

algorithm MergeArrange(a):
    n = length(a)
    l = 1
    m = ceiling(n / 2)

    while m > 1:
        for i from 0 to n - 1 step 2 * l:
            Reverse(a, i, i + l - 1)
            Reverse(a, i + l, i + 2 * l - 1)
            Reverse(a, i, i + 2 * l - 1)
        l = 2 * l
        m = ceiling(n / l)

    return a

algorithm Reverse(a, i, j):
    while i < j:
        swap a[i], a[j]
        i += 1
        j -= 1

⚠️ 注意

  • 该算法适用于对空间敏感但允许稍高时间复杂度的场景;
  • 逻辑较复杂,需要仔细处理边界条件;
  • 是一种稳定的重排算法。

5. 总结

我们介绍了两种将数组中负数元素排在正数元素之前的解法:

方法 时间复杂度 空间复杂度 是否稳定 适用场景
双队列 O(n) O(n) ✅ 是 空间允许时
原地合并(MergeArrange) O(n log n) O(1) ✅ 是 空间受限时

两种方法各有优劣,可根据具体场景选择合适的实现方式。如果你追求速度,双队列法是更优选择;如果内存受限,原地合并法更合适。


原始标题:Rearrange an Array to Put the Negative Elements Before the Positive Ones