这篇文章主要记录下几种常见的排序算法,以递归实现为主:
1、选择排序
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。
时间复杂度:O(n^2)
//递归实现方式
let sort = (numbers) => {
if (numbers.lenght > 2) {
let index = minIndex(numbers)
let min = numbers[index]
numbers.splice(index, 1)
return [min].concat(sort(numbers))
} else {
return numbers[0] > numbers[1] ? numbers.reverse() : numbers
}
}
let minIndex = (numbers) => indexOf(min(numbers)) //获取最小下标函数
let min = (numbers) => { if (numbers.lengh > 2) { return min([numbers[0], min(numbers.slice(1))]) } else { return Math.min.apply(null, numbers) }}
//循环实现方式
let sort = (numbers) => { if (numbers.length > 2) { let index = minIndex(numbers) let min = numbers[index] numbers.splice(index, 1) return [min].concat(sort(numbers)) } else { return numbers[0] > numbers[1] ? numbers.reverse() : numbers }}let swap = (numbers, a, b) => { //定义一个调换下标的函数swap c = numbers[a] numbers[a] = numbers[b] numbers[b] = c}
2、快速排序
打个比方:想象你是一个体育委员,你面对着一群学号不一致的同学,这个时候你喊“以某同学为基准,小的去前面,大的前后面,只要重复这句话,就可以排序。
快速排序的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
时间复杂度:O(n log2n)
let quickSort = (arr) => {
if (arr.lenght <= 1) {
return arr
} else {
let minddlenumber = Math.floor(arr.length / 2) //选定排序基准
let middle = arr[minddlenumber]
let left = [] //部署两个数列,用于添加排序项
let right = []
arr.splice(minddlenumber, 1)
for (let i = 0; i < arr.length; i++) { //大于基准项的去右边,小的去左边
if (arr[i] < middle) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return quickSort(left).concat([middle], quickSort(right))
} //返回数组,并将两边的数组继续进行quicksort排序递归,直到只剩一项直接返回
3、归并排序
归并排序比较难以理解,重点在于merge算法将排序数组内的每一项都拆分出来进行排序,
已有序的子序列合并,得到完全有序的序列;
时间复杂度:O(n log2n)

let mergeSort = arr => {
let k = arr.length
if (k === 1) { return arr }
let left = arr.slice(0, Math.floor(k / 2))
let right = arr.slice(Math.floor(k / 2))
return merge(mergeSort(left), mergeSort(right))
}
}let merge = (a, b) => {
if (a.length === 0) return b
if (b.length === 0) return a
return a[0] > b[0] ? [b[0]].concat(merge(a, b.slice(1))) : [a[0]].concat(merge(a.slice(1), b)
)}
4、计数排序
计数排序引入了一个数据结构哈希表,用其做记录,大致思路为发现数字N就记为N:1,如果再次发现N就加1,最后把哈希表的key全部打出来,假设N:m,那么n需要打印m次
let countSort = arr => { let hashTable = {}, max = 0, result = [] for (let i = 0; i < arr.length; i++) { // 遍历数组 if (!(arr[i] in hashTable)) { hashTable[arr[i]] = 1 } else { hashTable[arr[i]] += 1 } if (arr[i] > max) { max = arr[i] } } for (let j = 0; j <= max; j++) { // 遍历哈希表 if (j in hashTable) { for (let i = 0; i < hashTable[j]; i++) { result.push(j) } } } return result}
它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。 当然这是一种牺牲空间换取时间的做法。
时间复杂度:Ο(n+k)
5、冒泡排序
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。 这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
时间复杂度:Ο(n^2)
function bubbleSort(arr) { var i = arr.length, j; var tempExchangVal; while (i > 0) { for (j = 0; j < i - 1; j++) { if (arr[j] > arr[j + 1]) { tempExchangVal = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tempExchangVal; } } i--; } return arr;}
还有堆排序等较复杂的排序算法没有介绍,后面会在更新一篇。
———— 部分信息来自饥人谷、网络公开资料