题目
(1)给你一个整数数组 arr ,请使用 煎饼翻转 完成对数组的排序。
(2)一次煎饼翻转的执行过程如下:
- 选择一个整数 k ,1 <= k <= arr.length
- 反转子数组 arr[0…k-1](下标从 0 开始)
(3)例如,arr = [3,2,1,4] ,选择 k = 3 进行一次煎饼翻转,反转子数组 [3,2,1] ,得到 arr = [1,2,3,4] 。
(4)以数组形式返回能使 arr 有序的煎饼翻转操作所对应的 k 值序列。任何将数组排序且翻转次数在 10 * arr.length 范围内的有效答案都将被判断为正确。
(5)示例如下:
输入:[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],此时已完成排序。
解决思路
- 说明:煎饼排序简单来说就是每次反转数组中第1个元素到第k-1个元素,每完成一次反转,就记录下k的值。直到数组变成有序递增数组时停止反转,然后输出记录的所有的k值。
- 思路:先找到数组中最大的元素,将其反转到数组的最后一位;再找到数组中第二大的元素,将其反转到数组的倒数第二位,依次重复。
- 具体方法:
- 先找到数组中最大的元素,并将其反转到数组的第一个位置,再将其反转到数组的最后一个位置。
- 然后找到数组中第二大的元素,并将其反转到数组的第一个位置,再将其反转到数组的倒数第二个位置。
- 重复上述两步操作,直到整个数组变为有序递增的数组时停止反转。
代码
- C++代码
# include <stdio.h>
# include <vector>
using namespace std;
class Solution {
public:
// 对arr的第1个元素到第n-1个元素进行反转,并将反转后每个元素在arr中的索引更新到idx数组中
void reverse(vector<int>& arr, int n, vector<int>& idx) {
for (int i = 0, j = n - 1; i < j; i++, j--) {
swap(arr[i], arr[j]);
idx[arr[i]] = i;
idx[arr[j]] = j;
}
}
vector<int> pancakeSort(vector<int>& arr) {
vector<int> idx(arr.size() + 1); // 记录arr的每个元素在arr数组中的索引
vector<int> ret; // 存放k的值
int k = 0;
// 记录arr的每个元素在arr中的索引,由题知,arr中的元素是从1到arr.length的一个整数排列,则arr[i]的最小值为1,
// 对应到idx中,则索引是从1开始存储值的,因此idx的大小是arr.size() + 1
for (int i = 0; i < arr.size(); i++) {
// 技巧:索引是0、1、2、3...递增的,arr中值最大的数对应着idx的最大索引,arr中第二大的数对应着idx
// 的第二大索引,则根据idx的索引能依次找到arr中从小到大的值。
idx[arr[i]] = i;
}
// 从arr的最大值开始反转
for (int i = arr.size(); i >= 1; i--) {
// 第一步:将arr的最大值反转到arr的第1个位置。
if (idx[i] != 0) { // idx[i]的值为arr的元素在arr中的索引,当索引为0时,只有一个元素,此时不需要反转
k = idx[i] + 1; // 煎饼反转的策略是反转第1个元素到第k-1个元素,反转时要包含最大的值,因此索引要加1
ret.push_back(k);
reverse(arr, k, idx); // 对arr的第1个元素到第k-1个元素进行反转,并将反转后元素在arr中的索引更新到idx中。
}
// 第二步:将arr的最大值从第1个位置反转到最后一个位置。
if (i != 1) { // i=1对应着arr的索引0,此时只有1个元素,1个元素不需要反转。
k = i;
ret.push_back(k);
reverse(arr, k, idx);
}
}
return ret;
}
};
int main() {
vector<int> arr;
arr.push_back(3);
arr.push_back(2);
arr.push_back(4);
arr.push_back(1);
Solution *solution = new Solution();
vector<int> ret = solution->pancakeSort(arr);
for (int i = 0; i < ret.size(); i++) {
printf("%d ", ret[i]);
}
return 0;
}
- Python代码
# -*- coding: utf-8 -*-
from typing import List
class Solution:
# 对arr的第1个元素到第k - 1个元素进行反转,并将反转后每个元素在arr中的索引更新到idx数组中
def reverse(self, arr, k, idx):
i = 0
j = k - 1
while i < j:
arr[i], arr[j] = arr[j], arr[i]
idx[arr[i]] = i
idx[arr[j]] = j
i += 1
j -= 1
def pancakeSort(self, arr: List[int]) -> List[int]:
idx = [-1 for i in range(len(arr) + 1)] # 记录arr的每个元素在arr数组中的索引
ret = [] # 存放k的值
k: int = 0
# 记录arr的每个元素在arr中的索引,由题知,arr中的元素是从1到arr.length的一个整数排列,则arr[i]的最小值为1,
# 对应到idx中,则索引是从1开始存储值的,因此idx的大小是arr.size() + 1
for i in range(len(arr)):
# 技巧:索引是0、1、2、3...递增的,arr中值最大的数对应着idx的最大索引,arr中第二大的数对应着idx
# 的第二大索引,则根据idx的索引能依次找到arr中从小到大的值。
idx[arr[i]] = i
# 从arr的最大值开始反转
for i in range(len(arr), 0, -1):
# 第一步:将arr的最大值反转到arr的第1个位置。
if idx[i] != 0: # idx[i]的值为arr的元素在arr中的索引,当索引为0时,只有一个元素,此时不需要反转
k = idx[i] + 1 # 煎饼反转的策略是反转第1个元素到第k-1个元素,反转时要包含最大的值,因此索引要加1
ret.append(k)
self.reverse(arr, k, idx) # 对arr的第1个元素到第k-1个元素进行反转,并将反转后元素在arr中的索引更新到idx中。
# 第二步:将arr的最大值从第1个位置反转到最后一个位置。
if i != 1: # i=1对应着arr的索引0,此时只有1个元素,1个元素不需要反转。
k = i
ret.append(k)
self.reverse(arr, k, idx)
return ret
def main():
solution = Solution()
arr = [3, 2, 4, 1]
ret = solution.pancakeSort(arr)
print(ret)
if __name__ == "__main__":
main()
说明
- 对应LeetCode第969题。
- 链接:https://leetcode-cn.com/problems/pancake-sorting/