排序

  排序是将一个无序的序列按照一定顺序排列成一个有序序列的操作。其中,升序是指序列按从小到大排序,降序是指序列按从大到小排序。常见的排序算法有:冒泡排序、快速排序、选择排序、插入排序、归并排序、堆排序等。

  若一个序列中存在相同的两个元素,在排序后:

    a.若这两个元素先后顺序不变,则称该排序算法是稳定的。

    b.若这两个元素先后顺序变化,则称该排序算法是不稳定的。

  例如:对于序列(56, 34, 47, 23, 66, 18, 82, 47)中有两个47,在第一个47下划线。

    若排序结果为(18, 23, 34, 47, 47, 56, 66, 82),即两个47的先后顺序不变,则该排序算法是稳定的;

    若排序结果为(18, 23, 34, 47, 47, 56, 66, 82),即两个47的先后顺序变化,则该排序算法是不稳定的。

  Method枚举类定义的是排列方式,存在两个值:ASC(升序)和DESC(降序)。

 1 /**
 2  * 排序方式
 3  */
 4 enum Method {
 5     /**
 6      * 升序
 7      */
 8     ASC, 
 9 
10     /**
11      * 降序
12      */
13     DESC;
14 }
Method

  swap方法是将数组中两个元素互换位置。

 1 /**
 2  * 将数组中两个元素互换位置
 3  * @param arr
 4  * @param i
 5  * @param j
 6  */
 7 private static void swap(int[] arr, int i, int j) {
 8     int temp = arr[i];
 9     arr[i] = arr[j];
10     arr[j] = temp;
11 }
swap

冒泡排序

  冒泡排序是将序列中相邻两个元素进行比较,若为逆序,则交换位置。

  一趟升序的冒泡排序的过程为:序列第一个元素与第二个元素进行比较,小的在前大的在后,接着第二个元素与第三个元素比较,也是小的在前大的在后......直到最后两个元素比较交换位置完。

  一趟降序的冒泡排序的过程为:序列第一个元素与第二个元素进行比较,大的在前小的在后,接着第二个元素与第三个元素比较,也是大的在前小的在后......直到最后两个元素比较交换位置完。

  冒泡排序的代码如下:

 1 /**
 2  * 冒泡排序
 3  * @param arr
 4  * @param method 排序方式
 5  * @return
 6  */
 7 public static int[] bubble(int[] arr, Method method) {
 8     int[] a = Arrays.copyOf(arr, arr.length);
 9     if (method == Method.ASC) {   // 升序
10         for (int j = a.length; j > 1; j--) {
11             for (int i = 0; i < j - 1; i++) {
12                 /**
13                  * a[i] > a[i + 1]:该排序算法是稳定的
14                  * a[i] >= a[i + 1]:该排序算法是不稳定的
15                  */
16                 if (a[i] > a[i + 1]) {
17                     swap(a, i, i + 1);
18                 }
19             }
20         }
21     } else {   // 降序
22         for (int j = a.length; j > 1; j--) {
23             for (int i = 0; i < j - 1; i++) {
24                 /**
25                  * a[i] < a[i + 1]:该排序算法是稳定的
26                  * a[i] <= a[i + 1]:该排序算法是不稳定的
27                  */
28                 if (a[i] < a[i + 1]) {
29                     swap(a, i, i + 1);
30                 }
31             }
32         }
33     }
34     return a;
35 }
bubble

  例如:对序列(5, 8, 3, 4, 9, 2, 7, 6, 1)进行升序排序。

  第一趟冒泡排序的过程为:

  

  每一趟冒泡排序的结果为:

  [5, 8, 3, 4, 9, 2, 7, 6, 1] --> [5, 3, 4, 8, 2, 7, 6, 1, 9] --> [3, 4, 5, 2, 7, 6, 1, 8, 9] --> 

  [3, 4, 2, 5, 6, 1, 7, 8, 9] --> [3, 2, 4, 5, 1, 6, 7, 8, 9] --> [2, 3, 4, 1, 5, 6, 7, 8, 9] --> 

  [2, 3, 1, 4, 5, 6, 7, 8, 9] --> [2, 1, 3, 4, 5, 6, 7, 8, 9] --> [1, 2, 3, 4, 5, 6, 7, 8, 9]

快速排序

  快速排序是从序列中选定一个元素作为枢轴,将其他元素与枢轴进行比较,分为比枢轴小和比枢轴大两个子序列。快速排序算法是不稳定的。

  一趟升序的快速排序的过程为:定义两个指针low和high指向序列两端,并取low指向的元素作为枢轴。首先是high往前移,直到找到比枢轴小的数,将该数赋给low指向的位置;然后low往后移,直到找到比枢轴大的数,将该数赋给high指向的位置;接着又是high往前移......直到最后low和high指向同一个位置,将枢轴赋给该位置。

  一趟降序的快速排序的过程为:定义两个指针low和high指向序列两端,并取low指向的元素作为枢轴。首先是high往前移,直到找到比枢轴大的数,将该数赋给low指向的位置;然后low往后移,直到找到比枢轴小的数,将该数赋给high指向的位置;接着又是high往前移......直到最后low和high指向同一个位置,将枢轴赋给该位置。

  快速排序的代码如下:

 1 /**
 2  * 快速排序
 3  * @param arr
 4  * @param method  排序方式
 5  * @return
 6  */
 7 public static int[] quick(int[] arr, Method method) {
 8     int[] a = Arrays.copyOf(arr, arr.length);
 9     qSort(a, 0, a.length - 1, method);
10     return a;
11 }
12 
13 private static void qSort(int[] a, int low, int high, Method method) {
14     if (low < high) {
15         int pivotloc = partition(a, low, high, method);
16         qSort(a, low, pivotloc - 1, method);
17         qSort(a, pivotloc + 1, high, method);
18     }
19 }
20 
21 private static int partition(int[] a, int low, int high, Method method) {
22     int pivot = a[low];   // 取low位置的数作为枢轴
23     if (method == Method.ASC) {   // 升序
24         while (low < high) {
25             // 大数放右边
26             while (low < high && a[high] > pivot) high--;
27             a[low] = a[high];
28             // 小数放左边
29             while (low < high && a[low] < pivot) low++;
30             a[high] = a[low];
31         }
32     } else {   // 降序
33         while (low < high) {
34             // 小数放右边
35             while (low < high && a[high] < pivot) high--;
36             a[low] = a[high];
37             // 大数放左边
38             while (low < high && a[low] > pivot) low++;
39             a[high] = a[low];
40         }
41     }
42     a[low] = pivot;
43     return low;
44 }
quick

  例如:对序列(5, 8, 3, 4, 9, 2, 7, 6, 1)进行升序排序。

  第一趟快速排序的过程为:

  

  每一趟快速排序的结果为:

  [5, 8, 3, 4, 9, 2, 7, 6, 1] --> [1, 2, 3, 4, 5, 9, 7, 6, 8] --> [1, 2, 3, 4, 5, 9, 7, 6, 8] --> 

  [1, 2, 3, 4, 5, 9, 7, 6, 8] --> [1, 2, 3, 4, 5, 9, 7, 6, 8] --> [1, 2, 3, 4, 5, 8, 7, 6, 9] --> 

  [1, 2, 3, 4, 5, 6, 7, 8, 9]

选择排序

  选择排序是每次从序列中选择最值放在一端。

  一趟升序的选择排序的过程为:从序列中选择最大的元素放在最末端。

  一趟降序的选择排序的过程为:从序列中选择最小的元素放在最末端。

  选择排序的代码如下:

 1 /**
 2  * 选择排序
 3  * @param arr
 4  * @param method  排序方式
 5  * @return
 6  */
 7 public static int[] select(int[] arr, Method method) {
 8     int[] a = Arrays.copyOf(arr, arr.length);
 9     if (method == Method.ASC) {   // 升序
10         for (int i = a.length - 1; i > 0; i--) {
11             int index = getMaxIndex(a, i);
12             if (index != i) {
13                 swap(a, index, i);
14             }
15         }
16     } else {   // 降序
17         for (int i = a.length - 1; i > 0; i--) {
18             int index = getMinIndex(a, i);
19             if (index != i) {
20                 swap(a, index, i);
21             }
22         }
23     }
24     return a;
25 }
26 
27 private static int getMaxIndex(int[] arr, int end) {
28     int index = 0;
29     int num = arr[0];
30     for (int i = 1; i <= end; i++) {
31         /**
32          * num < arr[i]:该排序算法是稳定的
33          * num <= arr[i]:该排序算法是不稳定的
34          */
35         if (num < arr[i]) {
36             index = i;
37             num = arr[i];
38         }
39     }
40     return index;
41 }
42 
43 private static int getMinIndex(int[] arr, int end) {
44     int index = 0;
45     int num = arr[0];
46     for (int i = 1; i <= end; i++) {
47         /**
48          * num > arr[i]:该排序算法是稳定的
49          * num >= arr[i]:该排序算法是不稳定的
50          */
51         if (num > arr[i]) {
52             index = i;
53             num = arr[i];
54         }
55     }
56     return index;
57 }
select

  例如:对序列(5, 8, 3, 4, 9, 2, 7, 6, 1)进行升序排序。

  第一趟选择排序的过程为:

  

  每一趟选择排序的结果为:

  [5, 8, 3, 4, 9, 2, 7, 6, 1] --> [5, 8, 3, 4, 1, 2, 7, 6, 9] --> [5, 6, 3, 4, 1, 2, 7, 8, 9] --> 

  [5, 6, 3, 4, 1, 2, 7, 8, 9] --> [5, 2, 3, 4, 1, 6, 7, 8, 9] --> [1, 2, 3, 4, 5, 6, 7, 8, 9] --> 

  [1, 2, 3, 4, 5, 6, 7, 8, 9] --> [1, 2, 3, 4, 5, 6, 7, 8, 9] --> [1, 2, 3, 4, 5, 6, 7, 8, 9]

插入排序

  插入排序是找到第一个逆序的数,将该数插入到前面合适的位置。

  一趟升序的插入排序的过程为:找到第一个比其前一位小的数,将该数插在前面序列合适的位置。

  一趟降序的插入排序的过程为:找到第一个比其前一位大的数,将该数插在前面序列合适的位置。

  插入排序的代码如下:

 1 /**
 2  * 插入排序
 3  * @param arr
 4  * @param method  排序方式
 5  * @return
 6  */
 7 public static int[] insert(int[] arr, Method method) {
 8     int[] a = Arrays.copyOf(arr, arr.length);
 9     if (method == Method.ASC) {   // 升序
10         for (int i = 0; i < a.length - 1; i++) {
11             if (a[i + 1] < a[i]) {
12                 int guard = a[i + 1];
13                 a[i + 1] = a[i];
14                 int j = i;
15                 /**
16                  * guard < a[j - 1]:该排序算法是稳定的
17                  * guard <= a[j - 1]:该排序算法是不稳定的
18                  */
19                 while (j > 0 && guard < a[j - 1]) {
20                     a[j] = a[j - 1];
21                     j--;
22                 }
23                 a[j] = guard;
24             }
25         }
26     } else {   // 降序
27         for (int i = 0; i < a.length - 1; i++) {
28             if (a[i + 1] > a[i]) {
29                 int guard = a[i + 1];
30                 a[i + 1] = a[i];
31                 int j = i;
32                 /**
33                  * guard > a[j - 1]:该排序算法是稳定的
34                  * guard >= a[j - 1]:该排序算法是不稳定的
35                  */
36                 while (j > 0 && guard > a[j - 1]) {
37                     a[j] = a[j - 1];
38                     j--;
39                 }
40                 a[j] = guard;
41             }
42         }
43     }
44     return a;
45 }
insert

  例如:对序列(5, 8, 3, 4, 9, 2, 7, 6, 1)进行升序排序。

  第一趟插入排序的过程为:

  

  每一趟插入排序的结果为:

  [5, 8, 3, 4, 9, 2, 7, 6, 1] --> [3, 5, 8, 4, 9, 2, 7, 6, 1] --> [3, 4, 5, 8, 9, 2, 7, 6, 1] --> 

  [2, 3, 4, 5, 8, 9, 7, 6, 1] --> [2, 3, 4, 5, 7, 8, 9, 6, 1] --> [2, 3, 4, 5, 6, 7, 8, 9, 1] --> 

  [1, 2, 3, 4, 5, 6, 7, 8, 9]

归并排序

  归并排序是指把序列递归分解成若干个长度大致相等的子序列,然后将各个子序列合并,并在合并时进行排序,最终合并为整体有序序列的过程。

  归并排序的代码如下:

 1 /**
 2  * 归并排序
 3  * @param arr
 4  * @param method
 5  * @return  排序方式
 6  */
 7 public static int[] merge(int[] arr, Method method) {
 8     int[] a = Arrays.copyOf(arr, arr.length);
 9     merge1(a, 0, a.length - 1, method);
10     return a;
11 }
12 
13 private static void merge1(int[] arr, int begin, int end, Method method) {
14     if (begin == end) return;
15     int m = (begin + end + 1) / 2;
16     merge1(arr, begin, m - 1, method);
17     merge1(arr, m, end, method);
18     merge2(arr, begin, m, end, method);
19 }
20 
21 private static void merge2(int[] arr, int begin, int m, int end, Method method) {
22     int[] a = new int[end + 1 - begin];
23     int i = begin, j = m, k = 0;
24     if (method == Method.ASC) {
25         while (i < m && j <= end) {
26             /**
27              * arr[i] <= arr[j]:该排序算法是稳定的
28              * arr[i] < arr[j]:该排序算法是不稳定的
29              */
30             if (arr[i] <= arr[j]) {
31                 a[k++] = arr[i++];
32             } else {
33                 a[k++] = arr[j++];
34             }
35         }
36     } else {
37         while (i < m && j <= end) {
38             /**
39              * arr[i] >= arr[j]:该排序算法是稳定的
40              * arr[i] > arr[j]:该排序算法是不稳定的
41              */
42             if (arr[i] >= arr[j]) {
43                 a[k++] = arr[i++];
44             } else {
45                 a[k++] = arr[j++];
46             }
47         }
48     }
49     while (i < m) a[k++] = arr[i++];
50     while (j <= end) a[k++] = arr[j++];
51     System.arraycopy(a, 0, arr, begin, a.length);
52 }
merge

  例如:对序列(5, 8, 3, 4, 9, 2, 7, 6, 1)进行升序排序。

  归并排序的过程为:

  

 

堆排序

  堆排序是利用堆的特性进行排序的一种排序方法:

    大顶堆:堆顶是最大的元素,堆顶的两个子结点中较大的是第二大的元素。

    小顶堆:堆顶是最小的元素,堆顶的两个子结点中较小的是第二小的元素。

  堆排序算法是不稳定的。

  一趟升序的堆排序的过程为:将大顶堆堆顶的两个子结点中较大的结点与堆尾的前一个结点交换,将堆顶与堆尾交换,然后移除最后两个结点,最后对交换过的结点重新调整为大顶堆。

  一趟降序的堆排序的过程为:将小顶堆堆顶的两个子结点中较小的结点与堆尾的前一个结点交换,将堆顶与堆尾交换,然后移除最后两个结点,最后对交换过的结点重新调整为小顶堆。

  堆排序的代码如下:

  1 /**
  2  * 堆排序
  3  * @param arr
  4  * @param method  排序方式
  5  * @return
  6  */
  7 public static int[] heap(int[] arr, Method method) {
  8     int[] a = makeHeap(arr, method);
  9     for (int i = a.length; i > 1; i -= 2) {
 10         heap(a, i, method);
 11     }
 12     return a;
 13 }
 14 
 15 @SuppressWarnings("preview")
 16 private static int[] makeHeap(int[] arr, Method method) {
 17     int[] heap = new int[arr.length];
 18     heap[0] = arr[0];
 19     for (int i = 1; i < arr.length; i++) {
 20         int index = i;
 21         for (int parent = getParent(index); parent >= 0; parent = getParent(parent)) {
 22             switch (method) {
 23                 case ASC -> {
 24                     if (arr[i] > heap[parent]) {
 25                         heap[index] = heap[parent];
 26                         index = parent;
 27                     }
 28                 }
 29                 case DESC -> {
 30                     if (arr[i] < heap[parent]) {
 31                         heap[index] = heap[parent];
 32                         index = parent;
 33                     }
 34                 }
 35             }
 36         }
 37         heap[index] = arr[i];
 38     }
 39     return heap;
 40 }
 41 
 42 private static int getParent(int index) {
 43     return index % 2 == 1 ? (index - 1) / 2 : index / 2 - 1;
 44 }
 45 
 46 @SuppressWarnings("preview")
 47 private static void heap(int[] arr, int end, Method method) {
 48     if (end > 2) {
 49         int index = 0;
 50         switch (method) {
 51             case ASC -> {
 52                 index = arr[1] > arr[2] ? 1 : 2;
 53             }
 54             case DESC -> {
 55                 index = arr[1] < arr[2] ? 1 : 2;
 56             }
 57         }
 58         swap(arr, index, end - 2);
 59         swap(arr, 0, end - 1);
 60         int[] heap = Arrays.copyOf(arr, end - 2);
 61         adjust(heap, index, method);
 62         adjust(heap, 0, method);
 63         System.arraycopy(heap, 0, arr, 0, heap.length);
 64     } else {
 65         switch (method) {
 66             case ASC -> {
 67                 if (arr[0] > arr[1]) swap(arr, 0, 1);
 68             }
 69             case DESC -> {
 70                 if (arr[0] < arr[1]) swap(arr, 0, 1);
 71             }
 72         }
 73     }
 74 }
 75 
 76 @SuppressWarnings("preview")
 77 private static void adjust(int[] heap, int index, Method method) {
 78     switch (method) {
 79         case ASC -> {
 80             if (index < heap.length / 2) {
 81                 int lchild = index * 2 + 1;
 82                 int rchild = (index + 1) * 2;
 83                 if (rchild >= heap.length || heap[lchild] > heap[rchild]) {
 84                     if (heap[index] < heap[lchild]) {
 85                         swap(heap, index, lchild);
 86                         adjust(heap, lchild, method);
 87                     }
 88                 } else {
 89                     if (heap[index] < heap[rchild]) {
 90                         swap(heap, index, rchild);
 91                         adjust(heap, rchild, method);
 92                     }
 93                 }
 94             }
 95         }
 96         case DESC -> {
 97             if (index < heap.length / 2) {
 98                 int lchild = index * 2 + 1;
 99                 int rchild = (index + 1) * 2;
100                 if (rchild >= heap.length || heap[lchild] < heap[rchild]) {
101                     if (heap[index] > heap[lchild]) {
102                         swap(heap, index, lchild);
103                         adjust(heap, lchild, method);
104                     }
105                 } else {
106                     if (heap[index] > heap[rchild]) {
107                         swap(heap, index, rchild);
108                         adjust(heap, rchild, method);
109                     }
110                 }
111             }
112         }
113     }
114 }
heap

  例如:对序列(5, 8, 3, 4, 9, 2, 7, 6, 1)进行升序排序。

  生成大顶堆数组:

  

  第一趟堆排序的过程为:

  

  每一趟堆排序的结果为:

  [5, 8, 3, 4, 9, 2, 7, 6, 1] --> [9, 8, 7, 6, 5, 2, 3, 4, 1] --> [7, 6, 3, 4, 5, 2, 1, 8, 9] --> 

  [5, 4, 3, 1, 2, 6, 7, 8, 9] --> [3, 1, 2, 4, 5, 6, 7, 8, 9] --> [1, 2, 3, 4, 5, 6, 7, 8, 9]

转载于:https://www.cnblogs.com/lqkStudy/p/11300106.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值