排序:就是将一组无序的记录序列调整为有序的记录序列。
列表排序:将无序列表变为有序列表

  • 输入:列表
  • 输出:列表

排序方式:升序和降序
内置排序函数:sort
常见排序算法:
image.png

lOW B三人组

  • 原地排序
  • 时间复杂度都是O(n^2)

    冒泡排序

  • 列表每两个相邻的数,如果前面比后面大,则交换这两个数(升序排列)

  • 一趟排序完成之后,则无序区减少一个数,有序区增加一个数。

image.pngimage.png
从下往上,5和7,5 < 7 交换 第二个数是7,则 7和 4比,7大,继续往上交换
重复上面过程,也就是一趟下来,大的数子冒泡上去。
每一趟,最大的数会渐渐冒泡上去。
总共排序算法走了N-1趟。每一趟减少一个数的有序性。

  • 代码的关键点:趟、有序区和无序区 ```java package com.algorithm.demo.sorts;

import com.alibaba.fastjson.JSONObject;

/**

  • @Author leijs
  • @date 2022/4/9 */ public class BubbleSort { public static void main(String[] args) {
    1. int[] array = new int[] {7,2,5,4,1,9,17,11, 12,3};
    2. sort(array);
    3. System.out.println("final result:" + JSONObject.toJSONString(array));
    } public static void sort(int[] array) {
    1. // 第i趟
    2. for (int i = 0; i < array.length; i++) {
    3. // 每一趟少1,一共是n-1趟
    4. boolean exchange = false;
    5. for (int j = 0; j < array.length -i - 1; j++) {
    6. if (array[j] > array[j+1]) {
    7. int temp = array[j];
    8. array[j] = array[j+1];
    9. array[j+1] = temp;
    10. exchange = true;
    11. }
    12. }
    13. if (!exchange) {
    14. return;
    15. }
    16. System.out.println(JSONObject.toJSONString(array));
    17. }
    } }
  1. - 时间复杂度:O(n^2)
  2. 注意改进的地方:
  3. > 如果没有可以交换的,说明已经不需要排序了。
  4. <a name="eZZp7"></a>
  5. ## 选择排序
  6. 遍历一遍找到最小的数,再遍历一遍找到一个最小的数,拿出来放到一个新的列表
  7. ```java
  8. package com.algorithm.demo.sorts;
  9. import com.alibaba.fastjson.JSONObject;
  10. import com.google.common.collect.Lists;
  11. import java.util.List;
  12. /**
  13. * @Author leijs
  14. * @date 2022/4/9
  15. */
  16. public class SelectSort {
  17. public static void main(String[] args) {
  18. List<Integer> array = Lists.newArrayList(7,2,5,4,1,9,17,11, 12,3, 9);
  19. System.out.println("final result:" + JSONObject.toJSONString(sort(array)));
  20. }
  21. public static List<Integer> sort(List<Integer> array) {
  22. List<Integer> newList = Lists.newArrayList();
  23. int totalSize = array.size();
  24. for (int i = 0; i < totalSize; i++) {
  25. int minNum = getMinNum(array);
  26. newList.add(minNum);
  27. }
  28. return newList;
  29. }
  30. public static int getMinNum(List<Integer> array) {
  31. int size = array.size();
  32. int minIndex = 0;
  33. int minNum = array.get(0);
  34. for (int i = 1; i < size; i++) {
  35. if (array.get(i) < minNum) {
  36. minIndex = i;
  37. minNum = array.get(i);
  38. }
  39. }
  40. array.remove(minIndex);
  41. System.out.println("remove result:" + JSONObject.toJSONString(array));
  42. return minNum;
  43. }
  44. }

缺点:

  • 选择排序生成了一个新的列表,会占用更多的内存
  • 冒泡排序是原地排序

时间复杂度:O(n^2)

选择排序优化

  • 每一次遍历,找到最小的数和其下标,然后和首位交换
  • 这样首位及之后位置会随着这种和最小的交换变得有序
  • 然后我们重复关心无序区域的数据 ```java package com.algorithm.demo.sorts;

import com.alibaba.fastjson.JSONObject;

/**

  • @Author leijs
  • @date 2022/4/9 */ public class SelectSortV2 { public static void main(String[] args) {

    1. int[] array = new int[] {7,2,5,4,1,9,17,11, 12,3};
    2. sort(array);
    3. System.out.println("final result:" + JSONObject.toJSONString(array));

    }

    public static void sort(int[] array) {

    1. for (int i = 0; i < array.length; i++) {
    2. int cur = array[i];
    3. int minIndex = i;
    4. for (int j = i+ 1; j < array.length; j++) {
    5. if (cur > array[j]) {
    6. cur = array[j];
    7. minIndex = j;
    8. }
    9. }
    10. // 交换顺序
    11. int temp = array[i];
    12. array[i] = array[minIndex];
    13. array[minIndex] = temp;
    14. System.out.println("sort result:" + JSONObject.toJSONString(array));
    15. }

    } }

  1. 注意有序区和无序区的位置,注意记录最小数的位置。复杂度O(n^2)
  2. <a name="nG2za"></a>
  3. ## 插入排序
  4. 类似于玩牌的时候,摸牌并插入到有牌的正确位置。
  5. ```java
  6. package com.algorithm.demo.sorts;
  7. import com.alibaba.fastjson.JSONObject;
  8. /**
  9. * @Author leijs
  10. * @date 2022/4/9
  11. */
  12. public class InsertSort {
  13. public static void main(String[] args) {
  14. int[] array = new int[]{7, 2, 5, 4, 1, 9, 17, 11, 12, 3};
  15. sort(array);
  16. System.out.println("final result:" + JSONObject.toJSONString(array));
  17. }
  18. public static void sort(int[] array) {
  19. // 第i趟
  20. for (int i = 1; i < array.length; i++) {
  21. // j指的是手里的牌
  22. int j = i - 1;
  23. // 摸到的牌
  24. int temp = array[i];
  25. if (temp > array[j]) {
  26. continue;
  27. }
  28. while (j >= 0 && temp < array[j]) {
  29. array[j + 1] = array[j];
  30. j -= 1;
  31. }
  32. array[j + 1] = temp;
  33. System.out.println("sort result:" + JSONObject.toJSONString(array));
  34. }
  35. }
  36. }

image.png
时间复杂度:O(n^2)

牛B三人组

快速排序

快速排序的特点:

  1. 取一个元素P(第一个元素),使元素P归位
  2. 列表被P分为两个部分,左边都比P小,右边都比P大。
  3. 递归完成排序。

image.png
从右边找比5小的,从左边找比5大的。

快速排序的框架

image.png

  1. package com.algorithm.demo.sorts;
  2. import com.alibaba.fastjson.JSONObject;
  3. /**
  4. * @Author leijs
  5. * @date 2022/4/9
  6. */
  7. public class QuickSort {
  8. public static void main(String[] args) {
  9. int[] array = new int[] {7,2,5,4,1,9,17,11, 12,3};
  10. sort(array, 0, array.length - 1);
  11. System.out.println("final result:" + JSONObject.toJSONString(array));
  12. }
  13. public static void sort(int[] array, int left, int right) {
  14. if (left < right) {
  15. int mid = partition(array, left, right);
  16. sort(array, left, mid -1);
  17. sort(array, mid + 1, right);
  18. }
  19. }
  20. public static int partition(int[] array, int left, int right) {
  21. // 把第一个元素存起来
  22. int temp = array[left];
  23. while (left < right) {
  24. // 从右边找一个比5小的数,因为左边有空位
  25. while (left < right && array[right] >= temp) {
  26. // 只要比temp小的,往左走一
  27. right -= 1;
  28. }
  29. // 把右边的值写到昨天空位上
  30. array[left] = array[right];
  31. // 接下来就要从左边找比5大的数
  32. while (left < right && array[left] <= temp) {
  33. left += 1;
  34. }
  35. // 把左边的值写到右边的空位上
  36. array[right] = array[left];
  37. }
  38. // temp写在left和right碰头的位置,把temp归位
  39. array[left] = temp;
  40. return left;
  41. }
  42. }

快速排序的复杂度:
image.png
partition,每一层都O(n)的复杂度,整个复杂度就是:O(nlog(n))

快排的问题

  • 最坏情况

    倒序列表的排序:9 8 7 6 5 4 3 2 1 排序成 1 2 3 4 5 6 7 8 9 每次只少一个数,并非少一半,最坏复杂度就是O(n^2)

  • 递归的深度

最坏情况的解决:随机找一个数,和第一个数交换,然后逻辑再同之前。

堆排序

前传-树与二叉树

image.png
树的基本概念:

  • 根节点、叶子节点

    叶子节点:不能分叉的节点,没有孩子

  • 树的深度(高度)

    上图4层

  • 树的度

    节点的度:对于E节点,度是2,分了两个叉7,F节点是3 树的度最大节点分的叉,A分了6个叉,所以这个树的度就是6.

  • 孩子节点/父节点

    节点时间的关系。E是I的父节点,I是E的孩子节点

  • 子树

    E I J P Q 就属于子树,,一个树的分叉一起拿出来。

二叉树

度不超过2的树,
image.png

  • 完全二叉树:

image.png

完全二叉树就是满二叉树叶子节点右边拿到一些叶子节点。

  • 二叉树的存储方式
  • 链式存储方式
  • 顺序存储方式(重点):简单用列表来存储

下面这是一个完全二叉树:
image.png
首先9的编号是0,其左子节点8的编号是1
父节点8的编号是1,左子节点6的编号是3
image.png

什么是堆

堆:是一种特殊的完全二叉树

  • 大根堆:一颗完全二叉树,满足任一节点都比其孩子节点大
  • 小根堆:一颗完全二叉树,满足任一节点都比其孩子节点小。

    image.png

    堆的向下调整性质

    假设根节点的左右子树是堆,但根节点不满足堆的性质 可以通过一次向下调整来将其变成一个堆。

image.png
调整后:
image.png

堆排序过程

image.png
挨个出树,9走了,然后把3放上去:
image.png
然后向下调整。之后重复这个过程。

怎么建造一个堆?

首先需要下级先有序。农村包围城市。看最后一个非叶子节点。
image.png

  • 3个5调整。

image.png

  • 7和1调整

重复以上调整过程。

  1. package com.algorithm.demo.sorts;
  2. import java.util.Arrays;
  3. /**
  4. * @Author leijs
  5. * @date 2022/4/9
  6. */
  7. public class HeapSortV2 {
  8. public static void main(String[] args) {
  9. int[] a = {-1, 9, 0, 6, 5, 8, 2, 1, 7, 4, 3};
  10. System.out.println(Arrays.toString(a));
  11. HeapSortV2.sort(a);
  12. System.out.println(Arrays.toString(a));
  13. }
  14. public static void sort(int[] a) {
  15. //s[0]不用,实际元素数量和最后一个元素的角标都为N
  16. int N = a.length - 1;
  17. //构造堆
  18. //如果给两个已构造好的堆添加一个共同父节点,
  19. //将新添加的节点作一次下沉将构造一个新堆,
  20. //由于叶子节点都可看作一个构造好的堆,所以
  21. //可以从最后一个非叶子节点开始下沉,直至
  22. //根节点,最后一个非叶子节点是最后一个叶子
  23. //节点的父节点,角标为N/2
  24. for (int k = N / 2; k >= 1; k--) {
  25. sink(a, k, N);
  26. }
  27. //下沉排序
  28. while (N > 1) {
  29. swap(a, 1, N--); //将大的放在数组后面,升序排序
  30. sink(a, 1, N);
  31. }
  32. }
  33. //下沉(由上至下的堆有序化)
  34. private static void sink(int[] a, int k, int N) {
  35. while (true) {
  36. int i = 2 * k;
  37. if (i > N) { //保证该节点是非叶子节点
  38. break;
  39. }
  40. if (i < N && a[i + 1] > a[i]) { //选择较大的子节点
  41. i++;
  42. }
  43. if (a[k] >= a[i]) { //没下沉到底就构造好堆了
  44. break;
  45. }
  46. swap(a, k, i);
  47. k = i;
  48. }
  49. }
  50. private static void swap(int[] a, int i, int j) {
  51. int t = a[i];
  52. a[i] = a[j];
  53. a[j] = t;
  54. }
  55. }

时间复杂度

image.png
sift是一个折半的函数。
时间复杂度:O(nlogn)

问题:topK问题

现在有n个数,设计算法得到前K个大的数(k < n)
解决思路:

  • 方法一:排序后切片,时间复杂度:O(nlogn)
  • 方法二:排序LowB 三人组

    冒泡排序排K次。O(kn)

  • 方法三:堆排序,复杂度 O(nlogk)

    取列表前K个建立一个小根堆,堆顶就是目前第K大的数 依次向后遍历原列表,对于列表中的元素,如果小于堆顶,则忽略该元素,如果大于堆顶,则将该堆顶替换为该元素,并且对堆进行一次排序。 遍历完所有元素后,倒序倒出堆顶。

归并排序

image.png

  • 分解:将列表越分越小,直至分成一个元素。
  • 终止条件:一个元素是有序的。
  • 合并:将两个有序列表归并,列表越来越大。

两端,2 和 1比较,发现1小,放入第一位,然后第二段指针往右; 2 和 3 比,发现是2,则第一段指针往左挪 接下来:5和3比。重复此过程。

image.png
image.png

image.png

  1. package com.algorithm.demo.sorts;
  2. import com.alibaba.fastjson.JSONObject;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. /**
  6. * @Author leijs
  7. * @date 2022/4/10
  8. */
  9. public class MergeSort {
  10. public static void main(String[] args) {
  11. int[] array = new int[]{7, 2, 5, 4, 1, 9, 17, 11, 12, 3};
  12. mergeSort(array, 0, array.length - 1);
  13. System.out.println("final result:" + JSONObject.toJSONString(array));
  14. }
  15. public static void mergeSort(int[] li, int low, int high) {
  16. if (low < high) {
  17. // 至少有两个元素
  18. int mid = (low + high) / 2;
  19. mergeSort(li, low, mid);
  20. mergeSort(li, mid + 1, high);
  21. merge(li, low, mid, high);
  22. }
  23. }
  24. public static void merge(int[] li, int low, int mid, int high) {
  25. // low->mid是一段 mid+1->high是一段
  26. // i 是第一段的开始,j是第二段的开始
  27. int i = low;
  28. int j = mid + 1;
  29. // 临时存放列表
  30. List<Integer> temp = new ArrayList<>();
  31. // 只要左右两边都有数比一下这两个数
  32. while (i <= mid && j <= high) {
  33. if (li[i] < li[j]) {
  34. temp.add(li[i]);
  35. i += 1;
  36. } else {
  37. temp.add(li[j]);
  38. j += 1;
  39. }
  40. }
  41. // while执行完,肯定有一部分每数了
  42. while (i <= mid) {
  43. // 说明左边有数
  44. temp.add(li[i]);
  45. i += 1;
  46. }
  47. while (j <= high) {
  48. temp.add(li[j]);
  49. j += 1;
  50. }
  51. // temp写回li
  52. int curIndex = low;
  53. for (int k = 0; k < temp.size(); k++) {
  54. li[curIndex] = temp.get(k);
  55. curIndex += 1;
  56. }
  57. }
  58. }

时间复杂度:O(nlogn)
空间复杂度:O(n)
需要一些额外的空间。
image.png

总结

  • 三种时间复杂度O(nlogn)
  • 一般情况下,就运行时间而言:

    快速排序 < 归并排序 < 堆排序

  • 三种排序的优缺点

    快速排序: 极端情况下排序效率低 归并排序:需要额外的内存开销 堆排序:在快的排序算法中相对较慢。

image.png

其他排序

希尔排序

希尔排序是一种分组插入排序算法

过程

  1. 取一个整数d1 = n/2 ,将元素分为d1个组,每组相邻量元素之间的距离为d1, 在各组内进行直接插入排序
  2. 取第二个整数d2=d1/2, 重复上述分组排序过程,直到d1=1,即所有元素在同一组内进行直接插入排序。

希尔排序每趟并不使某些元素有序,而是使整体数据越来越接近有序,最后一趟使得所有数据有序。
image.png
长度是9,9/2 = 4, 那么我们可以把这个分成4组,间隔为4分组。
image.png
分别每组做插入排序:
image.png
然后再回到初始位置,
image.png]
接下来d=2, 然后分成两组,每组再做插入排序:
image.png
排序好之后再归位:
image.png
最后d=1,对这个数组直接进行插入排序完事
image.png

代码

  1. package com.algorithm.demo.sorts;
  2. import com.alibaba.fastjson.JSONObject;
  3. /**
  4. * @Author leijs
  5. * @date 2022/4/15
  6. */
  7. public class ShellSort {
  8. public static void main(String[] args) {
  9. int[] array = new int[]{7, 2, 5, 4, 1, 9, 17, 11, 12, 3};
  10. int d = array.length / 2;
  11. while (d >= 1) {
  12. insertSort(array, d);
  13. d = d / 2;
  14. }
  15. System.out.println("final result:" + JSONObject.toJSONString(array));
  16. }
  17. public static void insertSort(int[] array, int gap) {
  18. // 第i趟
  19. for (int i = gap; i < array.length; i++) {
  20. // j指的是手里的牌
  21. int j = i - gap;
  22. // 摸到的牌
  23. int temp = array[i];
  24. if (temp > array[j]) {
  25. continue;
  26. }
  27. while (j >= 0 && temp < array[j]) {
  28. array[j + gap] = array[j];
  29. j -= gap;
  30. }
  31. array[j + gap] = temp;
  32. System.out.println("sort result:" + JSONObject.toJSONString(array));
  33. }
  34. }
  35. }

其他

希尔排序的时间复杂度和gap取值有关系。

计数排序

对列表进行排序,已知列表中数的范围都在0-100之间,设计时间复杂度为O(n)的算法。
对每个数字计数
如: 1 3 2 4 1 2 4 5 7

  1. 数字 计数
  2. 1 2
  3. 2 2
  4. 3 1
  5. 4 2
  6. 5 1
  7. 7 1

实现

  1. package com.algorithm.demo.sorts;
  2. import com.alibaba.fastjson.JSONObject;
  3. /**
  4. * @Author leijs
  5. * @date 2022/4/15
  6. */
  7. public class CountSort {
  8. public static void main(String[] args) {
  9. int[] array = new int[] {7,2,5,4,1,9,17,11, 12,3};
  10. sort(array, 18);
  11. System.out.println("final result:" + JSONObject.toJSONString(array));
  12. }
  13. public static void sort(int[] li, int maxCount) {
  14. int[] count = new int[maxCount];
  15. for (int num : li) {
  16. count[num] = count[num] + 1;
  17. }
  18. int index = 0;
  19. for (int i = 0; i < count.length; i++) {
  20. int total = count[i];
  21. if (total == 0) {
  22. continue;
  23. }
  24. for (int j = 0; j < total; j++) {
  25. li[index] = i;
  26. index++;
  27. }
  28. }
  29. }
  30. }

总结

时间复杂度:O(n) 缺点:需要知道数的范围;对小数不是很友好。如果知道0-100,就需要一个长度101的数组,如果是1个亿?

桶排序

首先将数划分为不同的范围,相当于丢在不同的桶,然后每个桶的元素保持有序。
image.png

实现

  1. package com.algorithm.demo.sorts;
  2. import com.alibaba.fastjson.JSONObject;
  3. import java.util.Arrays;
  4. /**
  5. * @Author leijs
  6. * @date 2022/4/15
  7. */
  8. public class BucketSort {
  9. public static void main(String[] args) {
  10. int[] array = new int[]{7, 2, 5, 4, 1, 9, 17, 11, 12, 3};
  11. bucketSort(array, 4, 17);
  12. System.out.println("final result:" + JSONObject.toJSONString(array));
  13. }
  14. public static void bucketSort(int[] array, int bucketNums, int maxNum) {
  15. // 创建N个桶,每个桶放maxNum/bucketNums个数
  16. int range = maxNum / bucketNums;
  17. int[][] buckets = new int[bucketNums][];
  18. for (int i = 0; i < array.length; i++) {
  19. int value = array[i];
  20. int j = Math.min(value / range, bucketNums - 1);
  21. int[] currentBucketVals = buckets[j];
  22. if (null == currentBucketVals) {
  23. currentBucketVals = new int[1];
  24. buckets[j] = currentBucketVals;
  25. currentBucketVals[0] = value;
  26. continue;
  27. }
  28. int[] newElements = Arrays.copyOf(currentBucketVals, currentBucketVals.length + 1);
  29. newElements[currentBucketVals.length] = value;
  30. if (newElements.length > 1) {
  31. insetSort(newElements);
  32. }
  33. buckets[j] = newElements;
  34. }
  35. // 最后回写array
  36. int curIndex = 0;
  37. for (int i = 0; i < buckets.length; i++) {
  38. for (int j = 0; j < buckets[i].length; j++) {
  39. array[curIndex] = buckets[i][j];
  40. curIndex++;
  41. }
  42. }
  43. }
  44. public static void insetSort(int[] array) {
  45. // 第i趟
  46. for (int i = 1; i < array.length; i++) {
  47. // j指的是手里的牌
  48. int j = i - 1;
  49. // 摸到的牌
  50. int temp = array[i];
  51. if (temp > array[j]) {
  52. continue;
  53. }
  54. while (j >= 0 && temp < array[j]) {
  55. array[j + 1] = array[j];
  56. j -= 1;
  57. }
  58. array[j + 1] = temp;
  59. }
  60. }
  61. }
  • 桶排序的表现取决于数据的分布,也就是需要对不同数据排序时采取不同的分桶策略。
  • 平均情况时间复杂度:O(n+k)
  • 最坏情况时间复杂度:O(n^2 * k)
  • 空间复杂度:O(nk)

    对k,代表一个桶可以有多少数据

基数排序

多关键字排序,比方说员工表,先按照薪资排序,再按薪资相同按照年龄排序。
image.png

思路: 94和32, 看十位,9比3大,所以94大于32. 94和94,十位相同,比较个位

image.png
image.pngimage.png

实现

  1. package com.algorithm.demo.sorts;
  2. import com.alibaba.fastjson.JSONObject;
  3. import java.util.Arrays;
  4. /**
  5. * @Author leijs
  6. * @date 2022/4/15
  7. */
  8. public class RadixSort {
  9. public static void main(String[] args) {
  10. int[] array = new int[]{7, 2, 5, 4, 1, 9, 17, 11, 12, 3};
  11. radixSort(array, 17);
  12. System.out.println("final result:" + JSONObject.toJSONString(array));
  13. }
  14. public static void radixSort(int[] li, int max) {
  15. // 最大值:99-> 2次排序,999 -> 需要3次
  16. int it = 0;
  17. while (Math.pow(10, it) < max) {
  18. int[][] buckets = new int[10][];
  19. for (int value : li) {
  20. int curBucket = (int) (value / (Math.pow(10, it)) % 10);
  21. int[] currentBucketVals = buckets[curBucket];
  22. if (null == currentBucketVals) {
  23. currentBucketVals = new int[1];
  24. buckets[curBucket] = currentBucketVals;
  25. currentBucketVals[0] = value;
  26. continue;
  27. }
  28. int[] newElements = Arrays.copyOf(currentBucketVals, currentBucketVals.length + 1);
  29. newElements[currentBucketVals.length] = value;
  30. if (newElements.length > 1) {
  31. insetSort(newElements);
  32. }
  33. buckets[curBucket] = newElements;
  34. }
  35. // 最后回写array
  36. int curIndex = 0;
  37. for (int i = 0; i < buckets.length; i++) {
  38. if (null == buckets[i]) {
  39. continue;
  40. }
  41. for (int j = 0; j < buckets[i].length; j++) {
  42. li[curIndex] = buckets[i][j];
  43. curIndex++;
  44. }
  45. }
  46. it += 1;
  47. }
  48. }
  49. public static void insetSort(int[] array) {
  50. // 第i趟
  51. for (int i = 1; i < array.length; i++) {
  52. // j指的是手里的牌
  53. int j = i - 1;
  54. // 摸到的牌
  55. int temp = array[i];
  56. if (temp > array[j]) {
  57. continue;
  58. }
  59. while (j >= 0 && temp < array[j]) {
  60. array[j + 1] = array[j];
  61. j -= 1;
  62. }
  63. array[j + 1] = temp;
  64. }
  65. }
  66. }

时间复杂度

  • 时间复杂度:O(kn)
  • 空间复杂度:O(k+n)
  • k表示数字位数