315K排序算法最小操作次数计算 困难 | 豆包MarsCode AI刷题

62 阅读5分钟

题意分析

给定一个长度为 n 的排列 a 和一个整数 k。我们可以进行以下操作:

  • 每次操作选择最多 k 个位置的数,将它们移除,并且剩余的数左对齐。
  • 移除的数进行排序并将其放在数列的末尾。

要求你通过最少的操作次数,将排列 a 按升序排列。

我们的目标是计算出最少需要多少次操作。

思路详解

这个问题可以通过以下几个步骤来解决:

  1. 观察排列的有序部分

    • 为了尽可能减少操作次数,我们应当识别出排列中已经是按升序排列的部分。
    • 假设我们能找到一个最长的升序子序列(从排列开始到某一位置),那么剩下的元素就是需要通过操作来处理的部分。
  2. 关键点:找到排列中的连续递增子序列

    • 从排列的第一个元素开始,依次检查当前元素是否正好是期望值。如果当前元素正好是期望值,则可以继续,表示该元素已经处于正确位置。
    • 期望值从 1 开始,依次增加,直到最后。如果某个元素不等于期望值,那么我们就认为后续的元素需要进行操作。
  3. 计算剩余需要操作的元素数量

    • 一旦我们找到了最长的升序前缀,即那些已经处于正确位置的元素,剩下的元素需要通过 k 个一组的操作来完成。
    • 例如,如果剩余的元素有 m 个,而每次操作可以移除 k 个元素,那么至少需要 (m + k - 1) // k 次操作来处理这些剩余的元素。
  4. 总结

    • 我们的目标是找到 最长升序前缀,然后计算剩余需要排序的部分,最后通过计算需要的操作次数来得到答案。

代码实现

根据上面的思路,我们可以编写如下代码:

python
复制代码
def solution(n: int, k: int, a: list) -> int:
    i = 1  # 期望值,从 1 开始
    # 遍历排列 a,找出最长升序前缀
    for x in a:
        if x == i:  # 如果当前元素与期望值匹配,则期望值加 1
            i += 1
    # 计算剩余元素的数量
    last = n - i + 1
    # 计算需要的操作次数,每次操作最多移除 k 个元素
    return (last + k - 1) // k

代码分析

1. 初始化变量

python
复制代码
i = 1

变量 i 表示当前期望的数字,初始值为 1,因为我们要求按升序排列,期望数字从 1 开始逐步递增。

2. 遍历数组

python
复制代码
for x in a:
    if x == i:
        i += 1

通过遍历排列 a 中的每个元素 x,我们检查它是否等于期望值 i。如果相等,说明 x 已经在正确的位置上,可以增加 i,期望下一个元素出现在当前位置。

  • 如果 x 等于 i,则 i 加 1,表示下一个位置应放置数字 i+1
  • 该过程的目的是找到排列中的最大升序前缀部分。

3. 计算剩余元素

python
复制代码
last = n - i + 1

在循环结束后,i 将会是下一个期望值。此时,i - 1 就是排列中最大连续升序部分的长度。所以,剩余的元素数量为 n - (i - 1),即 last = n - i + 1

4. 计算操作次数

python
复制代码
return (last + k - 1) // k

剩余的 last 个元素需要通过操作来移除并排序。由于每次操作最多可以移除 k 个元素,因此最少需要的操作次数为 (last + k - 1) // k,这是 向上取整 的方式,确保即使剩余的元素不足 k 个,也会进行额外的操作。

时间复杂度分析

  • 遍历数组 a 需要 O(n)  的时间,因为我们只需要一次遍历来计算出最长升序前缀。
  • 计算剩余元素数量和操作次数的步骤都是常数时间操作 O(1)

因此,整个算法的时间复杂度为 O(n)

空间复杂度分析

  • 该算法只使用了少量的额外空间来存储变量(例如 i 和 last)。
  • 因此,空间复杂度为 O(1) ,即常数空间。

示例分析

让我们通过一些示例来验证该算法的正确性。

示例 1

输入

python
复制代码
n = 11
k = 1
a = [5, 1, 8, 3, 6, 2, 10, 9, 11, 4, 7]

执行过程

  • 初始 i = 1
  • 遍历数组 a,发现 1 位置正确,i 变为 2,然后找到 2i 变为 3,依此类推,直到 i = 6,即最长升序前缀是 [1, 2, 3, 4, 5]
  • 剩余元素为 n - 5 = 6 个。
  • 每次操作最多移除 1 个元素,所以需要的操作次数为 (6 + 1 - 1) // 1 = 6 次。

输出

python
复制代码
6

示例 2

输入

python
复制代码
n = 12
k = 1
a = [5, 9, 12, 3, 8, 4, 6, 1, 7, 2, 11, 10]

执行过程

  • 初始 i = 1
  • 遍历数组 a,发现 1 位置正确,i 变为 2,然后找到 2i 变为 3,依此类推,直到 i = 7,即最长升序前缀是 [1, 2, 3, 4, 5, 6]
  • 剩余元素为 n - 6 = 6 个。
  • 每次操作最多移除 1 个元素,所以需要的操作次数为 (6 + 1 - 1) // 1 = 6 次。

输出

python
复制代码
6

总结

本题通过寻找最长升序前缀来尽量减少需要操作的元素数量,然后通过计算需要的操作次数来解决问题。该算法的时间复杂度为 O(n) ,空间复杂度为 O(1) ,非常高效,能够解决题目中给定的排列排序问题。