「这是我参与2022首次更文挑战的第21天,活动详情查看:2022首次更文挑战」
编程世界总是离不了算法
最近在看框架源码时,会有很多算法的实现逻辑,有时候会感到吃力
于是决定蹭着假期,加强算法和数据结构相关的知识
那怎么提升呢?
其实我知道算法这东西没有捷径,多写多练才能提升,于是我开启我的LeetCode刷题之旅
第一阶段目标是:200道,每天1到2篇
为了不乱,本系列文章目录分为三部分:
- 今日题目:xxx
- 我的思路
- 代码实现
今天题目: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],此时已完成排序。 示例 2:
输入:[1,2,3] 输出:[] 解释: 输入已经排序,因此不需要翻转任何内容。 请注意,其他可能的答案,如 [3,3] ,也将被判断为正确。
我的思路
首先解读题目,翻译过来意思是说:有一个长度为n的数组arr,元素范围是1至n(元素不重复,可乱序),定义一次以 k 为参数的翻转操作是将下标为 [0, k-1] 的子数组进行reverse反转,求经过多少次翻转操作可以使得arr变成从1开始的增序数组,给出每次翻转的k序列(其中,每次翻转的k参数可为不超过n的任意正整数,k序列长度不超过 10*n)
对于这一题,直接看最终目标,变成一个从小到大排序的数组。先从翻转操作思考,每次翻转对数组有什么影响呢?可以发现翻转只影响了前k个元素,对后续的元素是无任何影响的(已经做好的煎饼就不用再加工了,再加工就糊了)。因此,我们应该从数组尾巴下手,而数组尾巴的元素是最大的元素,那么问题就转化为了:把数组中最大值依次放到数组末尾,即把熟了的鸡蛋饼放大末尾。看到这里,解决方案基本出来了,这就是冒泡排序阿,只是每次排序通过煎饼规则来弄。
那怎么进行煎饼规则的排序呢?假设现在已经知道了最大值的下标idx,要把它放到末尾下标end处,存在两种情况:
最大值已经是在末尾了,即idx == end,此时就不用再翻转了,已经达到了目的 最大值不在末尾处,即idx != end,此时就需要进行两次翻转了: 第一次翻转 [0, idx] 将最大值放到数组首位,此时 k=idx+1 第二次翻转 [0, end] 将最大值从首位放到 end 处,此时 k=end+1 如此从后向前依次寻找 [0, end] 范围内的最大值并进行翻转就可以煎出一串熟鸡蛋饼了 算法思路 从数组尾开始向前遍历,数组尾标记为 end 每次遍历时寻找下标为 [0, end] 的子数组中最大值对应的下标 idx 比较 idx 与 end 的排序关系: 二者相等,说明最大值已经在末尾,不进行操作 二者不相等,最大值不在末尾,进行两次翻转使得最大值排到末尾,翻转子数组分别为 [0, idx] 和 [0, end],并更新结果序列 直至 end 为第0项时返回结果
代码实现
/**
* @param {number[]} arr
* @return {number[]}
*/
var pancakeSort = function(arr) {
// 翻转函数:将下标尾 [0,idx] 的数组进行翻转
const reverse = (idx)=>{
for(let i=0, j=idx; i<j; i++, j--){
[arr[i], arr[j]] = [arr[j], arr[i]]
}
}
const n = arr.length;
const ans = [];
// 从尾开始,逐步将最大值放大末尾
for(let end = n-1; end > 0; end--){
// 寻找下标为[0, end]子数组里最大值的下标
let idx = end;
for( ; idx>0; idx--){
if(arr[idx] === end+1){
break
}
}
// 只有当最大值下标不是在末尾时,才需要移动
if(idx !== end){
// 如果最大值下标不是在数组首位,则先将最大值翻转到数组首位
if(idx > 0){
ans.push(idx+1);
reverse(idx)
}
// 然后再从尾翻转,从而使得最大值放到尾部
ans.push(end+1);
reverse(end);
}
}
return ans
};
总结
实现方式其实有很多,这里仅供参考~
由于刚开始刷题,也不知道从哪里刷好,如果前辈们有好的建议,希望不吝赐教,感谢🌹