Python 中数组轮换的块交换算法

2025年3月17日 | 阅读 7 分钟

将数组中的元素按给定数量的位置进行旋转是一种常见的数组操作。旋转数组的朴素方法是弹出每个元素并将其插入到旋转后的位置。然而,这需要 O(n) 次交换操作,其中 n 是数组的长度。对于大型数组来说,这种方法效率低下。

一种优化的技术是块交换算法。在该算法中,数组被分成块,并将这些块作为一个整体进行交换。通过交换连续的元素块而不是单个组件,可以显著减少交换操作。

块交换算法利用了这样一个事实:将数组元素进行数组长度整数倍的交换会得到原始数组。它计算 n 和 r 的最大公约数 (GCD),并将数组分成 gcd(n,r) 个块。通过将每个块向右(或向左)移动 1 个位置 gcd(n,r) 次,最终结果是将所有元素移动 r 个位置。

在本文中,我们将详细介绍块交换算法的工作原理。我们将通过算法步骤,并实现一个 Python 函数来使用块交换技术旋转数组。

Block Swap Algorithm for Array Rotation in Python

方法一:迭代法

将数组中的元素按给定数量的位置进行旋转是一种常见的操作。通过弹出每个元素并将其插入到新位置进行旋转的朴素方法,对于大小为 n 的数组需要 O(n) 次交换操作。对于大型数组来说,这可能效率低下。

一种优化的技术是块交换算法,它将数组分成块,并将这些块作为一个整体进行交换。通过交换元素块而不是单个组件,该算法减少了所需操作的数量。

块交换算法的工作原理如下:

  1. 将数组分成两个块——块 A 从索引 0 到 d-1,块 B 从索引 d 到 n-1,其中 d 是旋转量。
  2. 比较块 A 和块 B 的大小。
  3. 将较小的块与另一个块的末尾元素进行交换。例如,如果 A 较小,它将与 B 的最后一部分进行交换。
  4. 减小已交换块的大小,然后重复此过程。
  5. 当 A 和 B 的大小相等时,交换剩余的元素。

这样,该算法通过将交换操作分成块并同时交换相等的块来分配交换操作。

Python 实现

输出

Original Array:
1 2 3 4 5 6 7 
Array after rotating by 2 positions:
3 4 5 6 7 1 2

说明

  1. swap() 函数
    • 接受数组、两个块的起始索引(start1, start2)和块大小 d
    • 成对地在两个块之间交换 d 个元素
  2. block_rotate() 函数
    • 检查旋转量 d 是否为 0 或 n;无需旋转
    • 将 i 初始化为左块大小,将 j 初始化为右块大小
    • 当 i 不等于 j 时进入循环
      • 如果 i < j,则左块较小
        • 从 d-i 到 d+j-i 调用 swap(),大小为 i
        • 将 j 减去 i
      • 否则右块较小
        • 对大小为 j 的右块调用 swap()
        • 将 i 减去 j
      • 对剩余的左块进行最终的 swap()
  1. print_array() 函数
    • 打印数组元素
  2. 主驱动代码
    • 定义数组
    • 获取旋转量 d
    • 计算数组大小 n
    • 打印原始数组
    • 调用 block_rotate() 将数组旋转 d 个位置
    • 打印旋转后的数组

总结

  • swap() 交换大小为 d 的两个块
  • block_rotate() 将数组分成两个块
    • 每次交换较小的块
    • 减小已交换边的长度
    • 继续直到 i = j
  • 对剩余元素进行最终交换
  • 打印原始数组和旋转后的数组

因此,通过将数组分成两部分并同时交换相等的部分,高效地完成了块的交换。

方法二:递归法

块交换算法通过将数组分成块并交换它们来旋转数组。与单独旋转元素相比,这最大限度地减少了所需的交换操作次数。

前面讨论了一种迭代方法,其中数组被分成两个块,并在循环中进行交换,直到块的大小相等。然而,递归为实现相同的优化块交换提供了另一种实现方式。

在递归中,函数调用自身来解决问题的更小子问题。对于数组旋转,我们可以递归地将数组分成更小的块,旋转这些块,并交换元素以完成该过程。

递归方法的工作原理如下:

  1. 将数组分成两个块——大小为 d(旋转位置)的块 A 和大小为 n-d 的块 B。
  2. 交换这两个块以部分旋转数组。
  3. 根据块 A 或 B 哪个更小,通过递归地处理它们来减小问题规模。
  4. 在每次递归调用中,已交换的块会进一步分成块,并进行递归交换,直到达到基本情况。
  5. 基本情况是当 d 为 0 或 n 时(无需旋转),或者当 n-d = d 时(数组被分成两个相等的部分)。
  6. 通过巧妙地选择块的大小并递归地交换它们,数组可以以最优方式按 d 个位置旋转。

递归提供了一种优雅的方式将问题分解为更小的子问题,并自底向上地构建解决方案。由于函数堆栈用于递归,因此不需要额外的内存。时间复杂度保持为 O(n)。我们将在下一节中详细介绍递归实现。

输出

Original Array:
1 2 3 4 5 6 7 
Array after rotating by 2 positions:
3 6 4 5 7 1 2

说明

  1. 定义 swap() 函数,用于在数组中的两个子数组之间交换元素。它交换从 start 开始的 d 个元素与从 start+end 开始的 d 个元素。
  2. block_rotate() 是执行旋转的递归函数。
  3. 基本情况
    • 如果 d 为 0 或等于 n,则无需旋转,直接返回。
    • 如果 n-d = d,数组被分成两个相等的部分。交换这两个部分。
  4. 如果 d < n-d
    • 将前 d 个元素与后 n-d 个元素进行交换。
    • 递归地旋转前 n-d 个元素。
  5. 否则,如果 d > n-d
    • 将前 n-d 个元素与后 d 个元素进行交换。
    • 递归地旋转后 d 个元素。
  6. print_array() 函数打印数组。
  7. 驱动代码
    • 创建示例数组 arr。
    • 调用 block_rotate() 将 arr 旋转 d 个位置。
    • 打印原始数组和旋转后的数组。
  8. block_rotate() 根据 d 将数组分成不同大小的块,并在每次递归调用中减小问题规模的同时递归地交换这些块。

结论

块交换算法通过将数组分成块并迭代交换来提供一种有效的数组旋转方法。这通过一次切换相等大小的块来均匀地分配交换操作。该算法将交换次数从 O(n) 减少到 O(n/gcd),这可以显著提高大型数组的性能。

在本文中,我们详细介绍了迭代块交换方法的具体工作原理。该算法通过将数组分成两个块,并在循环中每次交换较小的块,在 Python 中实现。这最大限度地减少了交换操作的数量,同时仍然保持线性时间复杂度。块交换技术可以有效地应用于各种应用中的数组旋转。