- 换位采用es6写法 [arr[i], arr[j]] = [arr[j], arr[i]],
一、 O(n^2)排序: 三个时间复杂度非常高的算法,冒泡排序、插入排序、选择排序
1、 冒泡排序
- 每次排列好最大的一个数字,排到后面
/**
* @param {number[]} nums
* @return {number[]}
*/
var bubbleSort = function(nums) {
let len = nums.length
if (len <= 1) {
return nums
}
for(let i =0; i<len; i++) {
// 每次排好最大的一个,j < len - 1 - i
for(let j=0; j < len - 1 - i; j++) {
// 交换
[nums[j] , nums[j+1]] = [nums[j+1],nums[j]]
}
}
return nums
};
2、插入排序
- i是要插入数据的位置,0~j是已排序好的位置。 第一步腾出需要插入的位置,第二步插入
/**
* @param {number[]} nums
* @return {number[]}
*/
var insertSort= function(nums) {
const len = nums.length
if(len <= 1) {
return nums
}
for(let i =1; i< len; i++) {
let j = i -1
let value = nums[i]
// 数据移动
for (;j >= 0; j--) {
if(nums[j] > value) {
nums[j+1] = nums[j ]
} else{
break
}
}
// 插入数据
nums[j +1] =value
}
return nums
};
3、 希尔排序-插入排序优化
- 增加外面一层for循环分组gap, 里面插入排序采用间隔gap
var sortArray = function(nums) {
shellSort(nums)
return nums
// 希尔函数
function shellSort(arr) {
let gap = Math.floor(arr.length/2)
// gap间隔
for(; gap > 0; gap = Math.floor(gap/2)) {
插入算法//
for(let i = gap; i< arr.length;i++) {
let j = i - gap // [0~j]之间排序好的数组
let value = arr[i] // 插入的值
// 移位,腾出要插入的值
for(;j >= 0;j -= gap) {
if(arr[j] > value) {
arr[j + gap] = arr[j]
} else{
break
}
}
// 插入
arr[j + gap] = value
}
}
}
}
4、选择排序
- 第一个循环是定义最小值位置,第二个循环找出最小值
var selectionSort = function(nums) {
let len = nums.length
if (len <= 1) {
return nums
}
for(let i = 0; i< len; i++) {
let min = i
// 找出最小值
for (let j = i; j< len; j++) {
if(nums[j] < nums[min]) {
min = j
}
}
// 交换
[nums[i], nums[min]] = [nums[min], nums[i]]
}
return nums
};
- 这三种排序时间算法最坏时间复杂度是O(n^2),插入排序和冒泡排序时间复杂度最好时时O(n),选择排序时间最优复杂度是O(n^2)。空间复杂度是O(1)都是原地排序,冒泡排序和插入排序都是稳定排序,选择排序是不稳定排序。
- 这三大排序算法时间复杂度都非常高,有2个for循环,适用小数据量。虽然时间复杂度差不多,但是冒泡排序和选择排序需要 3 个赋值操作,而插入排序只需要 1 个。只有插入排序能通过LeetCode排序题
- 希尔排序是插入排序的升级版,最好时间复杂度O(n)
二、O (nlog(n)): 分治思想、递归
1、归并排序
- 归并排序的时间复杂度是nlgn,缺点是空间复杂度是O(n),
- 网上有很些用到shift方法,这个方法导致数组移位,造成时间复杂度升高
// 注意千万不要用 result.push(leftArr.shift())
/**
* @param {number[]} nums
* @return {number[]}
*/
var mergeArray = function(nums) {
let len = nums.length
if(len <= 1) {return nums}
return mergeSort(nums)
function mergeSort(arr) {
// 终止条件
if(arr.length === 1) {
return arr
}
// 取中值
let mid = Math.floor((arr.length)/2)
// 递推公式
return merge(mergeSort(arr.slice(0, mid)), mergeSort(arr.slice(mid, arr.length)))
}
function merge(leftArr, rightArr) {
let result = []
let left = 0
let right = 0
while(left < leftArr.length && right <rightArr.length) {
if(leftArr[left] <= rightArr[right]) {
result.push(leftArr[left])
left ++
} else{
result.push(rightArr[right])
right++
}
}
return result.concat(leftArr.slice(left)).concat(rightArr.slice(right))
}
};
2、快排
- 快排的优点就是快。缺点是不稳定排序。巧妙的设计可以实现原地排序
- 快排的分区点选择不好的情况,时间复杂度上升至O(n^2),这个算法也是通过不了leetcode
- 下面的代码是实现原地排序,网上很多采用两个数组的算法导致空间复杂度上升。这样的快排优势全无
var sortArray = function(nums) {
let len = nums.length
if(len <= 1) {return nums}
quickSort(nums)
return nums
function quickSort(arr, start = 0, last = nums.length-1) {
// 终止条件
if(last - start < 1) {
return
}
let pivot = arr[last] // 分区点
let i= start // i 代表分区的位置,i之前的是已经处理好的
let j = start //代表处理的数据
// 遍历 数据,换位分区
for(; j< last; j++) {
if(arr[j] <= pivot) {
[arr[i],arr[j]] = [arr[j], arr[i]]
i++
}
}
// 分区点的位置换位
[arr[i], arr[j]] = [arr[j], arr[i]]
// 分区后 递归
quickSort(arr,start, i -1)
quickSort(arr, i+1, last)
}
};
- 代码实现原理如图
三、 线性排序
1、桶排序
var sortArray = function(nums) {
const size = 3
let buckets = []
let max= min = nums[0]
for(let i =0; i < nums.length; i++) {
if(nums[i]< min) {
min = nums[i]
} else if(nums[i] > max) {
max = nums[i]
}
}
// 生成桶
for(let i = 0; i < nums.length; i++) {
let j =~~((nums[i] - min) / size)
if(buckets[j]) {
buckets[j].push(nums[i])
}else{
buckets[j] = [nums[i]]
}
}
let result = []
for(let i = 0; i< buckets.length; i++) {
let sortArr = shellSort(buckets[i])
result = result.concat(sortArr)
}
return result
function shellSort(arr = []) {
let gap = Math.floor(arr.length/2)
for(;gap > 0;gap = Math.floor(gap/2)){
for(let i = gap; i<arr.length; i++) {
let insertValue = arr[i]
let j = i - gap
for(; j >=0; j-=gap) {
if(insertValue < arr[j]) {
arr[j + gap] = arr[j]
}else{
break
}
}
arr[j + gap] = insertValue
}
}
return arr
}
}
2、计数排序
var sortArray = function(nums) {
if(nums.length <=1) {return nums}
return countSort(nums)
function countSort(arr) {
let min = max = arr[0]
// 查找最小值
for(let i =0; i < arr.length;i++) {
if(arr[i] < min) {
min = arr[i]
} else if(max < arr[i]) {
max = arr[i]
}
}
let arrLen = max + 1 - min
let countArr = new Array(arrLen)
for (let i = 0; i < arr.length; i++) {
let index = arr[i] -min // 相对位移
if(countArr[index]) {
countArr[index]++
} else {
countArr[index] = 1
}
}
let result = []
for(let j = 0; j< countArr.length; j++) {
if(countArr[j]) {
while(countArr[j] > 0) {
result.push(j+min)
countArr[j] --
}
}
}
return result
}
}
3、基数排序
Radix Sort,⾮⽐较型整数排序,将所有待⽐较数值(正整数)统⼀为同样的数位⻓
度,数位较短的数前⾯补零。然后,从最低位开始,依次进⾏⼀次排序。这样从最低位
排序⼀直到最⾼位排序完成以后,数列就变成⼀个有序序列。
var sortArray = function(nums) {
if(nums.length <= 1) {return nums}
let max = Math.max(...nums)
let maxIndend = 1
while(max = Math.floor(max/10) ) {
maxIndend ++
}
let indent = 1
let count = []
let dev = 1
let mod = 10
for(let i =0; i< maxIndend; i++) {
count = []
// 按位数排序
for(let j =0;j<nums.length; j++) {
let bucket = Math.floor(nums[j] % mod / dev)
if(count[bucket]){
count[bucket].push(nums[j])
}else{
count[bucket] = [nums[j]]
}
}
let index = 0
// 按位数重新放进数组
for(let j = 0; j < count.length; j++) {
if(count[j]){
while(count[j].length) {
nums[index++] = count[j].shift()
}
}
}
dev *= 10
mod *= 10
}
return nums
}
四、堆排序
Heap Sort,是指利⽤堆这种数据结构所设计的⼀种排序算法。
var sortArray = function (nums) {
function max_heapify(arr, start, end) {
let parentIndex = start,sonIndex = parentIndex * 2 + 1
if(sonIndex >= end) return
// right > left
if(sonIndex + 1 < end && arr[sonIndex + 1] > arr[sonIndex]) {
sonIndex ++
}
// 父子节点交换
if(arr[sonIndex] > arr[parentIndex]) {
[arr[sonIndex], arr[parentIndex]] = [arr[parentIndex], arr[sonIndex]]
}
max_heapify(arr, sonIndex, end)
}
let len = nums.length
if(len<2) { return nums }
// 有子节点的构建大堆顶
let i = (len >> 1) - 1
for(; i >= 0; i--) {
max_heapify(nums, i ,len)
}
// 最大值移到最后面
for(let j = len - 1; j >= 0; j--){
[nums[0],nums[j]] = [nums[j], nums[0]]
max_heapify(nums, 0 , j)
}
return nums
}