经典排序算法

86 阅读2分钟

1. 冒泡排序

1.1 原理

  • 比较相邻的元素,如果第一个比第二个大,就互相交换位置
  • 每一对相邻的元素做重复的工作,从开始第一对到末尾最后一对,最后的元素是最大的数
  • 针对所有的元素重复上面2步骤,除了最后一个(因为已经知道其就是最大值)
  • 重复上面步骤,直到排序完成

1.2 动画

849589-20171015223238449-2146169197.gif

1.3 代码实现

    function bubbleSort(arr) {
       let len = arr.length
       for(let i = 0; i < len - 1; i++ ) {
           for(let j = 0; j < len - 1 - i; j++ ) {
               if(arr[j] > arr[j+1]) {
                   let temp = arr[j+1]
                   arr[j+1] = arr[j]
                   arr[j] = temp
               }
           }
       }
       return arr
    }

2. 选择排序

2.1 原理

  • 首先在未排序的序列中找到最小的元素,存放到已排序序列的起始位置
  • 再从剩余未排序元素中继续寻找最小的元素,放到已排序序列的末尾
  • 重复以上步骤,直到所有元素均排序完成

2.2 动画

选择排序.gif

2.3 代码实现

    function selectionSort(arr){
        let len = arr.length
        let minIndex, temp
        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
                }
            }
            temp = arr[i]
            arr[i] = arr[minIndex]
            arr[minIndex] = temp
        }
        return arr
    }

3. 插入排序

3.1 原理

  • 从第一个元素开始,该元素可以认为已经排序
  • 拿到下一个元素,在已排序的元素序列中从后向前进行扫描
  • 如果该元素(已排序)大于新元素,则将该元素移到下一个位置
  • 重复步骤3,直到找到已排序的元素不大于新元素的位置
  • 将新元素插入到该位置
  • 重复步骤2~5,直到排序完成

3.2 动画

插入排序.gif

3.3 代码实现

    function insertionSort(arr){
        let len = arr.length
        let prevIndex, current
        for(let i = 1; i < len; i++){
            prevIndex = i - 1
            current = arr[i]
            while(prevIndex >=0 && arr[prevIndex] > current){
                arr[prevIndex + 1] = arr[prevIndex]
                prevIndex--
            }
            arr[prevIndex + 1] = current
        }
        return arr
    }

4. 希尔排序(缩小增量排序)

4.1 原理

  • 先将整个待排序的记录序列分割成若干个子序列分别进行插入排序
  • 选择一个增量序列t1,t2,…,tk,按照增量序列个数k,对序列进行k趟排序
  • 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

4.2 动画

希尔排序.gif

4.3 代码实现

    function shellSort(arr) {
        let len = arr.length;
        let temp;
        let gap = 1;
        // 动态的定义间隔序列
        while (gap < len / 3) {
          gap = gap * 3 + 1;
        }
        for (gap; gap > 0; gap = Math.floor(gap / 3)) {
          for (let i = gap; i < len; i++) {
            temp = arr[i];
            let j = i - gap;
            while (j >= 0 && arr[j] > temp) {
              arr[j + gap] = arr[j];
              j -= gap;
            }
            arr[j + gap] = temp;
          }
       }
       return arr
    }

5. 快速排序

5.1 原理

  • 使用分治法把一个串分为两个子串,从数组中挑出一个元素,作为基准
  • 重新选择数组,所有比基准值小的元素放在基准前面,所有比基准值大的放在基准后面(相同的数可以放在任意一边),当这个分区结束之后,该基准就处于数组的中间位置,这个称为分区操作
  • 递归的把小于基准值得元素的子数组和大于基准值的元素的子数组排序

5.2 动画

快速排序.gif

5.3 代码实现

    // 交换元素
    function swap(arr, i, j) {
        let temp = arr[i]
        arr[i] = arr[j]
        arr[j] = temp
    }
    // 分区操作
    function partition(arr, left, right){
        // 设置基准值
        let pivot = left
        let index = pivot + 1
        for(let i = index; i <= right; i++){
            if(arr[i] < arr[pivot]){
                swap(arr, i, index)
                index++
            }
        }
        swap(arr, pivot, index - 1)
        return index - 1
    }
    
    function quickSort(arr, newLeft, newRight){
        let len = arr.length
        let partitionIndex
        let left = typeof newLeft != "number" ? 0 : newLeft
        let right = typeof newRight != "number" ? len - 1 : newRight
        if(left < right) {
            partitionIndex = partition(arr, left, right)
            quickSort(arr, left, partitionIndex - 1)
            quickSort(arr, partitionIndex + 1, right)
        }
        return arr
    }