【路飞】煎饼排序

112 阅读2分钟

记录一道算法题

煎饼排序

969. 煎饼排序 - 力扣(LeetCode) (leetcode-cn.com)


要求:

    * 每次从 0 到 n 进行翻转
    * 数组是从 1 到 n 的升序数组进行打乱之后的数组
    * 用数组记录每次翻转的个数,直到恢复升序排列

看了给的例子并没有发现什么规律,官方给的例子是

    输入:[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],此时已完成排序。

完全没看出有什么规律,但如果数组是升序的就不需要进行翻转,输出的结果是 [], 然后第二次翻转其实提供了灵感,当最大的数在第一个位置的时候,这时候整体翻转整个数组,就可以得到 [3, 2, 1, 4], 这时候已经把一个数归位了。然后下次就是再整体翻转的时候就是 arr.length - 1,以此类推。

  1. 首先找到最大的数字的下标,如果不是在最后一位就进行翻转,如果是就直接下一轮查找
  2. 如果找到了是在第1位,直接翻转整个数组,如果在其他位置就翻转相关的个数。
    function reverse(arr, count) {
        // 根据个数算出中间的位置
        let k = count % 2 === 0 ? count / 2 : count >> 1
        for(let i = 0; i < k; i++) {
            const temp = arr[i]
            arr[i] = arr[count - 1 - i]
            arr[count- 1 - i] = temp
        }
    }
    
    function pancakeSort(arr) {
        let result = []
        let n = arr.length
        while(n > 1) {
            // 只要确定 length - 1 个的位置正确就可以,所以少一个
            let i = 0
            // n是还未确定的个数,后续会递减 相当于 [1, 3, 1, 4],就不用遍历全部数组
            for(; i < n; i++) {
                if(arr[i] === n) {
                    break
                    // 因为是升序所以刚好 n 等于未确定位置的最大数
                }
            }
            
            if (i === n) {
                // 找完一轮,如果最大的数在正确的位置就不用翻转
                n--
                continue
            }
            
            if (i === 0) {
                // 第一个就翻转全部, 这时有一个数会被正确归位
                reverse(arr, n)
                result.push(n)
                n--
            } else {
                reverse(arr, i + 1)
                result.push(i + 1)
            }
        }
        
        return result
    }

以上并不是次数最少的方法。但是也能做出来。不知道官方给的例子是怎么算出来的,只要4次翻转。