九大排序算法总结

九大排序算法

最近总结了一下各大常见的算法,并用Java代码实现了一遍。

  1. (平均)时间复杂度O(N^2)

    1. 冒泡排序

      时间复杂度最好的情况是O(N)、最坏情况是O(N^2) 空间复杂度(1) 稳定(稳定的意思就是一个数组中相同数据在排序后位置不变)

      思想: 比较相邻两个数据的大小。

      public static int[] sort(int[] data) {
          if(data == null) return null;
          for(int i =0;i<data.length;i++) {
              for(int j=0;j<data.length - 1-i;j++) {
                  if(data[j] > data[j+1] ) {
                      int temp = data[j];
                      data[j]= data[j+1];
                      data[j+1] = temp;
                  }
              }
          }
          return data;
      }
      public static int[] optimizeSort(int[] data) {
          if(data ==null) return null;
          for(int i = 0;i< data.length;i++) {
              boolean isSorted = false;
              for(int j = 0;j<data.length - 1 -i;j++) {
                  if(data[j]> data[j+1] ) {
                      int temp = data[j];
                      data[j]= data[j+1] ;
                      data[j+1] = temp;
                      isSorted = true;
                  }
              }
              if(!isSorted) {
                  //如果排序发现并没有数据进行交换,说明此为有序数据了
                  //要考虑有序数据
                  break;
              }
          }
          return data;
      }
    2. 插入排序

      时间复杂度:最好O(N) 最坏O(N^2) 空间复杂度 O(1) 稳定

      思想:假设待插入数据前的数据都是有序的,不断往其中插入数据,一般都是以第一个数据为有序数组,然后插入。

      public static int[] sort(int[] data) {
          if (data == null)
              return null;
          // 向有序数组内插入数据
          for (int i = 1; i < data.length; i++) {
              int value = data[i];// 要插入的值
              int j = i - 1;
              for (; j >= 0; --j) {
                  if (value < data[j]) {
                      data[j + 1] = data[j];
                  } else {
                      break;
                  }
              }
              data[j + 1] = value;
      ​
          }
          return data;
      }
    3. 选择排序

      时间复杂度最好最坏情况都是O(N^2) 空间复杂度 O(1) 不稳定(每次选择后面的最小的元素进行交换)

      思想:选择一个数作为最小值(最大值),然后找出后面的最小(最大)的元素和其交换。

      public static int[] sort(int[] data) {
          if(data == null) return null;
              
          for(int i = 0;i<data.length;i++) {
              int mim = i;
              for(int j= mim +1;j<data.length;j++) {
                  if(data[mim] >data[j] ) {
                      mim = j;
                  }
              }
              if(mim != i) {
                  int temp = data[i];
                  data[i]= data[mim];
                  data[mim]= temp; 
              }
          }
          return data;
      }
  2. (平均)时间复杂度O(N * logN)

    1. 希尔排序

      冒泡排序的升华版 时间复杂度:最好O(N) 最坏O(N^2) 空间复杂度 O(1) 不稳定(这里画个排序示意图就明白了)

      思想: 确定步长,即当步长为h,第一次先比较0、h-1、2h-1 .... 位置上的数据,然后步长逐步缩小

      public static int[] heerSort(int[] data) {
          if (data == null)
              return null;
          int h = 1;// 步长
          int n = data.length;
          while (h < n / 3) {
              h = 3 * h + 1; //为什么是3?这是由数学家经过大量实验得来的
          }
          // 0...h-1 // h ....2h-1...
          while (h >= 1) {
              for (int i = h; i < n; i++) {
                  int index;
                  int e = data[i];
                  for (index = i; index >= h && e < data[index - h]; index -= h) {
                      // 交换
                      data[index] = data[index - h];
                  }
                  data[index] = e;
              }
      ​
              h /= 3;
          }
          return data;
      }
    2. 快速排序

      时间复杂度:最好O(N * logN) 最坏O(N^2) 空间复杂度O(logN) ~O(N) 不稳定(如果和基点值一样大的有多个就会出现)。这里最好和最坏肯定是指整个数组是否有序,基点选择的是哪一个

      思想:选择一个基点pivot,使得左边的数比它小,右边的数比它大。

      /**这里包括普通快排 二路 三路优化版快排**/
      public static int[] sort(int[] data) {
          if (data == null)
              return null;
          //quickSort(data, 0, data.length - 1);
          parition3(data,0,data.length -1);
          return data;
      }
      ​
      private static void quickSort(int[] data, int left, int right) {
          if (left >= right)
              return;
          int pos = parition2(data, left, right);
          quickSort(data, left, pos - 1);
          quickSort(data, pos + 1, right);
      ​
      }
      ​
      private static int parition(int[] data, int left, int right) {
          int index = new Random().nextInt((right - left + 1));
          int value = data[index + left];// 随机选一个作为基准数
          data[index + left] = data[left];
          data[left] = value;
      //  int value = data[left];
          int j = left;
          for (int i = left + 1; i <= right; i++) {
              if (data[i] < value) {
                  data[j] = data[i];
                  ++j;
                  data[i] = data[j];
                  // data[j] = value;
              }
          }
          data[j] = value;
          return j;
      ​
      }
      ​
      private static int parition2(int[] data, int left, int right) {
          int number = right - left;
          int index = new Random().nextInt(number);
          int value = data[left + index];// 随机选一个作为基准数
          data[index + left] = data[left];
          data[left] = value;
          int i = left + 1;
          int j = right;
          while (i < j) {
              while (i <= j && data[i] <= value) {
                  i++;
              }
      //      data[j] = data[i];
              while (i <= j && data[j] >= value) {
                  j--;
              }
      //      data[i] = data[j];
              if (i > j)
                  break;
              int temp = data[i];
              data[i] = data[j];
              data[j] = temp;
              i++;
              j--;
          }
          data[left] = data[j];
          data[j] = value;
      //  data[i] = value;
          return j;
      ​
      }
      ​
      private static void parition3(int[] data, int left, int right) {
          if(left>= right) return;
          // if(right - left <= 15) {
          // 插入排序
          // }
          int number = right - left;
          int index = new Random().nextInt(number);
          int value = data[left + index];// 随机选一个作为基准数
          data[index + left] = data[left];
          data[left] = value;
      //  int value = data[left];
          int lt = left; // data[ left + 1 ... lt] < v
          int gt = right + 1;// data[gt ... right] > v
          int i = left + 1; // data[lt+ 1 ... i] == v
          while (i < gt) {
              if (data[i] < value) {
                  int temp = data[lt + 1];
                  data[lt + 1] = data[i];
                  data[i] = temp;
                  i++;
                  lt++;
              } else if (data[i] > value) {
                  int temp = data[gt - 1];
                  data[gt - 1] = data[i];
                  data[i] = temp;
                  gt--;
      ​
              } else {
                  i++;
              }
          }
          data[left] = data[lt];
          data[lt] = value;
          parition3(data, left, lt - 1);
          parition3(data, gt, right);
      }
    3. 归并排序

      时间复杂度 最好O(N * logN) 最好(N * logN) 稳定 空间复杂度O(N) 至于为什么是O(N)呢?因为新建辅助空间最大也就是N长,而归并排序合并的时候是自底向上的,方法存在于栈帧上,执行完后,栈上空间自动会释放,所以最后是O(N),而快排是自顶向下的,相当于树的深度为logN,而最坏情况是将基点选在最后一个元素上,此时深度是N,所以快排空间复杂度为O(logN) ~ O(N)

      思想:采用分而治之思想,将数组分成两半,然后再分,再分,到最后不能分了,就开始合并。

      /**
       * 自顶向下 先分称一个一组 O(NlogN)
       * 
      * @param data
      * @return
      */
      public static int[] sort(int[] data) {
          if (data == null)
              return null;
          int left = 0;
          int right = data.length - 1;
          mergeSort(data, left, right);
          return data;
      }
      ​
      /**
      * 自底向上
      * @param data
      * @return
      */
      public static int[] sortFormBootm(int[] data) {
          if (data == null)
              return null;
          //这里可以先用插入排序
          for(int i=0;i<data.length -1;i+=20) {
              insertSort(data, i, Math.min(i+19, data.length -1));
          }
          int n = data.length-1;
          for (int sz = 1; sz <= n; sz += sz) {//步长 逐一合并
              for(int i =0;i<n-sz;i+=sz+sz) {
                  if(data[i+sz-1] > data[i+sz]) {
                      merge(data, i, i+sz-1, Math.min(i+2*sz-1,n-1));
                  }
              }
          }
          return data;
      }
      ​
      private static void mergeSort(int[] data, int left, int right) {
          int mid = (right - left+1) / 2;
          // 将两边进行拆分
          mergeSort(data, left, mid);
          mergeSort(data, mid + 1, right);
          // 合并
          if (data[mid] > data[mid + 1]) {// 当合并时,最后一个数大于第一个数时才需要排序
              merge(data, left, mid, right);
          }
      }
      ​
      private static void merge(int[] data, int left, int mid, int right) {
          // 总共有 right - left + 1 个元素
          int[] aux = new int[right - left + 1];
          for (int i = left; i <= right; i++) {
              aux[i - left] = data[i];
          }
          int i = left, j = mid + 1;
          for (int k = left; k <= right; k++) {
              if (i > mid) {
                  // left 这边没有数据了
                  data[k] = aux[j - left];
                  ++j;
              } else if (j > right) {
                  // right 这边没有数据了
                  data[k] = aux[i - left];
                  ++i;
              } else if (aux[i - left] < aux[j - left]) {
                  data[k] = aux[i - left];
                  ++i;
              } else {
                  data[k] = aux[j - left];
                  ++j;
              }
          }
      }
      private static void insertSort(int[] data,int left,int right) {
          if(right< left) return;
          for(int i = left;i <= right;i++) {
              int value = data[i + 1];
              int j = i+1;
              for(;j>=0 && value < data[j-1];--j) {
                  data[j] = data[j-1];
              }
              data[j] = value;
          }
      }
    4. 堆排序

      时间复杂度最好、最坏情况都是O(logN) 不稳定 空间复杂度O(1)

      思想:构建一个最大堆,每次取出堆顶元素,就得将堆底元素放到堆顶,然后排序。(最大堆:父节点比左右孩子节点值大)

      private int[] data;//数组从 1 下标开始存数据。k 子节点 为 2k 2k+1 ,如果从0开始,子节点为 2k+1,2k+2
      private int count;
      private int capacity;
      ​
      public HeapSort(int capacity) {
          data = new int[capacity];
          count = 0;
          this.capacity = capacity;
      ​
      }
      ​
      public boolean isEmpty() {
          return count == 0;
      }
      ​
      public void insert(int a) {
          if (count > capacity)
              return;
          data[count + 1] = a;
          shiftUp(count + 1);
          count++;
      }
      ​
      public int extractMax() {
          int result = data[1];
          data[1] = data[count];
          data[count] = 0;
          count--;
          shiftDown(1);
          return result;
      }
          
      ​
      /**
       * 插入第k个节点 logN
      * 
      * @param k
      */
      private void shiftUp(int k) {
          while (k > 1 && data[k / 2] < data[k]) {// 父节点 值 小于 子节点 就需要交换
              int temp = data[k];
              data[k] = data[k / 2];
              data[k / 2] = temp;
              k = k / 2;
          }
      }
      ​
      private void shiftDown(int k) {
          int value = data[k];
          while (2 * k <= count) {// 父节点为k,左子孩子为 2*k
              int j = 2 * k;
              if (j + 1 <= count && data[j] < data[j + 1]) {
                      j++;
              }
              if (value >= data[j]) {
                  break;
              }
              //int temp = data[j];
              //data[j] = data[k];
              data[k] = data[j];
      ​
              k = j;// 继续深入树内
          }
          data[k] = value;
      }
  3. 时间复杂度为O(M+N)

    1. 桶排序 不稳定

      思想: 将数据分到有限量的桶子里,对每个桶子数据进行排序,然后取出来就是有序的了。

      做法:每个桶存放的数据范围先定下来,从最小值到最大值,新建一个二位数组(或List),二维存放的是数据,一维是桶的编号。首先通过 element * N /(max + 1) 得到 需要放到哪个桶中,然后存放数据就可以了。

    2. 计数排序

      思想:基于桶排序,例如 0~10的数据,数据分别为 2 3 8 5 5,则需要建一个长度为11数组桶,编号从0到10,0号桶存放0出现的次数,以此类推,遍历完后,打印桶号,打印次数为桶号对应数据的大小。

      /**
      * 如果已知这些数的范围 例如为0 到 100 分
      * O(2*(m+n)) -> O(M)
      * @param data
       */
      public static void sort(int... data) {
          int[] a = new int[101];
          //初始值都为0,代表出现过0次该分
          for(int i =0;i<data.length;i++) {// n次
              a[data[i]] ++;
          }
          for(int i=0;i<a.length;i++) {// m 次
              if(a[i]!= 0 ) {
                  for(int j = 0;j<a[i];j++) { // m- 不等于0 次
                      System.out.print(i+"\t"); // n次
                  }
      //          System.out.println();
              }
                  
          }
              
      }
      /**
      * 计数排序 基于桶排序思想,由于数过多,可以使用数出现的频率作为桶编号
      * @param data
      */
      public static void sortCount(int min,int max,int... data) {
          int length = max - min + 1;
          int[] a = new int[length];
              
          for(int i = 0;i<data.length;i++) {//最小值对应数组下标 0
              a[data[i]- min] ++;//出现了多少次
          }
          for(int i =0;i<a.length;i++) {
              if(a[i]!= 0 ) {
                  for(int j = 0;j<a[i];j++) {
                      System.out.print((i+min)+"\t"); 
                  }
              }
          }
      }
    3. 基数排序

      思想:基于桶排序,利用计数排序的思想,先对个位上的数字建立桶,然后入桶,然后再对十位上的数字,以此类推,最后打印出数据就是拍好序了的。

      /**
       * 基数排序
      * 空间O(N)
      * 时间O(N*digist)
      * @param max
      * @param data
      */
      public static void sortBase(int max,int... data) {
          int base = 10;
          int length= 1;
          while(max>base) {
              length ++;
              base *= 10;
          }
              
          int[] temp = new int[data.length];//0.....9
          int dividend = 1;
          while(length>0) {
              int[] count = new int[10];
              //统计某一位出现相同数字的个数
              for(int i = 0;i<data.length;i++) {
                  int index = data[i]/dividend %10;
                  count[index]++;
              }
                  
              //统计个位相同的数在数组中出现的位置
              for(int i = 1;i<10;i++) { //0 出现0次 1出现2次 2出现3次 那么2的下标为 1 2 3
                  count[i]+= count[i-1]; //i出现的位置为 i +(i-1)
              }
              for(int i =data.length-1;i>=0;i--) {
                  int index = data[i]/dividend %10; 
      ​
                  temp[--count[index]] = data[i];//先使用再增加,记录的是个数某个数字出现的起始位置
              }
              dividend *= 10;
              length --;
          }
              
          for(int n:temp) {
              System.out.print(n+"\t");
          }
      }
          
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值