912.排序数组|刷题打卡

296 阅读3分钟

912.排序数组

题目描述

给你一个整数数组 nums,请你将该数组升序排列。

示例 1:

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

示例 2:

输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]

提示:

1. 1 <= nums.length <= 50000
2. -50000 <= nums[i] <= 50000

解法一之冒泡排序

冒泡排序:从序列的一端开始往另一端冒泡,依次比较相邻两个数的大小。

[5, 2, 3, 1] 举例,从左往右依次冒泡将大的往右移动按照升序排列。

  1. 第一轮第一次比较:5 和 2 比较;发现 5 比 2 大 交换位置。[5, 2, 3, 1] ==> [2, 5, 3, 1]
  2. 第一轮第二次比较:5 和 3 比较;发现 5 比 3 大 交换位置。[2, 5, 3, 1] ==> [2, 3, 5, 1]
  3. 第一轮第三次比较:5 和 1 比较;发现 5 比 1 大 交换位置。[2, 3, 5, 1] ==> [2, 3, 1, 5]
  4. 第一轮比较结束:最大的已经排在最末尾了。[2, 3, 1, 5]
  5. 第二轮第一次比较:2 和 3 比较;发现 2 比 3 小,不做改动。
  6. 第二轮第二次比较:3 和 1 比较;发现 3 比 1 大,交换位置。[2, 3, 1, 5] ==> [2, 1, 3, 5]
  7. 第二轮比较结束。
  8. ...

代码实现

我们看到嵌套循环,应该立马就可以得出这个算法的时间复杂度为 O(n2)。

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function (nums) {
  if (nums.length <= 1) return nums

  // 从前往后冒泡 最后一个不用比较;所以比较轮数是 nums.length - 1
  for (let i = 0; i < nums.length - 1; i++) {
    // 最后一个不用比较并且每比较一轮都会多出一个有序的在后面。所以结束位置是 nums.length - 1 - i
    for (let j = 0; j < nums.length - 1 - i; j++) {
      if (nums[j] > nums[j + 1]) {
        ;[nums[j], nums[j + 1]] = [nums[j + 1], nums[j]]
      }
    }
  }

  return nums
}
// ===================================================================
// ========================== @test ==================================
// ===================================================================
console.log(sortArray([5, 2, 3, 1])) // [ 1, 2, 3, 5 ]
console.log(sortArray([5, 1, 1, 2, 0, 0])) // [ 0, 0, 1, 1, 2, 5 ]
console.log(sortArray([1])) // [1]
console.log(sortArray([1, 2, 3, 4, 5])) // [1, 2, 3, 4, 5]

冒泡优化

冒泡有一个最大的问题就是这种算法不管不管你有序还是没序,都是完整的循环。比如 [ 1, 2, 3, 5 ] 一个有序的数组,根本不需要排序。

针对这个问题,我们可以设定一个临时遍历来标记该数组是否已经有序,如果有序了就不用遍历了。

var sortArray = function (nums) {
  if (nums.length <= 1) return nums

  // 从前往后冒泡 最后一个不用比较;所以比较轮数是 nums.length - 1
  for (let i = 0; i < nums.length - 1; i++) {
    let flag = false

    // 最后一个不用比较并且每比较一轮都会多出一个有序的在后面。所以结束位置是 nums.length - 1 - i
    for (let j = 0; j < nums.length - 1 - i; j++) {
      if (nums[j] > nums[j + 1]) {
        ;[nums[j], nums[j + 1]] = [nums[j + 1], nums[j]]

        flag = true
      }
    }

    // 如果这次循环都没有数字被交换 说明已经是排序好的数组
    if (!flag) return nums
  }

  return nums
}

解法二之选择排序

选择排序:首先,找到数组中最小的元素,拎出来,将它和数组的第一个元素交换位置,第二步,在剩下的元素中继续寻找最小的元素,拎出来,和数组的第二个元素交换位置,如此循环,直到整个数组排序完成。找最大的元素也是一样的。

[5, 2, 3, 1] 举例按照升序排列。

  1. 第一次选择:找到最小的数字 1 与 数组第 1 个(5)交换位置 。[5, 2, 3, 1] ==> [1, 2, 3, 5]
  2. 第二次选择:找到最小的数字 2 与 数组第 2 个(2)交换位置(如果一样可以判断不交换) 。[1, 2, 3, 5] ==> [1, 2, 3, 5]
  3. 第三次选择:找到最小的数字 3 与 数组第 3 个(3)交换位置(如果一样可以判断不交换) 。[1, 2, 3, 5] ==> [1, 2, 3, 5]
  4. ...

代码实现

双层循环,时间复杂度和冒泡一模一样,都是 O(n2)。

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function (nums) {
  if (nums.length <= 1) return nums

  for (let i = 0; i < nums.length; i++) {
    let min = i // 最小元素的下标(先假设是当前自己)
    // 不用和自己比较 && 每一次选择都会多一个有序的在前面,所以只需要查找剩余位置,从 j = i + 1 开始。
    for (let j = i + 1; j < nums.length; j++) {
      if (nums[j] < nums[min]) {
        min = j // 找最小值
      }
    }
    // 交换位置
    ;[nums[i], nums[min]] = [nums[min], nums[i]]
  }

  return nums
}
// ===================================================================
// ========================== @test ==================================
// ===================================================================
console.log(sortArray([5, 2, 3, 1])) // [ 1, 2, 3, 5 ]
console.log(sortArray([5, 1, 1, 2, 0, 0])) // [ 0, 0, 1, 1, 2, 5 ]
console.log(sortArray([1])) // [1]
console.log(sortArray([1, 2, 3, 4, 5])) // [1, 2, 3, 4, 5]

本文参与

本文正在参与「掘金 2021 春招闯关活动」, 点击查看活动详情