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] 举例,从左往右依次冒泡将大的往右移动按照升序排列。
- 第一轮第一次比较:5 和 2 比较;发现 5 比 2 大 交换位置。
[5, 2, 3, 1] ==> [2, 5, 3, 1]。 - 第一轮第二次比较:5 和 3 比较;发现 5 比 3 大 交换位置。
[2, 5, 3, 1] ==> [2, 3, 5, 1]。 - 第一轮第三次比较:5 和 1 比较;发现 5 比 1 大 交换位置。
[2, 3, 5, 1] ==> [2, 3, 1, 5]。 - 第一轮比较结束:最大的已经排在最末尾了。
[2, 3, 1, 5]。 - 第二轮第一次比较:2 和 3 比较;发现 2 比 3 小,不做改动。
- 第二轮第二次比较:3 和 1 比较;发现 3 比 1 大,交换位置。
[2, 3, 1, 5] ==> [2, 1, 3, 5]。 - 第二轮比较结束。
- ...
代码实现
我们看到嵌套循环,应该立马就可以得出这个算法的时间复杂度为 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 个(5)交换位置 。
[5, 2, 3, 1] ==> [1, 2, 3, 5]。 - 第二次选择:找到最小的数字 2 与 数组第 2 个(2)交换位置(如果一样可以判断不交换) 。
[1, 2, 3, 5] ==> [1, 2, 3, 5]。 - 第三次选择:找到最小的数字 3 与 数组第 3 个(3)交换位置(如果一样可以判断不交换) 。
[1, 2, 3, 5] ==> [1, 2, 3, 5]。 - ...
代码实现
双层循环,时间复杂度和冒泡一模一样,都是 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 春招闯关活动」, 点击查看活动详情。