1、选择排序

每次选择剩余序列中最小的数,然后与当前数进行交换。
时间复杂度为平方级别,时间复杂度不需要额外空间。

  1. package sort;
  2. /**
  3. * @author zwlstart
  4. * @create 2020-12-20 17:14
  5. */
  6. public class Selection {
  7. /**
  8. * 选择排序,每次都选择剩余的当中最小的元素
  9. * @param a
  10. */
  11. public static void sort(int[] a){
  12. for (int i = 0; i < a.length; i++) {
  13. int min = i;
  14. for (int j = i + 1; j < a.length; j++) {
  15. if (a[j] < a[min]){
  16. min = j;
  17. }
  18. }
  19. int temp = a[i];
  20. a[i] = a[min];
  21. a[min] = temp;
  22. }
  23. return;
  24. }
  25. }

2、插入排序

每次将当前数字插入到已排好序的数组当中。
时间复杂度为平方级别,空间复杂度不需要额外空间。

  1. package sort;
  2. /**
  3. * @author zwlstart
  4. * @create 2020-12-20 17:14
  5. */
  6. public class Insertion {
  7. /**
  8. * 插入排序,每次将元素插入到已排好序的数组中,并保持有序,
  9. * 当索引到达数组末端时,排序结束
  10. * @param a
  11. */
  12. public static void sort(int[] a){
  13. for (int i = 1; i < a.length; i++) {
  14. for (int j = i; j > 0; j--) {
  15. if (a[j] < a[j - 1]){
  16. int temp = a[j];
  17. a[j] = a[j - 1];
  18. a[j - 1] = temp;
  19. }
  20. }
  21. }
  22. return;
  23. }
  24. }

3、希尔排序

是插入排序的一个变形。
相当于把整个数组看做多个h-子数组,子数组是相互独立的,然后对子数组进行排序,排序方法是插入排序,然后把h逐渐减小,减小的时候再排序之后就是有序数组。

  1. package sort;
  2. /**
  3. * @author zwlstart
  4. * @create 2020-12-20 17:44
  5. */
  6. public class Shell {
  7. /**
  8. * 希尔排序
  9. * @param a
  10. */
  11. public static void sort(int[] a){
  12. int n = a.length;
  13. int h = 1;
  14. while (h < n){
  15. h = 3 * h + 1;
  16. }
  17. while (h >= 1){
  18. //将数组变为h有序子数组,其中每个h子数组的排序过程是插入排序
  19. //然后逐渐将h变小
  20. //直到将h变为1
  21. //表明三重循环,但每次循环的比较和交换顺序都很小
  22. for (int i = h; i < n; i++) {
  23. for (int j = i; j >= h && a[j] < a[j - h]; j -= h) {
  24. int temp = a[j];
  25. a[j] = a[j - h];
  26. a[j - h] = temp;
  27. }
  28. }
  29. h /= 3;
  30. }
  31. return;
  32. }
  33. }

4、归并排序

即要将数组排序,可以先递归地将它分成两半分别排序,然后将结果归并起来。
归并排序的时间复杂度为Nlpog(N)级别,空间复杂度为O(N)

4.1、自顶向下的归并排序

  1. package sort;
  2. /**
  3. * @author zwlstart
  4. * @create 2020-12-20 20:57
  5. */
  6. public class Merge {
  7. private static int[] aux;
  8. public static void sort(int[] a){
  9. aux = new int[a.length];
  10. merge(a,0,a.length - 1);
  11. }
  12. /**
  13. * 希尔排序,自顶向下
  14. * @param a
  15. * @param lo
  16. * @param hi
  17. */
  18. public static void merge(int[] a,int lo,int hi){
  19. if (lo >= hi){
  20. return;
  21. }
  22. int mid = lo + (hi - lo) / 2;
  23. merge(a,lo,mid);
  24. merge(a,mid + 1,hi);
  25. int i = lo,j = mid + 1;
  26. for (int k = lo; k <= hi;k++) {
  27. if (i > mid){
  28. aux[k] = a[j++];
  29. }else if (j > hi){
  30. aux[k] = a[i++];
  31. }else if (a[i] < a[j]){
  32. aux[k] = a[i++];
  33. }else{
  34. aux[k] = a[j++];
  35. }
  36. }
  37. for (int k = lo; k <= hi; k++) {
  38. a[k] = aux[k];
  39. }
  40. }
  41. }

4.2、自底向上的希尔排序

  1. package sort;
  2. /**
  3. * @author zwlstart
  4. * @create 2020-12-20 21:26
  5. */
  6. public class MergeBU {
  7. private static int[] aux;
  8. /**
  9. * 自底向上的希尔排序
  10. * @param a
  11. */
  12. public static void sort(int[] a){
  13. aux = new int[a.length];
  14. for (int i = 1; i < a.length; i = (i +i)) {
  15. for (int j = 0; j < a.length - i; j += (i + i)) {
  16. merge(a,j,j + i - 1,Math.min(j + i + i - 1,a.length -1));
  17. }
  18. }
  19. }
  20. public static void merge(int[] a,int lo,int mid,int hi){
  21. int i = lo,j = mid + 1;
  22. for (int k = lo; k <= hi;k++) {
  23. if (i > mid){
  24. aux[k] = a[j++];
  25. }else if (j > hi){
  26. aux[k] = a[i++];
  27. }else if (a[i] < a[j]){
  28. aux[k] = a[i++];
  29. }else{
  30. aux[k] = a[j++];
  31. }
  32. }
  33. for (int k = lo; k <= hi; k++) {
  34. a[k] = aux[k];
  35. }
  36. }
  37. }

5、快速排序

快速排序是一种基于分治的排序算法。它将一个数组分为两个子数组,将两部分独立地排序。快速排序每次循环都能把一个数字放在它的最终位置。
快速排序是所有排序算法中平均性能最好的。平均时间复杂度为线性对数级别,不需要额外的空间。

5.2、基础算法

  1. package sort;
  2. /**
  3. * @author zwlstart
  4. * @create 2020-12-20 21:53
  5. */
  6. public class QuickSort {
  7. public static void sort(int[] a,int start,int end){
  8. if (start >= end){
  9. return;
  10. }
  11. int i = start,j = end;
  12. int temp = i;
  13. while (i < j){
  14. if (a[i] == a[temp]){
  15. while (a[j] >= a[temp] && j > i){
  16. j--;
  17. }
  18. if (j > i){
  19. int b = a[temp];
  20. a[temp] = a[j];
  21. a[j] = b;
  22. i = temp;
  23. temp = j;
  24. }
  25. }else{
  26. while (a[i] <= a[temp] && i < j){
  27. i++;
  28. }
  29. if (j > i){
  30. int b = a[temp];
  31. a[temp] = a[i];
  32. a[i] = b;
  33. j = temp;
  34. temp = i;
  35. }
  36. }
  37. }
  38. sort(a,start,temp - 1);
  39. sort(a,temp + 1,end);
  40. }
  41. }

5.2、算法改进

  1. 切换到插入排序:对于小数组,快速排序比插入排序慢。
  2. 三取样切分,基础算法是每次取第一个值进行切分,可能切分效果不好,可以取三个数,然后选择三个数的中间数进行切分。

    5.3、熵最优的排序

    主要针对的是存在大量重复数字的排序。
    简单的想法是把数组分为三部分,小于、等于、大于切分元素的三部分。然后只需要继续递归排序小于和大于的部分。
    三向切分的快速排序: ```java package sort;

import java.util.HashMap;

/**

  • @author zwlstart
  • @create 2020-12-20 22:22 */ public class Quick3Way {

    public static void sort(int[] a,int start,int end){

    1. if (start >= end){
    2. return;
    3. }
    4. //lt记录切分元素,小于lt的都说比切分元素小的
    5. //在lt和i之间的都说和切分元素相等的
    6. //在gt之后的都是比切分元素大的
    7. int lt = start, i = start + 1,gt = end;
    8. int v = a[start];
    9. while (i <= gt){
    10. int cmp = a[i] - v;
    11. if (cmp < 0){
    12. //说明该元素比切分元素小,该元素需要转移到lt之后
    13. int temp = a[i];
    14. a[i] = a[lt];
    15. a[lt] = temp;
    16. i++;
    17. lt++;
    18. }else if (cmp > 0){
    19. int temp = a[i];
    20. a[i] = a[gt];
    21. a[gt] = temp;
    22. gt--;
    23. }else{
    24. i++;
    25. }
    26. }
    27. sort(a,start,lt - 1);
    28. sort(a,gt + 1, end);

    } } ```