日新刷题 - 324. 摆动排序 II

106 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第13天,点击查看活动详情

一 描述

324. 摆动排序 II - 力扣(LeetCode) 给你一个整数数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]... 的顺序。

你可以假设所有输入数组都可以得到满足题目要求的结果。

 

示例 1:

输入:nums = [1,5,1,1,6,4]
输出:[1,6,1,5,1,4]
解释:[1,4,1,5,1,6] 同样是符合题目要求的结果,可以被判题程序接受。

示例 2:

输入:nums = [1,3,2,2,3,1]
输出:[2,3,1,3,1,2]

提示:

  • 1 <= nums.length <= 5 * 104
  • 0 <= nums[i] <= 5000
  • 题目数据保证,对于给定的输入 nums ,总能产生满足题目要求的结果  

进阶:你能用 O(n) 时间复杂度和 / 或原地 O(1) 额外空间来实现吗?

二 分析

排序。

观察题目,可知,可以对数组排序,然后等分为左右子数组,保证较小值的子数组的长度不小于另一子数组长度。

然后两个数组穿插排序,即归并排序的最后一步。

进阶

需要O(n)的时间复杂度或者O(1)的空间复杂度

因为排序方法的时间复杂度是nlogn,所以为了n的时间复杂度,不能全部排序。

观察可知,不需要全部排序,我们只需要将数组分为左右子数组,保证左数组的值全部小于等于右数组即可。

所以用快速选择算法,得到中位数,将数组分为左、中、右三个子数组,然后归并。

三 答案


/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
const getMid = (left, right, nums) => {
  const l = nums.length;
  let mid = nums[~~(l / 2)];
  const leftL = [],
    rightL = [];
  let v = 0;
  for (const n of nums) {
    if (n > mid) {
      rightL.push(n);
    } else if (n < mid) {
      leftL.push(n);
    } else {
      v++;
    }
  }
  if (Math.abs(left + leftL.length - right - rightL.length) <= v) return mid;
  if (left + leftL.length > right + rightL.length) {
    return getMid(left, rightL.length + right + v, leftL);
  } else {
    return getMid(left + leftL.length + v, right, rightL);
  }
};
var wiggleSort = function (nums) {
  const l = nums.length;
  const mid = getMid(0, 0, nums.slice());
  const left = [],
    right = [];
  let v = 0;
  for (const n of nums) {
    if (n > mid) {
      right.push(n);
    } else if (n < mid) {
      left.push(n);
    } else {
      v++;
    }
  }
  const c = left.length - right.length;
  if (c == 0) {
    left.splice(0, 0, ...new Array(Math.ceil(v / 2)).fill(mid));
    right.push(...new Array(~~(v / 2)).fill(mid));
  } else if (c > 0) {
    v -= c;
    left.splice(0, 0, ...new Array(Math.ceil(v / 2)).fill(mid));
    right.push(...new Array(~~(v / 2) + c).fill(mid));
  } else {
    v += c;
    left.splice(0, 0, ...new Array(Math.ceil(v / 2) - c).fill(mid));
    right.push(...new Array(~~(v / 2)).fill(mid));
  }
  for (let i = 0; i < l; i++) {
    if (i % 2) {
      nums[i] = right.shift();
    } else {
      nums[i] = left.shift();
    }
  }
  return nums;
};