zoukankan      html  css  js  c++  java
  • 八大排序算法

    冒泡排序

    1. 思想
      1. 通过对待排序序列从前向后依次比较相邻元素的值, 若发现逆序则交换, 使值较大的元素逐渐从前移向后部, 就像水底下的气泡一样逐渐向上冒.
    2. 优化
      1. 因为排序的过程中, 各元素不断接近自己的位置, 如果一趟比较下来没有进行过交换, 说明序列有序了.因此要在排序的过程中设置一个flag判断元素是否进行过交换, 从而减少不必要的比较.
    3. 代码
          public static void bubbleSort(int[] arr) {
              boolean flag = false;
              for(int i = 0; i < arr.length - 1; i++) {
                  for(int j = 0; j < arr.length-1-i; j++) {
                      if(arr[j] > arr[j+1]) {
                          swap(arr, j, j+1);
                          flag = true;
                      }
                  }
                  if(!flag) {
                      return;
                  }
                  flag = false;
              }
          }
      
          private static void swap(int[] arr, int j, int i) {
              int temp = arr[j];
              arr[j] = arr[i];
              arr[i] = temp;
          }

    选择排序

    1. 基本介绍
      1. 是从欲排序的数据中, 按指定的规则选出某一元素, 再依规定交换位置后达到排序的目的.
    2. 基本思想
      1. 第一次从arr[0]到arr[n-1]中选取最小值, 与arr[0]交换.
      2. 第二次从arr[1]到arr[n-1]中选取最小值, 与arr[1]交换,
      3. ......
    3. 代码
          public static void selectSort(int[] arr) {
              for (int i = 0; i < arr.length - 1; i++) {
                  for (int j = i + 1; j < arr.length; j++) {
                      if (arr[i] > arr[j]) {
                          swap(arr, i, j);
                      }
                  }
              }
          }
      
          private static void swap(int[] arr, int j, int i) {
              int temp = arr[j];
              arr[j] = arr[i];
              arr[i] = temp;
          }

    插入排序

    1. 介绍
      1. 是对于欲排序的元素以插入的方式找该元素适当位置, 以达到排序的目的.
    2. 思想
      1. 把n个待排序的元素看成一个有序表和一个无序表, 开始时有序表只有一个元素, 无序表有n-1个元素, 排序过程中每次从无序表中取出第一个元素, 把它的排序码依次与有序表元素的排序码比较, 将它插入到有序表中的适当位置.
    3. 代码
          public static void insertSort(int[] arr) {
              int i, j, temp;
              for (i = 0; i < arr.length; i++) {
                  temp = arr[i];
                  j = i - 1;
                  while (j >= 0 && temp < arr[j]) {
                      arr[j + 1] = arr[j];
                      j--;
                  }
                  arr[j + 1] = temp;
              }
          }

    快速排序

    1. 介绍
      1. quickSort是对冒泡排序的一种改进.
      2. 基本思想: 通过一趟排序将要排序的数据分隔为独立的两部分, 其中一部分的所有数据比另一部分的所有数据都小, 然后按此方法对两部分数据分别快排, (整个排序过程可以看成一个递归过程), 依此下去, 直到有序.
    2. 代码
          public static void quickSort(int[] arr, int left, int right) {
              int l = left; // 左索引
              int r = right; // 右索引
              // int mid = arr[(int)Math.random()*(right-left+1)+left];
              int mid = arr[(left + right) / 2];
              // while循环的目的是让比mid小的放到其左边, 比mid大的值放到右边.
              while (l < r) {
                  // 在mid左边一直找, 直到找到>=mid的值
                  while (arr[l] < mid) {
                      l++;
                  }
                  // 在mid右边一直找, 直到找到<=mid的值
                  while (arr[r] > mid) {
                      r--;
                  }
                  // 如果 l>=r 成立, 说明mid左右两边的值已经按照左边都<=mid, 右边都>=mid了.
                  if (l >= r)
                      break;
                  // 交换
                  swap(arr, l, r);
      
                  // 如果交换完后, 发现arr[l] == mid, 让r--
                  if (arr[l] == mid)
                      r--;
                  // 如果交换完后, 发现arr[r] == mid, 让l++
                  if (arr[r] == mid)
                      l++;
              }
              // 如果l==r, 必须l++, r--, 否则栈溢出
              if (l == r) {
                  l++;
                  r--;
              }
              // 向左递归
              if (left < r)
                  quickSort(arr, left, r);
              // 向右递归
              if (right > l)
                  quickSort(arr, l, right);
          }

    希尔排序

    1. 概述
      1. 希尔排序也是一种插入排序, 它是简单插入排序经过改进后的高效版本, 也叫缩小增量排序.
    2. 基本思想
      1. 把记录按下标的一定增量分组, 对每组使用直接插入排序算法排序, 随着增量逐渐减少, 每组包含的关键词变多, 当增量减至1时, 整个文件恰好被分为一组, 算法终止.
    3. 代码
      1. 交换式, 很垃圾, 比插入排序还慢
            public static void shellSort(int[] arr) {
                int gap = arr.length / 2;
                while (gap > 0) {
                    for (int i = gap; i < arr.length; i++) {
                        for (int j = i - gap; j >= 0; j -= gap) {
                            if (arr[j] > arr[j + gap])
                                swap(arr, j, j + gap);
                        }
                    }
                    gap /= 2;
                }
            }
      2. 移动式
            public static void shellSort(int[] arr) {
                int i, j, temp;
                int gap = arr.length / 2;
                while (gap > 0) {
                    for (i = gap; i < arr.length; i++) {
                        j = i;
                        temp = arr[i];
                        if (arr[j] < arr[j - gap]) {
                            while (j - gap >= 0 && temp < arr[j - gap]) {
                                arr[j] = arr[j - gap];
                                j -= gap;
                            }
                            arr[j] = temp;
                        }
                    }
                    gap /= 2;
                }
            }

    归并排序

    1. 介绍
      1. 采取分治策略
        • 分: 把问题分成一些小的问题然后递归求解
        • 治: 把分的阶段得到的各答案"修补"在一起, 即分而治之.
    2. 代码
          // 分+合的方法
          public static void mergeSort(int[] arr, int left, int right, int[] temp) {
              if (left < right) {
                  int mid = (left + right) / 2; // 中间的索引
                  // 向左递归进行分解
                  mergeSort(arr, left, mid, temp);
                  // 向右递归进行分解
                  mergeSort(arr, mid + 1, right, temp);
                  // 合并
                  merge(arr, left, mid, right, temp);
              }
          }
      
          // 合并的方法
          public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
              int i = left; // 初始化i, 左边有序序列的初始索引
              int j = mid + 1; // 初始化j, 右边有序序列的初始索引
              int t = 0; // 指向temp数组的当前索引
      
              // 先把左右两边(有序)的数据按照规则填充到temp数组, 直到有一边全部处理完毕
              while (i <= mid && j <= right) {
                  if (arr[i] <= arr[j]) {
                      temp[t++] = arr[i++];
                  } else {
                      temp[t++] = arr[j++];
                  }
      
              }
      
              // 把剩余的填充到temp数组中
              while (i <= mid) {
                  temp[t++] = arr[i++];
              }
      
              while (j <= right) {
                  temp[t++] = arr[j++];
              }
      
              // 将temp数组的元素拷贝到arr
              // 并不是每次都拷贝所有的
              t = 0;
              int tempLeft = left;
              while (tempLeft <= right) {
                  arr[tempLeft++] = temp[t++];
              }
          }

    基数排序

    1. 介绍
      1. 属于分配式排序, 又称桶排序, 顾名思义, 它是通过键值的各个位的值, 将要排序的元素分配至某些桶中,  达到排序的作用.
      2. 是将整数按位数切割成不同的数字, 然后按每个位数分别比较.
    2. 基本思想
      1. 将所有待比较数值统一为同样的数位长度, 数位短的数前面补零, 然后从最低位开始, 依次进行依次排序, 这样从最低位排序一直到最高位排序完成以后, 数列就有序了.
      2. 第一轮: 将每个元素的个位数取出, 然后看这个数应该放在哪个对应的桶, 按照这个桶的顺序(一维数组的下标依次取出数据, 放入原来数组)
      3. 第二轮: 将每个元素的十位数取出, 然后看这个数该放入哪个对应的桶, 按照这个桶的顺序(一维数组的下标依次取出数据, 放入原来数组)
      4. 依次下去, 直到有序
    3. 小数不能用基数排序
    4. 代码
          //该代码仅仅实现了非负数的排序, 若有负数出现, 则会ArrayIndexOutOfBoundsException
          public static void radixSort(int[] arr) {
              int[][] bucket = new int[10][arr.length];
              int[] cnt = new int[10];
              int max = arr[0];
              for (int i = 1; i < arr.length; i++) {
                  if (arr[i] > max)
                      max = arr[i];
              }
              int maxLen = ("" + max).length();
              for (int i = 0, n = 1; i < maxLen; i++, n *= 10) {
                  for (int j = 0; j < arr.length; j++) {
                      int digit = arr[j] / n % 10;
                      bucket[digit][cnt[digit]++] = arr[j];
                  }
                  int index = 0;
                  for (int k = 0; k < arr.length; k++) {
                      if (cnt[k] != 0) {
                          for (int l = 0; l < cnt[k]; l++)
                              arr[index++] = bucket[k][l];
                      }
                      cnt[k] = 0;
                  }
              }
          }
    5. 一轮一轮的讲解
          public static void radixSort(int[] arr) {
      
              // 定义一个二维数组表示10个桶, 每个桶就是一个一维数组
              // 为了防止放数时数据溢出, 则每个桶的大小定为arr.length
              // 很明显, 基数排序是用空间换时间的经典算法.
              int[][] bucket = new int[10][arr.length];
      
              // 为了记录每个桶中实际存放了多少个数据, 我们定义一个一维数组来记录各个桶每次放入数据个数
              int[] cnt = new int[10];
      
              // 第一轮(针对每个元素的个位进行排序处理)
              for (int i = 0; i < arr.length; i++) {
                  // 取出每个元素的个位
                  int digit = arr[i] % 10;
                  // 放入到对应桶中
                  bucket[digit][cnt[digit]++] = arr[i];
              }
              // 按照桶的顺序, 放入原来数组
              int index = 0;
              // 遍历每个桶, 并将每个桶中数据放入原数组
              for (int i = 0; i < cnt.length; i++) {
                  // 如果桶中有数据, 才放入到原数组
                  if (cnt[i] != 0) {
                      // 循环第i个桶, 放入数据即可
                      for (int j = 0; j < cnt[i]; j++) {
                          arr[index++] = bucket[i][j];
                      }
                  }
                  // 第一轮结束把cnt数组清空
                  cnt[i] = 0;
              }
              System.out.println("第一轮对个位的处理: " + Arrays.toString(arr));
      
              // 第二轮(针对每个元素的十位进行排序处理)
              for (int i = 0; i < arr.length; i++) {
                  // 取出每个元素的十位
                  int digit = arr[i] / 10 % 10;
                  // 放入到对应桶中
                  bucket[digit][cnt[digit]++] = arr[i];
              }
              // 按照桶的顺序, 放入原来数组
              index = 0;
              // 遍历每个桶, 并将每个桶中数据放入原数组
              for (int i = 0; i < cnt.length; i++) {
                  // 如果桶中有数据, 才放入到原数组
                  if (cnt[i] != 0) {
                      // 循环第i个桶, 放入数据即可
                      for (int j = 0; j < cnt[i]; j++) {
                          arr[index++] = bucket[i][j];
                      }
                  }
                  cnt[i] = 0;
              }
              System.out.println("第二轮对个位的处理: " + Arrays.toString(arr));
      
              // 第三轮(针对每个元素的百位进行排序处理)
              for (int i = 0; i < arr.length; i++) {
                  // 取出每百元素的个位
                  int digit = arr[i] / 100 % 10;
                  // 放入到对应桶中
                  bucket[digit][cnt[digit]++] = arr[i];
              }
              // 按照桶的顺序, 放入原来数组
              index = 0;
              // 遍历每个桶, 并将每个桶中数据放入原数组
              for (int i = 0; i < cnt.length; i++) {
                  // 如果桶中有数据, 才放入到原数组
                  if (cnt[i] != 0) {
                      // 循环第i个桶, 放入数据即可
                      for (int j = 0; j < cnt[i]; j++) {
                          arr[index++] = bucket[i][j];
                      }
                  }
                  cnt[i] = 0;
              }
              System.out.println("第三轮对个位的处理: " + Arrays.toString(arr));
          }

    堆排序

    ################

    总结

        

    • In-place: 不占用额外内存, Out-place: 占用额外内存
  • 相关阅读:
    LeetCode 227. Basic Calculator II
    LeetCode 224. Basic Calculator
    LeetCode 103. Binary Tree Zigzag Level Order Traversal
    LeetCode 102. Binary Tree Level Order Traversal
    LeetCode 106. Construct Binary Tree from Inorder and Postorder Traversal
    LeetCode 105. Construct Binary Tree from Preorder and Inorder Traversal
    LeetCode 169. Majority Element
    LeetCode 145. Binary Tree Postorder Traversal
    LeetCode 94. Binary Tree Inorder Traversal
    LeetCode 144. Binary Tree Preorder Traversal
  • 原文地址:https://www.cnblogs.com/binwenhome/p/12838429.html
Copyright © 2011-2022 走看看