JavaScript实现数据结构各种排序算法

本文主要总结了JavaScript中实现的各种排序算法,包括其原理和应用,适合面试复习和日常学习,帮助提升数据结构理解。
摘要由CSDN通过智能技术生成

今天面试,我的数据结构真的烂,做一波小总结吧,哎,真难受啊~

面试中数据结构是真的很重要,不论是以后工作还是考研,哎,悔不当初

下面是对排序算法的一些总结,希望下次面试心里有个谱吧

//测试数据
let arr = [1, 2, 5, 1, 3, 6, 7, 1, 3, 4, 12, 41, 23, 2, 33, 23, 45, 0]
console.log(`原始数组:${arr}`)

/*
 插入排序
 1.直接插入排序
 2.折半插入排序
 3.希尔排序
*/


// 根据不同的查找方法来找到插入的位置把插入排序分为不同的类别




/**算法:直接插入排序
 * 时间复杂度:最优O(n) 最差O(n^2)
 * 空间复杂度:O(1)
 * 稳定排序
 */
function insertSortDirect(arr) {
    if (arr.length <= 1) {
        return arr;
    }
    /**
     * 整个过程,数组的前面部分一直是有序的状态
     */

    for (let i = 1; i < arr.length; i++) {
        // 对于每一个数,先将它存到一个变量中
        let tmp = arr[i],
            j = i - 1;
        // 然后向左遍历
        while (j >= 0 && arr[j] > tmp) {
            //如果当前遍历到的数值比tmp值大,就将这个值移到后面的那个空
            arr[j + 1] = arr[j];
            j--;
        }
        //最后将停在j,j的位置就是arr[i]应该插入的位置
        arr[j + 1] = tmp;
    }

    return arr;
}


/**
 * 算法:折半插入排序
 * 时间复杂度:O(n^2)
 * 空间复杂度:O(1)
 * 稳定排序
 */
function insertSortHalf(arr) {
    if (arr.length <= 1) {
        return arr;
    }
    /**同直接插入排序一样,它是直接插入排序算法的一种改良
     * 直接插入排序算法前面的有序部分寻找j的位置的时候采用遍历的方式
     * 如果将前面查找j的位置的过程采用折半查找的方式,在数据量大的时候可以很大程度地提高效率
     */

    for (let i = 1; i < arr.length; i++) {
        // 折半查找下标
        let start = 0;
        let end = i - 1;
        let tmp = arr[i];

        //折半查找,只要end的位置不在start前面,就一直查找
        while (end >= start) {
            //获取中间下标
            let mid = Math.floor((start + end) / 2);
            //如果前面有序部分的中间位置的值大于现在要插入的值,说明要插入的位置在左半部分,反之,一样
            if (arr[mid] > tmp) {
                //将尾部改为mid,为下次循环作准备
                end = mid;
            } else {
                start = mid + 1;
            }
        }
        //上面那个while循环完成以后,最终end + 1的位置就是要插入的位置,让end后面的部分都腾一个位置出来
        for (let j = i - 1; j > end; j--) {
            arr[j + 1] = arr[j];
        }

        arr[end + 1] = tmp;

    }
    return arr;

}

/**
 * 算法:希尔排序
 * 时间复杂度:最好O(n) 最差O(nlog2n)
 * 空间复杂度:O(1)
 * 不稳定排序
 */

function insertSortShell(arr) {
    if (arr.length <= 1) {
        return arr;
    }
    // 也是对直接插入排序的一种改进,又称为 缩小增量排序  => 三层for循环+if

    //确定增量d
    for (let d = Math.floor(arr.length / 2); d > 0; d = Math.floor(d / 2)) {
        //对于每一遍循环,找到i和i-d元素进行比较,按照升序排列
        for (let i = d; i < arr.length; i++) {
            // j -= d是为了防止下面的数组下标越界
            for (let j = i - d; j >= 0; j -= d) {
                if (arr[j] > arr[j + d]) {
                    let tmp = arr[j];
                    arr[j] = arr[j + d];
                    arr[j + d] = tmp;
                }
            }
        }
    }

    return arr;

}


console.log("\n插入排序:")
console.log(`1.直接插入排序后:${insertSortDirect(arr)}`)
console.log(`2.折半插入排序后:${insertSortHalf(arr)}`)
console.log(`3.希尔排序后:${insertSortShell(arr)}`)


/**
 * 交换排序
 * 1.冒泡排序
 * 2.快速排序
 */


/**
 * 算法:冒泡排序
 * 时间复杂度:O(n^2)
 * 空间复杂度:O(1)
 * 稳定排序
 */
function switchSortBubble(arr) {
    if (arr.length <= 1) {
        return 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 tmp = arr[i];
                arr[i] = arr[j];
                arr[j] = tmp;
            }
        }
    }

    return arr;
}


/**
 * 算法:快速排序
 * 时间复杂度:最好O(nlogn) 最差O(n^2)
 * 空间复杂度:O(logn)
 * 不稳定排序
 * 
 */
function switchSortQuick(arr, start, end) {
    if (arr.length <= 1) {
        return arr;
    }
    /*
     *采用递归的思想 是对冒泡排序的一种改进
     *右-->左找比基准值小的  左-->右找比基准值大的 
     */

    if (start < end) {

        //基准值
        let base = arr[start];
        let i = start;
        let j = end;

        while (i < j) {
            //从右边开始找,一直到小于base才停止
            while (i < j && arr[j] > base) {
                j--;
            }
            //小于base的时候进行交换
            if (i < j) {
                arr[i] = arr[j];
                i++;
            }

            //同理,左边一样的
            while (i < j && arr[i] < base) {
                i++;
            }
            if (i < j) {
                arr[j] = arr[i];
                j--;
            }
        }
        // 第一轮循环以后可以知道  基准值 最终的位置该在哪里(i)
        arr[i] = base

        //然后递归调用左边和右边,将数组的左右两边递归
        switchSortQuick(arr, start, i - 1);
        switchSortQuick(arr, i + 1, end);
    }
    return arr;
}

console.log("\n交换排序:")
console.log(`1.冒泡排序后:${switchSortBubble(arr)}`)
console.log(`2.快速排序后:${switchSortQuick(arr)}`)


/**
 * 选择排序
 * 1.简单选择排序
 * 2.堆选择排序
 */


/**
 * 算法:简单选择排序
 * 时间复杂度:O(n^2)
 * 空间复杂度:O(1)
 * 不稳定排序
 */
function chooseSortSimple(arr) {
    if (arr.length <= 1) {
        return arr;
    }

    /**
     * 找出数组中最小的数与第一个数交换位置
     * 然后下轮在找到后面的最小的数,与第二个数交换位置
     * 
     * 简单选择排序是 冒泡排序 的一种改版
     */

    for (let i = 0; i < arr.length - 1; i++) {
        // 最小元素的下标
        let min = i;
        for (let j = i + 1; j < arr.length; j++) {
            if (arr[j] < arr[min]) {
                min = j;
            }
        }

        let tmp = arr[i];
        arr[i] = arr[min];
        arr[min] = tmp;
    }

    return arr;
}


/**
 * 算法:堆排序
 * 时间复杂度:O(nlogn)
 * 空间复杂度:O(1)
 * 不稳定排序
 */
function chooseSortHeap(arr){

    if(arr.length <= 1){
        return arr;
    }

    let len;

    //构建一个顶堆
    function bulidMaxHeap(array){
        len  = array.length;
        for(let i = Math.floor(len / 2);i >= 0;i--){
            //调整堆
            heapify(array,i);
        }
    }

    function heapify(array,i){
        let left = 2 * i + 1;
        let right = 2 * i + 2;
        let largest = i;

        if(left < len && array[left] > array[largest]){
            largest = left;
        }
        if(right < len && arr[right] > array[largest]){
            largest = right;
        }

        if(largest !== i){
            swap(array,i,largest);
            heapify(array,largest);
        }
    }

    function swap(array, i, j) {
        let tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

    bulidMaxHeap(arr);
    for(let i = arr.length - 1 ; i >= 0 ; i--){
        swap(arr,0,i);
        len --;
        heapify(arr,0);
    }

    return arr;
    

}

console.log("\n选择排序:")
console.log(`1.简单选择排序后:${chooseSortSimple(arr)}`)
console.log(`2.堆排序后:${chooseSortHeap(arr)}`)


//归并排序

/**
 * 算法:归并排序(JavaScript不推荐这种排序,递归层太多了,因为它的空间复杂度比较高)
 * 时间复杂度:O(nlogn)
 * 空间复杂度:O(n)
 * 稳定排序
 */

function mergeSort(arr){
    if(arr.length <= 1){
        return arr;
    }

    /**归并排序采用了 分治 的思想,将数组一直等分,等分后的区间作排序,最后在合并  也就是先 “分” 后 “治“*/


    let mid = Math.floor(arr.length / 2);
    let left = arr.slice(0,mid);
    let right = arr.slice(mid);

    //递归操作
    return merge(mergeSort(left),mergeSort(right));

    //定义一个merge方法,用来合并 合并left数组和right数组的部分  核心部分
    function merge(left,right){
        //用来存放合并的结果
        let result = [];


        //由于左右的部分不一样长,所以先比较两者共有的部分
        while(left.length && right.length){

            //如果左边的第一个元素的值比右边的元素的第一个值小,就把左边的第一个值push到待返回数组中,
            //反之一样,重复这个操作,直到有一边已经没有值了
            if(left[0] <= right[0]){
                result.push(left.shift());
            }else{
                result.push(right.shift());
            }

            //处理有一边还剩余的情况
            while(left.length){
                result.push(left.shift());
            }

            while(right.length){
                result.push(right.shift());
            }

            return result;

        }

    }
}

console.log(`\n归并排序后:${mergeSort(arr)}`)

//基数排序

/**
 * 算法:基数排序
 * 时间复杂度:O(n x k)
 * 空间复杂度:O(n + k)
 * 稳定排序
 */

function radixSort(arr){
    if(arr.length <= 1){
        return arr;
    }

    /**思想:数字是0 ~ 9的,为每个数字建立一个桶 根据键值的每位数字来分配桶; */

    //先找出数组中最大的数,确定最大的数是几位的
    function getMaxDigit(){
        let max = arr[0];
        for(let i = 1 ; i < arr.length; i++){
            if(arr[i] > max){
                max = arr[i];
            }
        }

        return max.toString().length;
    }


    let counter = [];
    let mod = 10;
    let dev = 1;
    let maxDigit = getMaxDigit();
    for (let i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
        for(let j = 0; j < arr.length; j++) {
            let bucket = parseInt((arr[j] % mod) / dev);
            if(counter[bucket]==null) {
                counter[bucket] = [];
            }
            counter[bucket].push(arr[j]);
        }
        let pos = 0;
        for(let j = 0; j < counter.length; j++) {
            let value = null;
            if(counter[j]!=null) {
                while ((value = counter[j].shift()) != null) {
                      arr[pos++] = value;
                }
          }
        }
    }
    return arr;
}


console.log(`\n基数排序后:${radixSort(arr)}`)

运行结果
运行结果

总结
排序算法总结

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值