js基础排序

123 阅读1分钟

为什么排序

无缘无故就想排序先,那是强迫症。 很多问题在排序之后,难度就降低了,因为我们能从一个元素上知道更多的信息,有序了才好用二分,有序了,就能更快速地排除不符合要求的部分。

快速排序

最近又复习了一下,突然觉得快排的思路是真的很好理解。 快排是分而治之的思路,拿到一个数组就把它三部分,小于基准数的 等于基准数的 大于基准数的。
至于基准数,是任意的 。一般取第一个就行。这样这三大块就相对有序了。
接下来,就是再把 小于基准数的那部分再次三分, 大于的那部分亦然。到最后,数组的长度为一时就不可再分了,一个元素自然是有序的, 排序也完成了。至于优化, 数组长度为3或者2的时候就可以使用其他简单的排序,减少递归层数。还有基准值 和 空间的优化就不多说了。

image.png

        if (arr.length < 2) return arr
        let mid = arr[0] 
        const arrL = [] , arrR = [] ,arrM = []
        for(const el of arr){
            if (el > mid) { arrR.push(el)}
            if (el < mid) { arrL.push(el)}
            if (el === mid) { arrM.push(el)}
        }
        return [...quickSort(arrL), ...arrM , ...quickSort(arrR)]
    }  
        }
        
// 快排的一般写法 上面那种太费内存了 力扣不给过        
        function quickSort2(arr, l, r){
    if (r -l < 1) return 
    let x = l, y = r , base = arr[l]
    // 两端往中间围剿啊
    while(x < y) {
        // 右边的值不小于基准值 不用动
        while(x< y && arr[y] >=base){
            y--
        }
        // 此时 arr[y]是右边第一个小于基准值的 应该去左边
        if (x< y){
            arr[x++] = arr[y]
            // arr[x] = arr[y]
            // x++
        }
        // 左边的值不大于基准值 不用动
        while(x< y && arr[x] <= base){
            x++
        }
            // 此时 arr[x]是左边第一个大于基准值的 应该去右边
        if (x < y){
            arr[y--] = arr[x]
        }
      

    }
    // 此时x === y 该把base放回去了
    arr[x] = base 
    // 递归
    quickSort2(arr, l,x-1)
    quickSort2(arr, x+1, r)

    return
}

上面说了思路 这里就说一下复杂度的问题,按上面的简单写法 空间复杂度差不多O(n)了, 时间复杂度就看运气了。
如果每次基准值都接近中位数 那么大概需要递归log 2 (n)次, 每次递归要遍历 就是n log(n) ,运气差的情况就是基准值每次都选到最值,这样几乎就退化到O(n2)了
顺便说一下个人对于时间复杂度的看法 ,那就是极限 只不过这里是无穷大的极限。 不过无穷大的倒数不就是无穷小了吗。

快排就适合本身相对有序的集合,快速排序不稳定。

归并排序

归并排序的基本思路就是先微分再积分,不,是先分再合。先拆解为小问题,然后解决小问题,然后解决把小问题的成果合并成一个大问题的问题,也就是解决两个有序数组的合并。

给最小单元排序

一个待排序数列,有n个元素。 如果n= 2是不是非常容易处理,如果n = 1,那不就,不用排序了。 好了,你现在已经知道怎么给1个或者两个元素排序了。可是这又有什么用呢?单独来看没用。要结合下面的合并有序数组来用。

合并有序数组

还是刚才的思路, 合并n个数组,让人无从下手。但是合并两个数组,就相对简单了。

  • 合并两个升序数组 假设,要合并的两个数组是升序的。只需新建一个数组,然后两个数组各来一个指针,在一轮迭代中,把元较小的那个元素从原来的数组里拿出来,放到新数组里, 对应的指针向后走一位。如果有一个指针先走完了,那么就直接把还有剩余的那个数组的元素依次放到新数组。最后,这个新数组就是合并了之后的新的有序数组。

选择排序

选择排序的思路有点像冒泡排序。 只不过冒泡排序在不停的比较和交换位置,而选择排序会在比较过程记住最值的下标,最后只需要把最值放在排序后它应该在的位置。 另一种比较费空间的思路就是依次找到最值 ,然后从原数组中删除,放入新数组。这种思路如果把找最值的过程优化,就能优化这个选择排序,那就是建堆。

// 记录最小值的下标

 function selectSort(arr){
        let len = arr.length ,count = 0;
        for( let i = 0; i< len ; i++){
            count = i ;
            for (let j =i; j < len ;j++) {
                if( arr[count] >arr[j] ) {
                    count = j ;
                }
            }
            let t = arr[i] 
            arr[i] = arr[count] 
            arr[count] = t ;
        }
    }
    
    
// 找到最小值放入新数组
   function selectSort(arr){
        let len = arr.length ,count = 0;
        let res = []

        while(arr.length){
            const ind = getMin(arr)
            res.push(...arr.splice(ind, 1))
        }
        return res
    }
    function  getMin(arr){
        let ind = 0;
        for(let i = 1 ; i < arr.length;i++){
            if (arr[i] < arr[ind]){
                ind = i
            }
        }
        return ind
    }

思路理解了,写出来的代码也需要验证,这道题就很合适力扣912