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]
第一次进桶:
image
第一次出桶的数组:[100,25,45,89]

第二次入桶:
image
第二次出桶的数组:[100,25,45,89]

第三次入桶:
image
第三次出桶的数组:[25,45,89,100]

得到排序后的数组

posted @ 2022-03-13 23:38  kihyun  阅读(144)  评论(0编辑  收藏  举报