前言十大排序

下面我们用2个小时时间过下10个排序。 算法-入门篇之十大排序 - 图1image.png

一 冒泡排序

冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。
原理:每次比较前后两个值

  1. 比较相邻元素。如果前一个元素比后一个元素大,则交换这两个元素位置。
  2. 对每一对元素重复操作,直至比较完成。

image.png

1.1 代码实现

  1. /**
  2. * 冒泡排序
  3. * @param array
  4. * @return
  5. */
  6. public static int[] bubbleSort(int[] array) {
  7. int len = arr.length;
  8. //从0开始,依次向后遍历,每次把最大的移动到最后
  9. for (int i = 0; i < len; i++) {
  10. //出勤最后的大值,比较前面的乱序
  11. for (int j = 0; j < len - i - 1; j++) {
  12. if (arr[j] > arr[j + 1]) {
  13. int temp = arr[j];
  14. arr[j] = arr[j + 1];
  15. arr[j + 1] = temp;
  16. }
  17. }
  18. }
  19. return arr;
  20. }
  21. public static void main(String[] args) {
  22. System.out.println(JSONObject.toJSONString(SortTest.bubbleSort(new int[]{6, 5, 4, 3, 2, 1})));
  23. }

1.2 总结

  • 最佳情况:T(n) = O(n)
  • 最差情况:T(n) = O(n2)
  • 平均情况:T(n) = O(n2)

    二 选择排序

    选择排序(Selection-sort)是一种简单直观的排序算法。
    原理:每次获取最小值
  1. 每次遍历,都假定第一个索引处元素是最小值,然后和其他索引处值依次进行比较,如果当前索引处值大于其他某个索引处值,那么其他索引处值为最小值,交换第一个索引处和最小值所在索引处值。
  2. 以此类推,最后可以找到最小值所在索引。

image.png

2.1 代码实现

  1. /**
  2. * 选择排序
  3. *
  4. * @param array
  5. * @return
  6. */
  7. public static int[] selectionSort(int[] arr) {
  8. int len = arr.length;
  9. //从0开始,假设第一个数最小,依次向后假设
  10. for (int i = 0; i < len - 1; i++) {
  11. int minIndex = i;
  12. //每次与最小的值比较后面的数据,有最小的向前提取。
  13. for (int j = i; j < len; j++) {
  14. if (arr[j] < arr[minIndex]) {
  15. minIndex = j;
  16. }
  17. }
  18. int temp = arr[i];
  19. arr[i] = arr[minIndex];
  20. arr[minIndex] = temp;
  21. }
  22. return arr;
  23. }
  24. public static void main(String[] args) {
  25. System.out.println(JSONObject.toJSONString(SortTest.selectionSort(new int[]{6, 5, 4, 3, 2, 1})));
  26. }

2.2 总结

  • 最佳情况:T(n) = O(n2)
  • 最差情况:T(n) = O(n2)
  • 平均情况:T(n) = O(n2)

    三 插入排序

    插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。
    原理:
  1. 把所有的元素分为两组,已经排序的和未排序的;
  2. 找到未排序组中第一个元素,向已经排序组中进行插入;

image.png

3.1 代码实现

  1. /**
  2. * 插入排序
  3. *
  4. * @param array
  5. * @return
  6. */
  7. public static int[] insertionSort(int[] arr) {
  8. int len = arr.length;
  9. //从一开始,每次新插一个
  10. for (int i = 1; i < len; i++) {
  11. //对新增后的分组,重新排序
  12. for (int j = i; j > 0; j--) {
  13. if (arr[j - 1] > arr[j]) {
  14. int temp = arr[j];
  15. arr[j] = arr[j - 1];
  16. arr[j - 1] = temp;
  17. }
  18. }
  19. }
  20. return arr;
  21. }
  22. public static void main(String[] args) {
  23. System.out.println(JSONObject.toJSONString(SortTest.insertionSort(new int[]{6, 5, 4, 3, 2, 1})));
  24. }

3.2 总结

  • 最佳情况:T(n) = O(n)
  • 最坏情况:T(n) = O(n2)
  • 平均情况:T(n) = O(n2)

    四 希尔排序

    1959年Shell发明,第一个突破O(n)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序**
    原理:**
  1. 选定一个增长量h,按照增长量h作为数据分组的依据,对数据进行分组;
  2. 对分好组的每一组数据完成插入排序;
  3. 减小增长量,最小减为1,重复第二步操作。

image.png
增长量h的确定:增长量h的值每一固定的规则,我们这里采用以下规则:

  1. int h=1
  2. while(h<5){
  3. h=2h+1//3,7
  4. }
  5. //循环结束后我们就可以确定h的最大值;
  6. h的减小规则为:
  7. h=h/2

4.1 代码实现

  1. package com.ycc.data.sort;
  2. import com.alibaba.fastjson.JSONObject;
  3. /**
  4. * 希尔排序
  5. *
  6. * @author hezhaoming
  7. * @date 2020/10/19
  8. */
  9. public class ShellSort {
  10. public static void main(String[] args) {
  11. int[] arr = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
  12. int len = arr.length;
  13. int gap = len / 2;
  14. int temp;
  15. while (gap > 0) {
  16. //找到每一个待插入元素 加入gap=4,i=4,gap=4,i=5,gap=4,i=6
  17. for (int i = gap; i < len; i++) {
  18. //把待插入的元素插入到有序数列中 比较 0-5,1-6,2-7
  19. for (int j = i; j >= gap; j -= gap) {
  20. if (arr[j - gap] > arr[j]) {
  21. temp = arr[j];
  22. arr[j] = arr[j - gap];
  23. arr[j - gap] = temp;
  24. } else {
  25. break;
  26. }
  27. }
  28. }
  29. gap = gap / 2;
  30. }
  31. System.out.println(JSONObject.toJSONString(arr));
  32. }
  33. }

4.2 总结

  • 最佳情况:T(n) = O(nlog2 n)
  • 最坏情况:T(n) = O(nlog2 n)
  • 平均情况:T(n) = O(nlog2n)

    五 归并排序

    是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。
    原理:
  1. 尽可能将数据拆分成两个元素相等子组,并对每个子组继续拆分,直到每个子组元素个数是1为止。
  2. 将相邻的两个子组进行合并成一个有序的大组;
  3. 不断的重复步骤2,直到最终只有一个组为止。

image.png

5.1 代码实现

  1. package com.ycc.data.sort;
  2. import com.alibaba.fastjson.JSONObject;
  3. /**
  4. * 归并排序
  5. *
  6. * @author 何兆明
  7. * @description 如果要排序一个数组,我们先把数组从中间分成前后两部分,然后分别对前后两部分进行排序,再将排好序的两部分数据合并在一起就可以了
  8. * @create 2019-04-09 11:33
  9. */
  10. public class MergeSort {
  11. public static void main(String[] args) {
  12. int[] arr = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 2, 4};
  13. int len = arr.length;
  14. int[] temp = new int[arr.length];//在排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
  15. sort(arr, 0, arr.length - 1, temp);
  16. System.out.println(JSONObject.toJSONString(arr));
  17. }
  18. private static void sort(int[] arr, int left, int right, int[] temp) {
  19. if (left < right) {
  20. int mid = (left + right) / 2;
  21. sort(arr, left, mid, temp);//左边归并排序,使得左子序列有序
  22. sort(arr, mid + 1, right, temp);//右边归并排序,使得右子序列有序
  23. merge(arr, left, mid, right, temp);//将两个有序子数组合并操作
  24. }
  25. }
  26. private static void merge(int[] arr, int left, int mid, int right, int[] temp) {
  27. int li = left;//左序列指针
  28. int ri = mid + 1;//右序列指针
  29. int t = 0;//临时数组指针
  30. while (li <= mid && ri <= right) {
  31. //左边小于右边
  32. if (arr[li] <= arr[ri]) {
  33. temp[t++] = arr[li++];
  34. } else {
  35. temp[t++] = arr[ri++];
  36. }
  37. }
  38. //左边提前结束,将左边剩余元素填充进temp中
  39. while (li <= mid) {
  40. temp[t++] = arr[li++];
  41. }
  42. //将右序列剩余元素填充进temp中
  43. while (ri <= right) {
  44. temp[t++] = arr[ri++];
  45. }
  46. t = 0;
  47. //将temp中的元素全部拷贝到原数组中
  48. while (left <= right) {
  49. arr[left++] = temp[t++];
  50. }
  51. }
  52. }

5.2 总结

  • 最佳情况:T(n) = O(n)
  • 最差情况:T(n) = O(nlogn)
  • 平均情况:T(n) = O(nlogn)

    六 快速排序

    快速排序是对冒泡排序的一种改进。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
    原理:
  1. 首先设定一个分界值,通过该分界值将数组分成左右两部分。
  2. 将大于或等于分界值的数据放到到数组右边,小于分界值的数据放到数组的左边。此时左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
  3. 然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
  4. 重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左侧和右侧两个部分的数据排完序后,整个数组的排序也就完成了。

image.png

6.1 代码实现

  1. package com.ycc.power;
  2. import com.alibaba.fastjson.JSONObject;
  3. /**
  4. * 快速排序
  5. * @author 何兆明
  6. * @description
  7. * @create 2019-04-11 17:27
  8. */
  9. public class QuickSort {
  10. public static void quickSort(int[] arr, int low, int high) {
  11. int left, right, temp, t;
  12. if (low > high) {
  13. return;
  14. }
  15. left = low;
  16. right = high;
  17. //temp就是基准位
  18. temp = arr[low];
  19. while (left < right) {
  20. //先看右边,依次往左递减
  21. while (temp <= arr[right] && left < right) {
  22. right--;
  23. }
  24. //再看左边,依次往右递增
  25. while (temp >= arr[left] && left < right) {
  26. left++;
  27. }
  28. //如果满足条件则交换
  29. if (left < right) {
  30. t = arr[right];
  31. arr[right] = arr[left];
  32. arr[left] = t;
  33. }
  34. }
  35. //最后将基准为与i和j相等位置的数字交换
  36. arr[low] = arr[left];
  37. arr[left] = temp;
  38. //递归调用左半数组
  39. quickSort(arr, low, right - 1);
  40. //递归调用右半数组
  41. quickSort(arr, right + 1, high);
  42. }
  43. public static void main(String[] args) {
  44. int[] arr = {10, 7, 2, 4, 7, 62, 3, 4, 2, 1, 8, 9, 19};
  45. quickSort(arr, 0, arr.length - 1);
  46. System.out.println(JSONObject.toJSONString(arr));
  47. }
  48. }

6.2 总结

  • 最佳情况:T(n) = O(nlogn)
  • 最差情况:T(n) = O(n2)
  • 平均情况:T(n) = O(nlogn)

    七 堆排序

    堆排序(Heapsort) 是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

    7.1 代码实现

    ```java package com.ycc.power;

import com.alibaba.fastjson.JSONObject;

/**

  • @author 何兆明
  • @description 堆就是一个完全二叉树;最大堆根节点大于子节点,最小堆根节点小于子节点
  • @create 2019-04-10 15:12 */ public class HeapSort {

    // 构建堆 public static void buildMaxHeap(int[] array) {

    1. if (array == null || array.length == 1)
    2. return;
    3. // 堆的公式就是 int root = 2*i, int left = 2*i+1, int right = 2*i+2;
    4. int cursor = array.length / 2;
    5. for (int i = cursor; i >= 0; i--) { // 这样for循环下,就可以第一次排序完成

    // maxHeap(array, array.length, i);

    1. minHeap(array, array.length, i);
    2. }

    }

    // 最大堆 public static void maxHeap(int[] array, int heapSieze, int index) {

    1. int left = index * 2 + 1; // 左子节点
    2. int right = index * 2 + 2; // 右子节点
    3. int maxValue = index; // 暂时定在Index的位置就是最大值
    4. // 如果左子节点的值,比当前最大的值大,就把最大值的位置换成左子节点的位置
    5. if (left < heapSieze && array[left] > array[maxValue]) {
    6. maxValue = left;
    7. }
    8. // 如果右子节点的值,比当前最大的值大,就把最大值的位置换成右子节点的位置
    9. if (right < heapSieze && array[right] > array[maxValue]) {
    10. maxValue = right;
    11. }
    12. // 如果不相等,说明啊,这个子节点的值有比自己大的,位置发生了交换了位置
    13. if (maxValue != index) {
    14. swap(array, index, maxValue); // 就要交换位置元素
    15. // 交换完位置后还需要判断子节点是否打破了最大堆的性质。最大堆性质:两个子节点都比父节点小。
    16. maxHeap(array, heapSieze, maxValue);
    17. }

    }

    // 最小堆 public static void minHeap(int[] array, int heapSieze, int index) {

    1. int left = index * 2 + 1; // 左子节点
    2. int right = index * 2 + 2; // 右子节点
    3. int maxValue = index; // 暂时定在Index的位置就是最小值
    4. // 如果左子节点的值,比当前最小的值小,就把最小值的位置换成左子节点的位置
    5. if (left < heapSieze && array[left] < array[maxValue]) {
    6. maxValue = left;
    7. }
    8. // 如果右子节点的值,比当前最小的值小,就把最小值的位置换成左子节点的位置
    9. if (right < heapSieze && array[right] < array[maxValue]) {
    10. maxValue = right;
    11. }
    12. // 如果不相等,说明啊,这个子节点的值有比自己小的,位置发生了交换了位置
    13. if (maxValue != index) {
    14. swap(array, index, maxValue); // 就要交换位置元素
    15. // 交换完位置后还需要判断子节点是否打破了最小堆的性质。最小性质:两个子节点都比父节点大。
    16. minHeap(array, heapSieze, maxValue);
    17. }

    }

    // 数组元素交换 public static void swap(int[] array, int index1, int index2) {

    1. int temp = array[index1];
    2. array[index1] = array[index2];
    3. array[index2] = temp;

    }

    public static void heapSort(int[] array) {

    1. if (array == null || array.length == 1)
    2. return;
    3. buildMaxHeap(array); // 第一次排序,构建最大堆,只保证了堆顶元素是数组里最大的
    4. for (int i = array.length - 1; i >= 1; i--) {
    5. // 这个是什么意思呢?,经过上面的一些列操作,目前array[0]是当前数组里最大的元素,需要和末尾的元素交换
    6. // 然后,拿出最大的元素
    7. swap(array, 0, i);
    8. // 交换完后,下次遍历的时候,就应该跳过最后一个元素,也就是最大的那个值,然后开始重新构建最大堆
    9. // 堆的大小就减去1,然后从0的位置开始最大堆

    // maxHeap(array, i, 0);

    1. minHeap(array, i, 0);
    2. }

    }

  1. public static void main(String[] args) {
  2. int[] array = {19, 38, 7, 36, 5, 5, 3, 2, 1, 0, 56};
  3. System.out.println("排序前:" + JSONObject.toJSONString(array));
  4. System.out.println("分割线---------------");
  5. heapSort(array);
  6. System.out.println("排序后:" + JSONObject.toJSONString(array));
  7. }

}

```

7.2 总结

八 计数排序

适合整数排序,原理 统计每个数出现的个数
image.png

8.1 代码实现

8.2 总结

计数排序是一个稳定的排序算法。当输入的元素是 n 个 0到 k 之间的整数时,时间复杂度是O(n+k),空间复杂度也是O(n+k),其排序速度快于任何比较排序算法。当k不是很大并且序列比较集中时,计数排序是一个很有效的排序算法。

九 桶排序(Bucket Sort)

image.png

9.1 代码实现

9.2 总结

十 基数排序(Radix Sort)

image.png

10.1 代码实现

10.2 总结