排序

44 阅读3分钟

冒泡排序

元素项向上移动至正确的顺序,就好像气泡升至表面一样,冒泡排序因而得名

      var bubbleSort = function(arr){
            var len = arr.length-1;
            for(var j=0; j < len; j++){
                //声明一个变量 作为标志位
                var done = true
                //遍历数据 次数就是arr.length-1
                //已经获得第j大的值,后续不需要冒泡
                for(var i=0; i < len - j; i++){
                    //如果前一个数 大于 后一个数 交换位置
                    if(arr[i] > arr[i + 1]){
                        var temp = arr[i]
                        arr[i] = arr[i + 1]
                        arr[i+1] = temp
                        done = false;
                    }
                }
                if(done){
                    break;
                }
            }
            return arr
        }

        //调用 
        let arr = [3,4,1,2]
        console.log(bubbleSort(arr))

选择排序

首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此递归。

核心:首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕

       function selectionSort(arr){
            const len = arr.length;
            let minIndex;
            for(let i = 0; i < len -1 ; i++){
                minIndex = i
                for(let j = i + 1; j < len; j++){
                    if(arr[j] < arr[minIndex]){
                        //将此元素设置成为新的最小值
                        minIndex = j
                    }
                }
                //判断相等
                if(minIndex === i) continue;
                //将最小值和第一个没有排序过的位置交换
                let temp = arr[minIndex];
                arr[minIndex] = arr[i];
                arr[i] = temp
            }

            return arr
        }

        //调用
        let arr = [3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];
        console.log(selectionSort(arr))

插入排序

插入排序的比较顺序不同于冒泡排序和选择排序,插入排序的比较顺序是当前项向前比较。

注意:从第二项开始,依次向前比较,保证当前项以前的序列是顺序排列

       function insertionSort(arr){
            const len = arr.length;
            let current, pointer;
            for(let i = 1; i < len; i++){
                current =  arr[i];
                pointer = i;
                while(pointer >= 0 && current < arr[pointer - 1]){
                    //向前比较
                    arr[pointer] = arr[pointer - 1];
                    pointer -= 1
                }
                //插入元素
                arr[pointer] = current
            }
            return arr
        }

        //调用
        let arr = [3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];
        console.log(insertionSort(arr))

归并排序

归并排序和快速排序相较于上面三种排序算法在实际中更具有可行性(在第四小节我们会通过实践复杂度来比较这几种排序算法)

JavaScriptArray 类定义了一个 sort 函数(Array.prototype.sort)用以排序 JavaScript 数组。ECMAScript 没有定义用哪个排序算法,所以浏览器厂商可以自行去实现算法。例如,Mozilla Firefox使用归并排序作为 Array.prototype.sort 的实现,而 Chrome 使用了一个快速排序的变体

归并排序是一种分治算法。其思想是将原始数组切分成较小的数组,直到每个小数组只有一 个位置,接着将小数组归并成较大的数组,直到最后只有一个排序完毕的大数组。因此需要用到递归

核心:归并排序,拆分成左右两块数组,分别排序后合并

function mergeSort(arr){
            const len =  arr.length;
            // 终止条件
            if(len < 2) return arr 
            const middle = Math.floor(len / 2);
            const left = arr.slice(0,middle) ;
            const right = arr.slice(middle);

            return merge(mergeSort(left), mergeSort(right))
        }

        function merge(left,right){
            const ret = [];
            while(left.length && right.length){
                //如果左侧首值 <= 右侧首值
                if(left[0] <= right[0]){
                    // 拷贝左侧首项的值
                    ret.push(left.shift())
                }else{
                    // 拷贝右侧部分首值
                    ret.push(right.shift())
                }
            }

            while (left.length){
                ret.push(left.shift())
            }

            while (right.length){
                ret.push(right.shift())
            }
            
            return ret
        }

        //调用
        let arr = [3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];
        console.log(mergeSort(arr))

快速排序

快速排序也许是最常用的排序算法了。它的复杂度为 O(nlogn),且它的性能通常比其他的复 杂度为 O(nlogn) 的排序算法要好。和归并排序一样,快速排序也使用分治的方法,将原始数组分为较小的数组。

先选择中间值,然后把比它小的放在左边,大的放在右边(具体的实现是从两边找,找到一对后交换)。然后对两边分别使用这个过程(递归)。

核心:分治算法,以参考值为界限,将比它小的和大的值拆开

       function quickSort(arr, left = 0, right = arr.length - 1){
            //left right 数组首尾
            if(left < right){
                let partitionIndex = partition(arr, left, right);
                quickSort(arr, left, partitionIndex - 1);       //左分区
                quickSort(arr, partitionIndex + 1, right);      //右分区
            }
            return arr;
        }

        function partition(arr, left, right){
            //将第一个元素设为轴心点
            let pivot = left;
            //存储指数 = 轴心点指数 +1
            let index = left + 1

            for(let i = index; i <= right; i ++){
                //元素[i] < 元素[轴心点]
                if(arr[i] < arr[pivot]){
                    //交换(i, 存储指数); 存储指数++
                    let temp = arr[i]
                    arr[i] = arr[index]
                    arr[index] = temp
                    index ++
                }
            }

            //  交换(轴心点, 存储指数 - 1)
            let temp1 = arr[index -1]
            arr[index -1] = arr[pivot]
            arr[pivot] = temp1
            return index - 1
        }

        //调用
        let arr = [3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];
        console.log(quickSort(arr))