Medium

题目描述

给你一个整数数组 arr,请使用 煎饼翻转 完成对数组的排序。

一次煎饼翻转的执行过程如下:

  • 选择一个整数 k1 <= k <= arr.length
  • 反转子数组 arr[0...k-1](下标从 0 开始)

例如,arr = [3,2,1,4],选择 k = 3 进行一次煎饼翻转,反转子数组 [3,2,1],得到 arr = [1,2,3,4]

以数组形式返回能使 arr 有序的煎饼翻转操作所对应的 k 值序列。任何将数组排序且翻转次数在 10 * arr.length 范围内的有效答案都将被判断为正确。

示例 1:

输入:arr = [3,2,4,1]
输出:[4,2,4,3]
解释:
我们执行 4 次煎饼翻转,k 值分别为 4,2,4,和 3。
初始状态 arr = [3, 2, 4, 1]
第一次翻转后(k = 4):arr = [1, 4, 2, 3]
第二次翻转后(k = 2):arr = [4, 1, 2, 3]
第三次翻转后(k = 4):arr = [3, 2, 1, 4]
第四次翻转后(k = 3):arr = [1, 2, 3, 4],此时已完成排序。 

示例 2:

输入:arr = [1,2,3]
输出:[]
解释:输入已经排序,因此不需要翻转任何内容。
请注意,其他可能的答案,如 [3,3],也将被判断为正确。

提示:

  • 1 <= arr.length <= 100
  • 1 <= arr[i] <= arr.length
  • arr 中的所有整数互不相同(即,arr 是从 1arr.length 整数的一个排列)

解题思路

这道题的核心思路是贪心算法。我们需要逐个将最大的元素放到正确的位置。

基本思路:

  1. 从最大的元素开始处理,每次将当前未排序部分的最大元素移动到正确位置
  2. 对于每个最大元素,如果它不在正确位置,我们需要两步操作:
    • 第一步:如果最大元素不在数组开头,先将它翻转到开头
    • 第二步:将整个未排序部分翻转,使最大元素到达正确位置

具体步骤:

  1. 找到当前未排序部分(长度为 n)的最大元素位置
  2. 如果最大元素已在正确位置(索引 n-1),直接处理下一个
  3. 如果最大元素在开头(索引 0),直接翻转整个未排序部分
  4. 否则,先翻转 [0…maxIndex],将最大元素移到开头,再翻转整个未排序部分

这个方法最多需要 2n 次翻转,满足题目要求的 10n 次限制。算法简单直观,易于理解和实现。

代码实现

class Solution {
public:
    vector<int> pancakeSort(vector<int>& arr) {
        vector<int> result;
        int n = arr.size();
        
        for (int size = n; size > 1; size--) {
            // 找到当前未排序部分的最大元素位置
            int maxIndex = 0;
            for (int i = 1; i < size; i++) {
                if (arr[i] > arr[maxIndex]) {
                    maxIndex = i;
                }
            }
            
            // 如果最大元素已经在正确位置,跳过
            if (maxIndex == size - 1) continue;
            
            // 如果最大元素不在开头,先翻转到开头
            if (maxIndex != 0) {
                reverse(arr.begin(), arr.begin() + maxIndex + 1);
                result.push_back(maxIndex + 1);
            }
            
            // 将最大元素翻转到正确位置
            reverse(arr.begin(), arr.begin() + size);
            result.push_back(size);
        }
        
        return result;
    }
};
class Solution:
    def pancakeSort(self, arr: List[int]) -> List[int]:
        result = []
        n = len(arr)
        
        for size in range(n, 1, -1):
            # 找到当前未排序部分的最大元素位置
            max_index = arr[:size].index(max(arr[:size]))
            
            # 如果最大元素已经在正确位置,跳过
            if max_index == size - 1:
                continue
            
            # 如果最大元素不在开头,先翻转到开头
            if max_index != 0:
                arr[:max_index + 1] = arr[:max_index + 1][::-1]
                result.append(max_index + 1)
            
            # 将最大元素翻转到正确位置
            arr[:size] = arr[:size][::-1]
            result.append(size)
        
        return result
public class Solution {
    public IList<int> PancakeSort(int[] arr) {
        List<int> result = new List<int>();
        int n = arr.Length;
        
        for (int size = n; size > 1; size--) {
            // 找到当前未排序部分的最大元素位置
            int maxIndex = 0;
            for (int i = 1; i < size; i++) {
                if (arr[i] > arr[maxIndex]) {
                    maxIndex = i;
                }
            }
            
            // 如果最大元素已经在正确位置,跳过
            if (maxIndex == size - 1) continue;
            
            // 如果最大元素不在开头,先翻转到开头
            if (maxIndex != 0) {
                Array.Reverse(arr, 0, maxIndex + 1);
                result.Add(maxIndex + 1);
            }
            
            // 将最大元素翻转到正确位置
            Array.Reverse(arr, 0, size);
            result.Add(size);
        }
        
        return result;
    }
}
var pancakeSort = function(arr) {
    const result = [];
    const n = arr.length;
    
    for (let size = n; size > 1; size--) {
        // Find the index of the maximum element in the current unsorted portion
        let maxIndex = 0;
        for (let i = 1; i < size; i++) {
            if (arr[i] > arr[maxIndex]) {
                maxIndex = i;
            }
        }
        
        // If the max element is already at the end, continue
        if (maxIndex === size - 1) continue;
        
        // If the max element is not at the beginning, flip to bring it to the front
        if (maxIndex !== 0) {
            result.push(maxIndex + 1);
            reverse(arr, maxIndex);
        }
        
        // Flip to move the max element to its correct position
        result.push(size);
        reverse(arr, size - 1);
    }
    
    return result;
};

function reverse(arr, k) {
    let left = 0;
    let right = k;
    while (left < right) {
        [arr[left], arr[right]] = [arr[right], arr[left]];
        left++;
        right--;
    }
}

复杂度分析

复杂度类型分析
时间复杂度O(n²) - 外层循环 n 次,每次需要找到最大元素位置需要 O(n) 时间
空间复杂度O(1) - 除了结果数组外,只使用常数额外空间(原地修改数组)