排序算法 | 平均时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 是否稳定排序 |
---|---|---|---|---|
冒泡排序 | O(n2) | O(n2) | O(1) | 稳定 |
鸡尾酒排序 | O(n2) | O(n2) | O(1) | 稳定 |
桶排序 | O(n) | O(nlogn) | O(n) | 稳定 |
快速排序 | O(nlogn) | O(n2) | O(logn) | 不稳定 |
桶排序
简单的桶排序
- 栗子
一场考试有5名同学,满分10分,5名同学得到的分数分别为5分,3分,5分,2分,8分,请给同学进行排序 - JS简易版桶排序算法实现
var bucketSort = function(arr, len, rule = 'desc') {
if (!arr || arr.length < 1 || !len) return
let tempArr = new Array(len)
let returnArr = []
for (let i = 0; i < len; i++) tempArr[i] = 0
for (let j = 0; j < arr.length; j++) {
tempArr[arr[j]] += 1
}
if (rule === 'desc') {
for (let i = 0; i < len; i++) {
for (let j = 1; j <= tempArr[i]; j++) {
returnArr.push(i)
}
}
} else {
for (let i = len; i > 0; i--) {
for (let j = 1; j <= tempArr[i]; j++) {
returnArr.push(i)
}
}
}
return returnArr
};
console.log(bucketSort([5,3,5,2,8], 11, 'asc'))
-
学习讲解
上述代码中新建大小为11的数组,数组下表代表的是成绩分布情况(0到10分)
简易版桶排序排序思路:将同学的成绩按照各自所对应的数组下标“倒进去”,多一个人,桶内数字加一;最后按照桶内数字的大小进行每个下标的循环输出 -
算法说明
时间复杂度:O(M+N)
时间复杂度说明:
第一个循环–桶的数量—循环M次
待排序的列表循环–同学的分数—循环N次
循环输出–循环M+N次
整个算法循环M+N+M+N次 = O(2*(M+N)) = O(M+N)
冒泡排序
- JS 实现
var bubbleSort = function(arr) {
if (!arr || arr.length < 1) return
let returnArr = arr
for (let i = 0; i < returnArr.length; i++) {
for (let j = 0; j < returnArr.length; j++) {
if (returnArr[j] > returnArr[j+1]) {
let temp = returnArr[j+1]
returnArr[j+1] = returnArr[j]
returnArr[j] = temp
}
}
}
return returnArr
};
console.log(bubbleSort([8,100,50,22,15,6,1,1000,99,0]))
-
学习讲解
冒泡排序的基本思想是:每次比较相邻的两个元素,顺序错误就交换 -
算法说明
时间复杂度:O(N2)
- 冒泡排序优化实现-1
var sortArray = function(nums) {
if(!nums || nums.length < 1) return nums
for (let i = 0; i < nums.length; i++) {
let isSorted = true
for (let j = 0; j < nums.length - 1; j++) {
if (nums[j] > nums[j+1]) {
let temp = nums[j]
nums[j] = nums[j+1]
nums[j+1] = temp
isSorted = false
}
}
if (isSorted) break
}
return nums
};
- 学习讲解
添加了一个标志isSorted 用来标志当前数组是否已经是有序的了,如果是有序的了,就不再继续循环
- 冒泡排序优化实现-2
var sortArray = function(nums) {
if(!nums || nums.length < 1) return nums
let lastExchangeIndex = 0 // 记录最后一次交换位置
let sortBorder = nums.length - 1 // 无序数组的边界
for (let i = 0; i < nums.length - 1; i++) {
let isSorted = true
for (let j = 0; j < sortBorder; j++) {
if (nums[j] > nums[j+1]) {
let temp = nums[j]
nums[j] = nums[j+1]
nums[j+1] = temp
isSorted = false
lastExchangeIndex = j
}
}
sortBorder = lastExchangeIndex
if (isSorted) break
}
return nums
};
- 学习讲解
添加一个无序数组循环的边界值,这样每次循环的时候只循环在边界值就好了
- 冒泡排序升级版–鸡尾酒排序
- JS实现
var sortArray = function(nums) {
if(!nums || nums.length < 1) return nums
for (let i = 0; i < nums.length/2; i++) {
let isSorted = true
// 从左向右比较和交换
for (let j = i; j < nums.length - i - 1; j++) {
if (nums[j] > nums[j+1]) {
let temp = nums[j]
nums[j] = nums[j+1]
nums[j+1] = temp
isSorted = false
}
}
if (isSorted) break
// 从右向左比较和交换
isSorted = true
for (let j = nums.length - i - 1; j > i; j--) {
if (nums[j] < nums[j-1]) {
let temp = nums[j]
nums[j] = nums[j-1]
nums[j-1] = temp
isSorted = false
}
}
if (isSorted) break
}
return nums
};
- 学习讲解
鸡尾酒排序的比较和交换过程是双向的,排序过程就像钟摆一样。算法的优点是能够在特定的条件下减少排序的回合数;最好是能在大部分元素已经有序的情况下使用哦
快速排序
分治法:在每一轮挑选一个基准元素,并让其他比它大的元素移动到数列一边,比他小的元素移动到数列另一边,从而把数列拆解成两个部分
- 双边循环JS 实现
var sortArray = function(nums) {
function quickSort(array, start = 0, end = array.length - 1) {
if (start < end) {
// 取基准值,将array的每个元素与基准值大小比对后放在基准值的左边或者右边
let pivot = partition(array, start, end);
// 对小于基准值的元素排序
quickSort(array, start, pivot - 1);
// 对大于基准值的元素排序
quickSort(array, pivot + 1, end);
}
return array;
}
var partition = function(arr, startIndex, endIndex) {
// 基准元素的选取
let pivot = arr[startIndex]
let left = startIndex
let right = endIndex
while (left !== right) {
while (left < right && arr[right] > pivot) {
right--
}
while (left < right && (arr[left] < pivot || arr[left] === pivot)) {
left++
}
if (left < right) {
let temp = arr[left]
arr[left] = arr[right]
arr[right] = temp
}
}
arr[startIndex] = arr[left]
arr[left] = pivot
return left
}
return quickSort(nums)
};
-
学习讲解
双边循环需要两个指针left,right,代表了每次比较的区间;双边循环,如同名字一样,从数据两边进行循环,先右边循环后左边循环,依次进行 -
单边循环JS 实现
/**
* @param {number[]} nums
* @return {number[]}
*/
var sortArray = function(nums) {
if (!nums || nums.length < 1) return nums
let startIndex = 0, lastIndex = nums.length - 1;
var quickSort = function(array, start, end) {
if (start < end) {
// 取基准值,将array的每个元素与基准值大小比对后放在基准值的左边或者右边
let pivot = partition(array, start, end);
// 对小于基准值的元素排序
quickSort(array, start, pivot - 1);
// 对大于基准值的元素排序
quickSort(array, pivot + 1, end);
}
return array;
}
var partition = function(arr, startIndex, endIndex) {
// 基准元素的选取
let pivot = arr[startIndex]
let mark = startIndex
for (let i = startIndex + 1; i <= endIndex; i++) {
if (arr[i] < pivot) {
mark++
let temp = arr[mark];
arr[mark] = arr[i];
arr[i] = temp
}
}
arr[startIndex] = arr[mark]
arr[mark] = pivot
return mark
}
return quickSort(nums, startIndex, lastIndex)
};
- 学习讲解
单边循环只需要一个指针mark - 非递归JS 实现
/**
* @param {number[]} nums
* @return {number[]}
*/
var sortArray = function(nums) {
if (!nums || nums.length < 1) return nums
var quickSort = function(array, start, end) {
let arrStack = [{startIndex: start, endIndex: end}]
while(arrStack.length > 0) {
let param = arrStack.pop();
let pivot = partition(array, param.startIndex, param.endIndex)
if (param.startIndex < pivot - 1) {
let leftParam = {
startIndex: param.startIndex,
endIndex: pivot - 1
}
arrStack.push(leftParam)
}
if (pivot + 1 < param.endIndex) {
let rightParam = {
startIndex: pivot + 1,
endIndex: param.endIndex
}
arrStack.push(rightParam)
}
}
return array;
}
var partition = function(arr, startIndex, endIndex) {
// 基准元素的选取
let pivot = arr[startIndex]
let mark = startIndex
for (let i = startIndex + 1; i <= endIndex; i++) {
if (arr[i] < pivot) {
mark++
let temp = arr[mark];
arr[mark] = arr[i];
arr[i] = temp
}
}
arr[startIndex] = arr[mark]
arr[mark] = pivot
return mark
}
let startIndex = 0, lastIndex = nums.length - 1;
return quickSort(nums, startIndex, lastIndex)
};
堆排序
- JS实现
var sortArray = function(nums) {
let downAdjust = (array, parentIndex, length) => {
let temp = array[parentIndex] // 父节点值,用于最后的赋值
let childIndex = 2 * parentIndex + 1;
while (childIndex < length) {
if (childIndex + 1 < length && array[childIndex + 1] > array[childIndex]) {
childIndex++;
}
if (temp >= array[childIndex]) {
break;
}
array[parentIndex] = array[childIndex]
parentIndex = childIndex
childIndex = 2 * childIndex + 1
}
array[parentIndex] = temp
}
};
- 学习讲解
堆排序的算法步骤:
- 把无序数组构建成二叉堆。需要从小到大排序,则构建成最大堆;需要从大到小排序,则构建成最小堆
- 循环删除堆顶元素,替换到二叉堆的末尾,调整堆产生新的堆顶