十大排序算法(JavaScript语言)

一:冒泡排序

动图理解

在这里插入图片描述

代码实现
function bubbleSort(arr){
    for(let i=0;i<arr.length-1;i++){
        let didSwap = false;
        for(let j=0;j<arr.length-i-1;j++){
        	// 交换的是相邻索引处元素,所以稳定
            if(arr[j]>arr[j+1]){
                let temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
                didSwap = true;
            }
        }
        if(!didSwap){
            break;
        }
    }
}
算法分析
  • 平均时间复杂度【时间】:O(n2)
  • 最好情况【时间】:O(n),发生在数组本身有序的情况下。
  • 最坏情况【时间】:O(n2) ,发生在数组本身反序的情况下。
  • 空间复杂度【空间】:O(1) ,仅在原数组空间下进行元素交换。
  • 排序方式【空间】:内排序
  • 稳定性【空间】:稳定

二:选择排序

动图理解

在这里插入图片描述

代码实现
function selectSort(arr){
    for(let i=0;i<arr.length-1;i++){
        for(let j=i+1;j<arr.length;j++){
        	// 交换的不是相邻索引处的元素,所以不稳定
            if(arr[i]>arr[j]){
                let temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
    }
}
算法分析
  • 平均时间复杂度【时间】:O(n2)
  • 最好情况【时间】:O(n2)
  • 最坏情况【时间】:O(n2)
  • 空间复杂度【空间】:O(1),仅在原数组空间下进行元素交换。
  • 排序方式【空间】:内排序
  • 稳定性【空间】:不稳定(如 [5a,8,5b,2,9],第一轮选择交换后为[2,8,5b,5a,9],两个5前后顺序变化了)。

三:插入排序

动图理解

在这里插入图片描述

代码实现
function insertSort(arr){
    for(let i=1;i<arr.length;i++){
        let insert_index = i;
        let insert_ele = arr[i];
        // 找到插入位置
        for(let j=i-1;j>=0;j--){
            if(insert_ele<arr[j]){
                insert_index = j;
            }else{
            	break;
			}
        }
        // 腾出插入位置
        for(let z=i-1;z>=insert_index;z--){
        	// 交换的是相邻索引处元素,所以稳定
            arr[z+1] = arr[z];
        }
        // 插入元素
        arr[insert_index] = insert_ele;
    }
}
算法分析
  • 平均时间复杂度【时间】:O(n2)
  • 最好情况【时间】:O(n),发生在数组本身有序情况下,每个元素(除第一个元素外)都比一次挪一次插一次。
  • 最坏情况【时间】:O(n2),发生在数组本身无序情况下。
  • 空间复杂度【空间】:O(1),仅在原数组空间下进行元素交换。
  • 排序方式【空间】:内排序
  • 稳定性【空间】:稳定

四:希尔排序

动图理解

在这里插入图片描述

代码实现
function shellSort(arr){
    let gap = Math.floor(arr.length/2);
    while(gap>=1){
        for(let i=gap;i<arr.length;i++){
            shellSortHelp(arr,i,gap);
        }
        gap = Math.floor(gap/2);
    }
}
// 在当前增量为gap的分组情况下,把索引为i的元素插入至正确位置。
function shellSortHelp(arr,i,gap){
    let insert_index = i;
    let insert_ele = arr[i];
    // 找到插入位置(间隔为gap)
    for(let j=i-gap;j>=0;j=j-gap){
    	// 交换的不是相邻索引处的元素,所以不稳定
        if(insert_ele<arr[j]){
            insert_index = j;
        }else{
			break
		}
    }
    // 腾出插入位置
    for(let z=i-gap;z>=insert_index;z=z-gap){
        arr[z+gap] = arr[z];
    }
    arr[insert_index] = insert_ele;
}
算法分析
  • 平均时间复杂度【时间】:O(nlog2 n)
  • 最好情况【时间】:O(nlog2 n)
  • 最坏情况【时间】:O(nlog2 n)
  • 空间复杂度【空间】:O(1),仅在原数组空间下进行元素交换。
  • 排序方式【空间】:内排序
  • 稳定性【空间】:不稳定(如 [5a,5b,2,9],第一次分组交换后为[2,5b,5a,9],第二次分组顺序不变,两个5前后顺序变化了)

五:归并排序

动图理解

在这里插入图片描述

代码实现
function mergeSort(arr,left,right) {
    if(left<right){
        // 1.分治分组时,索引中值二分
        let center = Math.floor((left+right)/2)
        // 左分
        mergeSort(arr,left,center)
        // 右分
        mergeSort(arr,center+1,right)
        // 左右合一
        mergeSortHelp(arr,left,right,center)
    }
}
function mergeSortHelp(arr,left,right,center) {
    let pointer_left = left
    let pointer_right = center+1
    let arr_copy = arr.slice()// 借用到拷贝数组,空间复杂度O(n)
    for(let i=left;i<=right;i++){
        if(pointer_left===center+1){
            for(let j=i;j<=right;j++){
                arr[j] = arr_copy[pointer_right++]
            }
            break
        }
        else if(pointer_right===right+1){
            for(let j=i;j<=right;j++){
                arr[j] = arr_copy[pointer_left++]
            }
            break
        }
        // 2.合并比较时,先判断取左边数组元素,结合1判断 归并排序稳定
        else if(arr_copy[pointer_left]<=arr_copy[pointer_right]){
            arr[i] = arr_copy[pointer_left++]
        }
        else{
            arr[i] = arr_copy[pointer_right++]
        }
    }
}
算法分析
  • 平均时间复杂度【时间】:O(nlogn)
  • 最好情况【时间】:O(nlogn)
  • 最坏情况【时间】:O(nlogn)
  • 空间复杂度【空间】:O(n),会使用到拷贝数组。
  • 排序方式【空间】:外排序
  • 稳定性【空间】:稳定。

六:快速排序

动图理解

在这里插入图片描述

代码实现
// 分治法
function quickSort(arr,left,right){
    if(left<right){
        let loginMid = arrAdjust(arr,left,right);
        quickSort(arr,left,loginMid-1);
        quickSort(arr,loginMid+1,right);
    }
}
// 挖坑填数(比较时使用双指针索引值比对交换,所以不稳定)
function arrAdjust(arr,left,right){
    let pointerLeft = left;
    let pointerRight = right;
    let referNum = arr[pointerLeft];
    while (pointerLeft<pointerRight){
        //arr[pointerLeft]为坑位
        while(pointerLeft<pointerRight && arr[pointerRight]>referNum){
            pointerRight--;
        }
        if(pointerLeft<pointerRight){
            arr[pointerLeft] = arr[pointerRight];
        }
        //arr[pointerRight]为坑位
        while(pointerLeft<pointerRight && arr[pointerLeft]<referNum){
            pointerLeft++;
        }
        if(pointerLeft<pointerRight){
            arr[pointerRight] = arr[pointerLeft];
        }
    }
    //pointerLeft==pointerRight,使用基准数占住坑位
    arr[pointerLeft] = referNum;
    // 返回逻辑中间值索引,此时arr[x]<arr[pointerLeft]<arr[y]。(x<pointerLeft<y)
    return pointerLeft;
}
算法分析
  • 平均时间复杂度【时间】:O(nlogn)
  • 最好情况【时间】:O(nlogn)
  • 最坏情况【时间】:O(n2)
  • 空间复杂度【空间】:O(nlogn)
  • 排序方式【空间】:内排序
  • 稳定性【空间】:不稳定(索引逻辑中值二分)

七:堆排序

动图理解

在这里插入图片描述

代码实现
// 1.堆排序
let len = 0// heapSort和heapAdjust方法共享。
function heapSort(arr){
    buildMaxHelp(arr)
    for(let i=arr.length-1;i>0;i--){
        swap(arr,0,i)
        len--
        heapAdjust(arr,0)
    }
}
// 2.构建大顶堆
function buildMaxHelp(arr){
    len = arr.length
    // 堆数组:索引为[0,Math.floor(arr.length)-1]的为非叶子结点。
    for(let i=Math.floor(arr.length/2)-1;i>=0;i--){
        heapAdjust(arr,i)
    }
}
// 3.堆调整:调整为以arr[i]为堆顶的堆为大顶堆
function heapAdjust(arr,i){
    let left = 2*i+1
    let right = 2*i+2
    let largest = i// 最大元素的索引指针
    if(right<len && arr[right]>arr[largest]){ // 排序已提出的元素不参与构建堆,此处len不能为arr.length
        largest = right
    }
    if(left<len && arr[left]>arr[largest]){
        largest = left
    }
    if(largest!=i){
        swap(arr,i,largest)
        heapAdjust(arr,largest)// 堆变化后,递归调整子堆
    }
}
// 4.元素交换
function swap(arr,i,j){
    let temp = arr[i]
    arr[i] = arr[j]
    arr[j] = temp
}
  • 腾讯笔试题:无序不重复的数字,取出第K大的数字(取出第k小的数字用小顶堆)。
function heapsort2(arr,k){
    buildMaxHeap(arr)
    
    for(let i=arr.length-1;i>=arr.length-k;i--){// 只需遍历到arr.length-k即可
        swap(arr,0,i)
        len--
        heapify(arr,0)
    }
    return arr[arr.length-k]
}
算法分析
  • 平均时间复杂度【时间】:O(nlogn)
  • 最好情况【时间】:O(nlogn)
  • 最坏情况【时间】:O(nlogn)
  • 空间复杂度【空间】:O(1)
  • 排序方式【空间】:内排序
  • 稳定性【稳定性】:不稳定

八:计数排序

动图理解

在这里插入图片描述

代码实现
function countingSort(arr,max = Math.max(...arr)){
    let bucketArr = new Array(max+1)
    // 装篮
    for(let i=0;i<arr.length;i++){
        if(!bucketArr[arr[i]]){
            bucketArr[arr[i]] = 0
        }
        bucketArr[arr[i]]+=1
    }

    // 出篮
    let pointer = 0
    for(let j=0;j<bucketArr.length;j++){
        while (bucketArr[j]-->0){
            arr[pointer++]=j
        }
    }
}
算法分析
  • 平均时间复杂度【时间】:O(n+k)。(n=arr.length,k=bucket.length)
  • 最好情况【时间】:O(n+k)
  • 最坏情况【时间】:O(n+k)
  • 空间复杂度【空间】:O(k)
  • 排序方式【空间】:外排序
  • 稳定性【空间】:稳定(计数排序不基于元素的比较,此处稳定不指上述未优化的计数排序代码)。

九:桶排序

十:基数排序

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章