前言
没想到有一天我们会把摊煎饼的技术运用到算法解题上,当然是联想,我们前面做过的链表翻转就是这个道理,今天我们要实现的是数组翻转,一个道理。
题目描述
969. 煎饼排序
给你一个整数数组 arr ,请使用 煎饼翻转 完成对数组的排序。
一次煎饼翻转的执行过程如下:
选择一个整数 k ,1 <= 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:
输入:[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 <= arr.length <= 100
1 <= arr[i] <= arr.length
arr 中的所有整数互不相同(即,arr 是从 1 到 arr.length 整数的一个排列)
解题思路
- 此题的概要就是将一个非重复数字数组通过k次翻转得到一个
升序的数字数组,而我们要获得最终的结果就是每次翻转数组元素的个数的一个集合。 - 就是说我们每次翻转后需要将本次翻转的元素个数记录下来push到一个数组中
- 如何翻转?
- 升序数组,我们可以
先把数组中元素最大的给翻到最前面,左一翻,然后再右一翻,给最大的再给翻到末尾,这就完成了升序数组中一个数的翻转,记录下这两次翻转的元素个数,因为数组下标从0开始,所以每次数组最大值的索引+1就是翻转元素的记录个数,记录完后我们让翻转完成的数pop出去 - 注意:
1)当最大值下标索引为末位时无需翻转
2)当最大值下标为首位时,只需要进行第二步首尾翻转 - 如此循环遍历数组,直至数组中
还剩一个元素,我们就不用再翻转了
开始解题
/**
* @param {number[]} arr
* @return {number[]}
*/
var pancakeSort = function(arr) {
let curArr = arr;
let resArr = [];
while(curArr.length>1) { // 数组中还剩最后一个就不用再翻了
const maxIndex = getMaxIndex(curArr); // 获取最大值下标
if(maxIndex){
if(maxIndex !== curArr.length-1){ // 如果最大值下标不是最后一个元素才需要翻转
resArr.push(maxIndex+1);
reverse(curArr,maxIndex);
reverse(curArr, curArr.length-1);
resArr.push(curArr.length);
}
}else { // 如果最大值下标为0,那么就直接进行第二步的首尾翻转
reverse(curArr, curArr.length-1);
resArr.push(curArr.length);
}
curArr.pop(); // 翻转之后将已完成翻转升序任务的末尾数出栈
}
return resArr;
};
var getMaxIndex = function(arr) {
let maxIndex=0;
for(let i = 0; i < arr.length; i++) {
if(arr[i] > arr[maxIndex]){
maxIndex = i;
}
}
return maxIndex;
}
var reverse = function(arr, maxIndex) {
let i = 0,j = maxIndex - i; // maxIndex其实就是要翻转的末位数下标
while(i < j) { // 这里的翻转可以简写[arr[i],arr[j]] = [arr[j],arr[i]],但是用到的内存会增加
let temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
i++;
j--;
}
}