JavaScript实现排序
选择排序
描述:遍历数组,在数组中选择最小的数字,排列在第一位(与原本第一位的数进行交换),此时第一位的数排好;缩小数组的范围,起始位置在2,遍历剩下的数,选择最小的放在第二位,依次类推。时间复杂度是O(N²)
代码实现:
function chooseSort(arr){
for(let i=0;i<arr.length;i++){
let min = arr[i];
let index = i
for(let j=i+1;j<arr.length;j++){
if(arr[j]<min){
min = arr[j]
index = j
}
}
// 交换两个数
let tmp = arr[i]
arr[i] = arr[index]
arr[index] = tmp
}
return arr
}
冒泡排序
描述:相邻的两个数进行比较,如果前面的数比后面的数大,那么将前面的数与后面的数进行交换,换到最后面的值就是数组中最大的数,即最后一位的数排列正确;缩小右边界,继续排列。时间复杂度为O(N²)
代码实现:
function bubbleSort(arr){
console.log(arr)
let n = arr.length;
// console.log(n)
for(let i=n;i>=0;i--){
for(let j = 0;j<i-1;j++){
if(arr[j+1]<arr[j]){
let tmp = arr[j]
arr[j] = arr[j+1]
arr[j+1] = tmp
}
}
}
return arr
}
插入排序
描述:首先保证数组中0-0上有序,不需要操作,只有一个数字,一定有序。
保证0-1上有序,若在1位置的数比在0位置的数要小,则需要交换。
保证0-2上有序,若在2位置的数比在1位置的数要小,则需要交换,如果比在0位置上的数要小,需要第二次交换。
以此类推。时间复杂度是O(N²)
代码实现:
function insertSort(arr){
let n = arr.length;
for(let i=0;i<n;i++){
for(let j = i;j>0;j--){
if(arr[j-1]>arr[j]){
let tmp = arr[j]
arr[j] = arr[j-1]
arr[j-1] = tmp
}
}
}
return arr
}
归并排序
描述:用中间的数,将数组分成左右两部分,分别对左右两边排序,然后再merge。
merge的方式:两个指针分别指向左右两边第一个数,比较,将小的放入新开辟的数组,然后对应的那一边的指针向右移动。
此算法的时间复杂度为O(NlogN)
代码实现:
function guibing(arr){
if(arr == null || arr.length < 2){
return
}
process(arr,0,arr.length-1)
return arr
}
function process(arr,l,r){
if(l === r){
return
}
let mid = l + ((r-l)>>1)
process(arr,l,mid)
process(arr,mid+1,r)
merge(arr,l,mid,r)
}
function merge(arr,l,m,r){
let help = new Array(r-l+1)
// i指的是help的下标
let i = 0
let p1 = l
let p2 = m+1
while(p1<=m && p2<=r){
help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++]
}
while(p1 <= m){
help[i++] = arr[p1++]
}
while(p2 <= r){
help[i++] = arr[p2++]
}
for(i = 0;i<help.length;i++){
arr[l+i] = help[i]
}
}
快速排序
描述:在数组中随机选取一个数num作为标准,然后将数组分成三个区域,左边的区域是比num小的,中间的区域是与num相等的,右边的区域是比num大的。所以分为三种情况讨论:
①比num小:将该数与左边区域右边界+1位置的数交换,然后右边界向右拓展1,比较的指针向右移动
②与num相等:不需要移动,比较的指针向右移动
③比num大:将右边区域左边界-1的数与之交换,右边区域左边界向左移动,不需要移动指针(因为指针指向的是刚刚被交换过来的数,还未进行比较)
时间复杂度为O(N²)
代码实现:
function result(arr){
if(arr == null || arr.length < 2){
return arr;
}
quickSort(arr,0,arr.length-1)
return arr
}
function quickSort(arr,l,r){
if(l < r){
// 在数组中随机挑选一个数作为基准,这个基准与最后一个数进行交换
let a = l+Math.floor(Math.random()*(r-l+1))
swap(arr,a,r)
let p = partition(arr,l,r)
quickSort(arr,l,p[0]-1)
quickSort(arr,p[1]+1,r)
}
}
// 返回等于区域的左边界和右边界
function partition(arr,l,r){
// <区的右边界
let less = l-1
// >区的左边界
let more = r
while(l<more){
if(arr[l] < arr[r]){
swap(arr,++less,l++)
}else if(arr[l] > arr[r]){
swap(arr,--more,l)
}else{
l++;
}
}
swap(arr,more,r)
return new Array(less+1,more)
}
function swap(arr,a,b){
let tmp = arr[a]
arr[a] = arr[b]
arr[b] = tmp
}
堆排序
描述:构造大根堆,保证根的节点是最大的,然后将它与数组最后边的数交换,减小堆的大小。此时根的位置不符合大根堆的定义,需要将根移动到符合定义的区域,移动完成后,堆中最大的数是新的根,以此类推。时间复杂度为O(NlogN)
大根堆:在一个完全二叉树中,保证每一个子树中根节点的值都是最大的。
代码实现:
function heapSort(arr){
if(arr == null || arr.length < 2){
return arr;
}
for(let i = 0; i<arr.length; i++){
heapInsert(arr,i)
}
let heapSize = arr.length;
swap(arr,0,--heapSize)
while(heapSize > 0){
heapify(arr,0,heapSize)
swap(arr,0,--heapSize)
}
return arr;
}
// 用户输入数字,然后插入完全二叉树中间,构成大根堆
// index是需要插入的数字的下标
function heapInsert(arr,index){
// 与父节点进行比较
while(arr[index] > arr[(index-1)/2]){
swap(arr,index,(index-1)/2);
index = (index-1)/2;
}
}
// 某个数在index的位置,能否往下移动
// 此数组不是大根堆,因为有一个节点交换了,需要把这个节点放进合适的位置,使得二叉树为大根堆
function heapify(arr,index,heapSize){
// 获取左孩子的下标
let left = index*2 + 1;
while(left < heapSize){
// 找左右孩子中数字最大的节点
let largest = left + 1 < heapSize && arr[left+1] > arr[left] ? left+1 : left;
largest = arr[largest] > arr[index] ? largest : index;
// 如果父节点比左右节点更大,返回
if(largest === index){
break;
}
// 如果父节点比节点小,交换,形成大根堆
swap(arr,largest,index);
// 指向需要动的节点,index的值变化
index = largest;
// 更新左孩子节点的下标
left = index * 2 + 1
}
}
function swap(arr,a,b){
let tmp = arr[a]
arr[a] = arr[b]
arr[b] = tmp
}
桶排序
需要特殊的数据。
例子:进制排序(十进制)
准备10个桶,将数组中的数按照个位上的数分别放进桶里面,然后依次倒出。(同一个桶内先进先出)
倒出后按顺序按照十位上的数字,进桶,再倒出。
直到比较到数组中位数最多的数的最高位
这里有一个数组:[25,45,89,100]
第一次进桶:
第一次出桶的数组:[100,25,45,89]
第二次入桶:
第二次出桶的数组:[100,25,45,89]
第三次入桶:
第三次出桶的数组:[25,45,89,100]
得到排序后的数组