使用JavaScript完成排序算法:冒泡排序、选择排序 、快速排序 、插入排序、希尔排序、归并排序

1.冒泡排序

每一趟只能确定将一个数归位。即第一趟只能确定将末位上的数归位,第二趟只能将倒数第 2 位上的数归位,依次类推下去。如果有 n 个数进行排序,只需将 n-1 个数归位,也就是要进行 n-1 趟操作。

而 “每一趟 ” 都需要从第一位开始进行相邻的两个数的比较,将较大的数放后面,比较完毕之后向后挪一位继续比较下面两个相邻的两个数大小关系,重复此步骤,直到最后一个还没归位的数。

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

时间复杂度:O(n^2)

2.选择排序

工作原理是:第一次从待排序的中数据元素选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。首先在未排序的序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

      function selectionSort(arr) {
        for (var i = 0; i < arr.length - 1; i++) {
          var min = i;
          for (var j = i + 1; j < arr.length; j++) {
            if (arr[j] < arr[min]) min = j;
          }
          if (min != i) {
            var num = arr[i];
            arr[i] = arr[min];
            arr[min] = num;
          }
        }
        return arr;
      }

时间复杂度:O(n^2)

3.快速排序

(1)选出一个mid,一般是最左边或是最右边的。
(2)定义一个left[]和一个right[],遍历数组,在走的过程中,若遇到大于mid的数,则存入right中,否则存入right中
(4)此时mid的左边都是小于mid的数,mid的右边都是大于mid的数
(5)将mid的左序列和右序列再次进行这种单趟排序,如此反复操作下去,直到左右序列只有一个数据,或是左右序列不存在时,便停止操作,此时此部分已有序

      function quickSort(arr) {
        if (arr.length <= 1) {
          return arr;
        } else {
          var left = [],
            right = [],
            mid = arr[0];
          for (var i = 1; i < arr.length; i++) {
            arr[i] > mid ? right.push(arr[i]) : left.push(arr[i]);
          }
          return quickSort(left).concat([mid], quickSort(right));
        }
      }
      console.log(quickSort(arr));

时间复杂度:O(nLogn)

4.插入排序

(1)从第一个元素开始,该元素可以认为已经被排序。

(2)取出下一个元素作为新元素,在已经排序的元素序列中从后向前扫描,进行大小比较。

(3)如果已排序的元素大于新元素,则继续向前扫描,比较大小。

(4)重复上一个步骤,直到找到已排序的元素小于或者等于新元素的位置,将新元素插入到该位置,重复上面的步骤。

      function insertionSort(arr) {
        for (let i = 1; i < arr.length; i++) {
          let preIndex = i - 1;
          let current = arr[i];
          // 位置i之前,是已排好序的数字,while的作用是找到一个坑位,给当前数字current插入
          while (preIndex >= 0 && arr[preIndex] > current) {
            arr[preIndex + 1] = arr[preIndex]; // 对大于current的值,往后移一位,给current的插入腾出位置
            preIndex--;
          }
          arr[preIndex + 1] = current;
        }
        return arr;
      }

时间复杂度:O(n^2)

5.希尔排序 (插入排序的进阶)

希尔排序是把序列按下标的一定增量(一般为数组长度的一半)分组,对每组使用直接插入排序算法排序;随着增量的逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个序列恰好被分为一组,算法便终止。

      function shellSort(arr) {
        // 参数为需要排序的数组

        // 获取初始的增量
        let gap = Math.floor(arr.length / 2);
        while (gap >= 1) {
          // 增量小于1时结束循环

          // 从gap开始遍历,因为插入排序假设第一个是有序的
          for (let i = gap; i < arr.length; i++) {
            let j = i; // 为插入排序从后向前排序提供条件

            // 如果排序的后面的数字小于前面的,交换两个数的位置
            while (arr[j] < arr[j - gap] && j - gap >= 0) {
              let temp = arr[j];
              arr[j] = arr[j - gap];
              arr[j - gap] = temp;
              // j减小gap从后向前遍历
              j -= gap;
            }
          }

          // 增量每次都减小一半
          gap = Math.floor(gap / 2);
        }

        return arr;
      }

时间复杂度:
步长的选择是希尔排序的重要部分,只要最终步长为1任何步长序列都可以工作。

算法最开始以一定的步长进行排序。然后会继续以一定步长进行排序,最终算法以步长为1进行排序。当步长为1时,算法变为插入排序,这就保证了数据一定会被排序。

步长序列的不同,会导致最坏的时间复杂度情况的不同。

本文中,以N/2为步长的最坏时间复杂度为N^2。
用这样步长序列的希尔排序比插入排序要快,甚至在小数组中比快速排序还快,但是在涉及大量数据时希尔排序还是比快速排序慢。

6.归并排序 (大数据进行排序的常用排序算法)

将大的数组,分解为小数组,直至单个元素。然后,使用选择排序的方式,对分拆的小数组,进行回溯,并有序合并,直至合并为一个大的数组。

1.将大数组分为若干小数组,直至单个元素

⒉将若干个小数组两两合并,保证合并后的组是有序的

3.重复第二步操作直到只剩下一组,排序完成

      function mergeSort(arr) {
        return sort(arr, 0, arr.length - 1); // 注意右区间是arr.length - 1

        // sort方法,进行递归
        function sort(arr, left, right) {
          // 当left !== right时,证明还没分拆到最小元素
          if (left < right) {
            // 取中间值,分拆为两个小的数组
            const mid = Math.floor((left + right) / 2);
            const leftArr = sort(arr, left, mid);
            const rightArr = sort(arr, mid + 1, right);
            // 递归合并
            return merge(leftArr, rightArr);
          }

          // left == right, 已经是最小元素,直接返回即可
          return left >= 0 ? [arr[left]] : [];
        }

        // 合并两个有序数组
        function merge(leftArr, rightArr) {
          let left = 0;
          let right = 0;
          const tmp = [];

          // 使用双指针,对两个数组进行扫描
          while (left < leftArr.length && right < rightArr.length) {
            if (leftArr[left] <= rightArr[right]) {
              tmp.push(leftArr[left++]);
            } else {
              tmp.push(rightArr[right++]);
            }
          }

          // 合并剩下的内容
          if (left < leftArr.length) {
            while (left < leftArr.length) {
              tmp.push(leftArr[left++]);
            }
          }

          if (right < rightArr.length) {
            while (right < rightArr.length) {
              tmp.push(rightArr[right++]);
            }
          }

          return tmp;
        }
      }

时间复杂度:nlog2n

posted @ 2022-07-30 16:30  Lamb~  阅读(80)  评论(0编辑  收藏  举报