经典排序算法

排序

  1. 分类

    • 外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行(k路归并)
    • 内排序:所有操作再内存就可以完成
    • 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。
    • 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。
    • 批注 2021-06-07 220103
    • 批注 2021-06-07 220217
  2. 冒泡排序:

    • 比较两个相邻的元素,如果前一个比后一个大,就交换 = 每次回确定一个最大数并且交换到对应的末位

    • public static void main(String[] args) {
          int[] arr = {2, 4, 1, 14, 22, -5, 9, 0};
          int len = arr.length;
          for (int i = 0; i < len; i++) {
              for (int j = 1; j < len - i; j++) {
                  //前一个比后面的大就交换
                  if (arr[j - 1] > arr[j]) {
                      int tmp = arr[j - 1];
                      arr[j - 1] = arr[j];
                      arr[j] = tmp;
                  }
              }
          }
          System.out.println(Arrays.toString(arr));
      }
      
  3. 选择排序:

    • 初始状态:无序区为 [0, n),有序区为空

    • 每次从无序区找到第i小的数,将其与i位置的数交换,无序区减一

    • public static void main(String[] args) {
          int[] arr = {2, 4, 1, 14, 22, -5, 9, 0};
          int len = arr.length;
          for (int i = 0; i < len - 1; ++i) {
              int min = i;
              for (int j = i + 1; j < len; ++j) {
                  if (arr[min] > arr[j]) {
                      min = j;
                  }
              }
              int tmp = arr[i];
              arr[i] = arr[min];
              arr[min] = tmp;
          }
          System.out.println(Arrays.toString(arr));
      }
      
  4. 插入排序:

    • 初始状态:一般认为第数组的一个数是已排序状态,之后为还未排序区域

    • 每次将当前数与前一个数比较,如果比前一个数小,就交换,直到遇到比它小的或到头了

    • public static void main(String[] args) {
          int[] arr = {2, 4, 1, 14, 22, -5, 9, 0};
          for (int i = 1; i < arr.length; i++) {
              int inNext = arr[i]; //待排序的数
              int index = i - 1; //待排序数的前一个数下标
              while (index >= 0 && inNext < arr[index]) {
                  arr[index + 1] = arr[index];
                  index--;
              } //退出循环代表以找到位置
              arr[index + 1] = inNext;
          }
          System.out.println(Arrays.toString(arr));
      }
      
  5. 希尔排序:(缩小增量排序)

    • 简单插入排序的改进版,与插入排序的不同之处在于,它会优先比较距离较远的元素

    • 思路:每次对数组分为Len/2组,每组进行比较交换,每次分组越来越细

    • public static void main(String[] args) {
          int[] arr = {2, 4, 1, 14, 22, -5, 9, 0};
      //        sort(arr);
          sortMove(arr);
      }
      //交换法
      public static void sort(int[] arr) {
          int temp = 0;
          for (int interval = arr.length / 2; interval > 0; interval /= 2) {
              for (int i = interval; i < arr.length; i++) {
                  for (int j = i - interval; j >= 0; j -= interval) {
                      if (arr[j] > arr[j + interval]) {
                          temp = arr[j];
                          arr[j] = arr[j + interval];
                          arr[j + interval] = temp;
                      }
                  }
              }
          }
          System.out.println(Arrays.toString(arr));
      }
      //移位法
      public static void sortMove(int[] arr) {
          for (int interval = arr.length / 2; interval > 0; interval /= 2) {
              for (int i = interval; i < arr.length; i++) {
                  int j = i;
                  int temp = arr[j];
                  if (arr[j] < arr[j - interval]) {
                      while (j - interval >= 0 && temp < arr[j - interval]) {
                          arr[j] = arr[j - interval];
                          j -= interval;
                      }
                      arr[j] = temp;
                  }
              }
          }
          System.out.println(Arrays.toString(arr));
      }
      
  6. 归并排序:

    • 该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并

    • 步骤:分 -> 合

      1. 把长度为n的输入序列分成两个长度为n/2的子序列;
      2. 对这两个子序列分别采用归并排序;
      3. 将两个排序好的子序列合并成一个最终的排序序列;
    • //分 : 将数组按二分法分为左右子数组(左右指针),递归,直到left == right跳出递归;
      //合 : 设置两个指针分别对应左右数组,比较大小按序合并为一个数组
      
  7. 快速排序:

    • 快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。(分治法)

    • 步骤

      1. 从数列中挑出一个元素,称为 “基准”(pivot);
      2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
      3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
    • public static void main(String[] args) {
          int[] arr ={32,6,88,63,2,95,33,8};
          sort(arr);
          System.out.println(Arrays.toString(arr));
      }
      
      public static void sort(int[] arr){
          int first = 0;
          int tail = arr.length-1;
          sort(arr,first,tail);
      }
      
      public static void sort(int[] arr,int first,int tail){
          if (first<tail){
              int index = partition(arr,first,tail);
              //向左排序
              sort(arr,first,index-1);
              //向右排序
              sort(arr,index+1,tail);
          }
      }
      
      public static int partition(int[] arr, int first, int tail){
          int i = first;
          int j = tail;
          int temp = arr[first];
          while (i<j){
              //从右到左,找到第一个小于temp的值
              while (arr[j]>=temp && i<j){
                  j--;
              }
              //将找到的值填入arr[i],i指针向右移一位
              if (i<j){
                  arr[i] = arr[j];
                  i++;
              }
              //从左到右,找到第一个大于temp的值
              while (arr[i]<temp && i<j){
                  i++;
              }
              //将找到的值填入arr[j],j指针向左移一位
              if (i<j){
                  arr[j] = arr[i];
                  j--;
              }
          }
          arr[i] = temp;
          return i;
      }
      
    • 优化:

      • 对于快排,最主要的点就是基准值的确定,当基准值的数值在中间区域时是最优的,当基准值是最大或者最小时,快排的时间复杂度将是最差的[O(n^2)]
      • 可以对区间取左,右,中三个数,取中位数为基准值
  8. 堆排序:

    • 堆排序(HeapSort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点
    • Java中优先队列(PriorityQueue)实现的方式
    • 步骤:
      1.
  9. 计数排序

    • 计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数
  10. 桶排序

    • 桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序
  11. 基数排序

    • 基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前

    • 步骤:

      1. 取得数组中的最大数,并取得位数;
      2. arr为原始数组,从最低位开始取每个位组成bucket数组;
      3. 对bucket进行计数排序(利用计数排序适用于小范围数的特点);
    • public static void main(String[] args) {
          int[] arr ={44,256,77,2,89,60,456};
          sort(arr);
          System.out.println(Arrays.toString(arr));
      }
      
      public static void sort(int[] arr){
          int[][] bucket = new int[10][arr.length];
          int[] Effectiveindex = new int[10];
          //找出数组最大的数,得出最大数的位数,来确定循环次数
          int max = arr[0];
          for (int i=1;i<arr.length;i++){
              if (arr[i]>max){
                  max = arr[i];
              }
          }
          int max_length = (max +" ").length();//计算max的位数
          for (int k=0,n=1;k<max_length;k++,n *=10){
              //按基数放数据;记录存放次数
              for (int i=0;i<arr.length;i++){
                  int Remainder = arr[i] /n % 10;
                  bucket[Remainder][Effectiveindex[Remainder]] = arr[i];
                  Effectiveindex[Remainder]++;
              }
              int index = 0;
              //将桶里的数依次放入原数组
              for (int j=0;j<Effectiveindex.length;j++){
                  if (Effectiveindex[j]!=0){//存放进j桶的次数
                      for (int p=0;p<Effectiveindex[j];p++){
                          arr[index++] = bucket[j][p];
                      }
                  }
                  Effectiveindex[j]=0;
              }
          }
      }
      
posted @ 2021-06-07 22:04  hehell  阅读(51)  评论(0)    收藏  举报