排序:就是将一组无序的记录序列调整为有序的记录序列。
列表排序:将无序列表变为有序列表
- 输入:列表
- 输出:列表
lOW B三人组


从下往上,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) {
} public static void sort(int[] array) {int[] array = new int[] {7,2,5,4,1,9,17,11, 12,3};sort(array);System.out.println("final result:" + JSONObject.toJSONString(array));
} }// 第i趟for (int i = 0; i < array.length; i++) {// 每一趟少1,一共是n-1趟boolean exchange = false;for (int j = 0; j < array.length -i - 1; j++) {if (array[j] > array[j+1]) {int temp = array[j];array[j] = array[j+1];array[j+1] = temp;exchange = true;}}if (!exchange) {return;}System.out.println(JSONObject.toJSONString(array));}
- 时间复杂度:O(n^2)注意改进的地方:> 如果没有可以交换的,说明已经不需要排序了。<a name="eZZp7"></a>## 选择排序遍历一遍找到最小的数,再遍历一遍找到一个最小的数,拿出来放到一个新的列表```javapackage com.algorithm.demo.sorts;import com.alibaba.fastjson.JSONObject;import com.google.common.collect.Lists;import java.util.List;/*** @Author leijs* @date 2022/4/9*/public class SelectSort {public static void main(String[] args) {List<Integer> array = Lists.newArrayList(7,2,5,4,1,9,17,11, 12,3, 9);System.out.println("final result:" + JSONObject.toJSONString(sort(array)));}public static List<Integer> sort(List<Integer> array) {List<Integer> newList = Lists.newArrayList();int totalSize = array.size();for (int i = 0; i < totalSize; i++) {int minNum = getMinNum(array);newList.add(minNum);}return newList;}public static int getMinNum(List<Integer> array) {int size = array.size();int minIndex = 0;int minNum = array.get(0);for (int i = 1; i < size; i++) {if (array.get(i) < minNum) {minIndex = i;minNum = array.get(i);}}array.remove(minIndex);System.out.println("remove result:" + JSONObject.toJSONString(array));return minNum;}}
缺点:
- 选择排序生成了一个新的列表,会占用更多的内存
- 冒泡排序是原地排序
选择排序优化
- 每一次遍历,找到最小的数和其下标,然后和首位交换
- 这样首位及之后位置会随着这种和最小的交换变得有序
- 然后我们重复关心无序区域的数据 ```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) {
int[] array = new int[] {7,2,5,4,1,9,17,11, 12,3};sort(array);System.out.println("final result:" + JSONObject.toJSONString(array));
}
public static void sort(int[] array) {
for (int i = 0; i < array.length; i++) {int cur = array[i];int minIndex = i;for (int j = i+ 1; j < array.length; j++) {if (cur > array[j]) {cur = array[j];minIndex = j;}}// 交换顺序int temp = array[i];array[i] = array[minIndex];array[minIndex] = temp;System.out.println("sort result:" + JSONObject.toJSONString(array));}
} }
注意有序区和无序区的位置,注意记录最小数的位置。复杂度O(n^2)<a name="nG2za"></a>## 插入排序类似于玩牌的时候,摸牌并插入到有牌的正确位置。```javapackage com.algorithm.demo.sorts;import com.alibaba.fastjson.JSONObject;/*** @Author leijs* @date 2022/4/9*/public class InsertSort {public static void main(String[] args) {int[] array = new int[]{7, 2, 5, 4, 1, 9, 17, 11, 12, 3};sort(array);System.out.println("final result:" + JSONObject.toJSONString(array));}public static void sort(int[] array) {// 第i趟for (int i = 1; i < array.length; i++) {// j指的是手里的牌int j = i - 1;// 摸到的牌int temp = array[i];if (temp > array[j]) {continue;}while (j >= 0 && temp < array[j]) {array[j + 1] = array[j];j -= 1;}array[j + 1] = temp;System.out.println("sort result:" + JSONObject.toJSONString(array));}}}
牛B三人组
快速排序
快速排序的特点:
- 取一个元素P(第一个元素),使元素P归位
- 列表被P分为两个部分,左边都比P小,右边都比P大。
- 递归完成排序。
快速排序的框架

package com.algorithm.demo.sorts;import com.alibaba.fastjson.JSONObject;/*** @Author leijs* @date 2022/4/9*/public class QuickSort {public static void main(String[] args) {int[] array = new int[] {7,2,5,4,1,9,17,11, 12,3};sort(array, 0, array.length - 1);System.out.println("final result:" + JSONObject.toJSONString(array));}public static void sort(int[] array, int left, int right) {if (left < right) {int mid = partition(array, left, right);sort(array, left, mid -1);sort(array, mid + 1, right);}}public static int partition(int[] array, int left, int right) {// 把第一个元素存起来int temp = array[left];while (left < right) {// 从右边找一个比5小的数,因为左边有空位while (left < right && array[right] >= temp) {// 只要比temp小的,往左走一right -= 1;}// 把右边的值写到昨天空位上array[left] = array[right];// 接下来就要从左边找比5大的数while (left < right && array[left] <= temp) {left += 1;}// 把左边的值写到右边的空位上array[right] = array[left];}// temp写在left和right碰头的位置,把temp归位array[left] = temp;return left;}}
快速排序的复杂度:
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)
递归的深度
最坏情况的解决:随机找一个数,和第一个数交换,然后逻辑再同之前。
堆排序
前传-树与二叉树

树的基本概念:
根节点、叶子节点
叶子节点:不能分叉的节点,没有孩子
树的深度(高度)
上图4层
树的度
节点的度:对于E节点,度是2,分了两个叉7,F节点是3 树的度最大节点分的叉,A分了6个叉,所以这个树的度就是6.
孩子节点/父节点
节点时间的关系。E是I的父节点,I是E的孩子节点
子树
E I J P Q 就属于子树,,一个树的分叉一起拿出来。
二叉树
度不超过2的树,
- 完全二叉树:

完全二叉树就是满二叉树叶子节点右边拿到一些叶子节点。
- 二叉树的存储方式
- 链式存储方式
- 顺序存储方式(重点):简单用列表来存储
下面这是一个完全二叉树:
首先9的编号是0,其左子节点8的编号是1
父节点8的编号是1,左子节点6的编号是3
什么是堆
堆:是一种特殊的完全二叉树
- 大根堆:一颗完全二叉树,满足任一节点都比其孩子节点大
- 小根堆:一颗完全二叉树,满足任一节点都比其孩子节点小。

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

挨个出树,9走了,然后把3放上去:
然后向下调整。之后重复这个过程。
怎么建造一个堆?
首先需要下级先有序。农村包围城市。看最后一个非叶子节点。
- 3个5调整。

- 7和1调整
重复以上调整过程。
package com.algorithm.demo.sorts;import java.util.Arrays;/*** @Author leijs* @date 2022/4/9*/public class HeapSortV2 {public static void main(String[] args) {int[] a = {-1, 9, 0, 6, 5, 8, 2, 1, 7, 4, 3};System.out.println(Arrays.toString(a));HeapSortV2.sort(a);System.out.println(Arrays.toString(a));}public static void sort(int[] a) {//s[0]不用,实际元素数量和最后一个元素的角标都为Nint N = a.length - 1;//构造堆//如果给两个已构造好的堆添加一个共同父节点,//将新添加的节点作一次下沉将构造一个新堆,//由于叶子节点都可看作一个构造好的堆,所以//可以从最后一个非叶子节点开始下沉,直至//根节点,最后一个非叶子节点是最后一个叶子//节点的父节点,角标为N/2for (int k = N / 2; k >= 1; k--) {sink(a, k, N);}//下沉排序while (N > 1) {swap(a, 1, N--); //将大的放在数组后面,升序排序sink(a, 1, N);}}//下沉(由上至下的堆有序化)private static void sink(int[] a, int k, int N) {while (true) {int i = 2 * k;if (i > N) { //保证该节点是非叶子节点break;}if (i < N && a[i + 1] > a[i]) { //选择较大的子节点i++;}if (a[k] >= a[i]) { //没下沉到底就构造好堆了break;}swap(a, k, i);k = i;}}private static void swap(int[] a, int i, int j) {int t = a[i];a[i] = a[j];a[j] = t;}}
时间复杂度
问题:topK问题
现在有n个数,设计算法得到前K个大的数(k < n)
解决思路:
- 方法一:排序后切片,时间复杂度:O(nlogn)
方法二:排序LowB 三人组
冒泡排序排K次。O(kn)
方法三:堆排序,复杂度 O(nlogk)
取列表前K个建立一个小根堆,堆顶就是目前第K大的数 依次向后遍历原列表,对于列表中的元素,如果小于堆顶,则忽略该元素,如果大于堆顶,则将该堆顶替换为该元素,并且对堆进行一次排序。 遍历完所有元素后,倒序倒出堆顶。
归并排序

- 分解:将列表越分越小,直至分成一个元素。
- 终止条件:一个元素是有序的。
- 合并:将两个有序列表归并,列表越来越大。
两端,2 和 1比较,发现1小,放入第一位,然后第二段指针往右; 2 和 3 比,发现是2,则第一段指针往左挪 接下来:5和3比。重复此过程。



package com.algorithm.demo.sorts;import com.alibaba.fastjson.JSONObject;import java.util.ArrayList;import java.util.List;/*** @Author leijs* @date 2022/4/10*/public class MergeSort {public static void main(String[] args) {int[] array = new int[]{7, 2, 5, 4, 1, 9, 17, 11, 12, 3};mergeSort(array, 0, array.length - 1);System.out.println("final result:" + JSONObject.toJSONString(array));}public static void mergeSort(int[] li, int low, int high) {if (low < high) {// 至少有两个元素int mid = (low + high) / 2;mergeSort(li, low, mid);mergeSort(li, mid + 1, high);merge(li, low, mid, high);}}public static void merge(int[] li, int low, int mid, int high) {// low->mid是一段 mid+1->high是一段// i 是第一段的开始,j是第二段的开始int i = low;int j = mid + 1;// 临时存放列表List<Integer> temp = new ArrayList<>();// 只要左右两边都有数比一下这两个数while (i <= mid && j <= high) {if (li[i] < li[j]) {temp.add(li[i]);i += 1;} else {temp.add(li[j]);j += 1;}}// while执行完,肯定有一部分每数了while (i <= mid) {// 说明左边有数temp.add(li[i]);i += 1;}while (j <= high) {temp.add(li[j]);j += 1;}// temp写回liint curIndex = low;for (int k = 0; k < temp.size(); k++) {li[curIndex] = temp.get(k);curIndex += 1;}}}
时间复杂度:O(nlogn)
空间复杂度:O(n)
需要一些额外的空间。
总结
- 三种时间复杂度O(nlogn)
一般情况下,就运行时间而言:
快速排序 < 归并排序 < 堆排序
三种排序的优缺点
快速排序: 极端情况下排序效率低 归并排序:需要额外的内存开销 堆排序:在快的排序算法中相对较慢。
其他排序
希尔排序
过程
- 取一个整数d1 = n/2 ,将元素分为d1个组,每组相邻量元素之间的距离为d1, 在各组内进行直接插入排序
- 取第二个整数d2=d1/2, 重复上述分组排序过程,直到d1=1,即所有元素在同一组内进行直接插入排序。
希尔排序每趟并不使某些元素有序,而是使整体数据越来越接近有序,最后一趟使得所有数据有序。
长度是9,9/2 = 4, 那么我们可以把这个分成4组,间隔为4分组。
分别每组做插入排序:
然后再回到初始位置,
]
接下来d=2, 然后分成两组,每组再做插入排序:
排序好之后再归位:
最后d=1,对这个数组直接进行插入排序完事
代码
package com.algorithm.demo.sorts;import com.alibaba.fastjson.JSONObject;/*** @Author leijs* @date 2022/4/15*/public class ShellSort {public static void main(String[] args) {int[] array = new int[]{7, 2, 5, 4, 1, 9, 17, 11, 12, 3};int d = array.length / 2;while (d >= 1) {insertSort(array, d);d = d / 2;}System.out.println("final result:" + JSONObject.toJSONString(array));}public static void insertSort(int[] array, int gap) {// 第i趟for (int i = gap; i < array.length; i++) {// j指的是手里的牌int j = i - gap;// 摸到的牌int temp = array[i];if (temp > array[j]) {continue;}while (j >= 0 && temp < array[j]) {array[j + gap] = array[j];j -= gap;}array[j + gap] = temp;System.out.println("sort result:" + JSONObject.toJSONString(array));}}}
其他
计数排序
对列表进行排序,已知列表中数的范围都在0-100之间,设计时间复杂度为O(n)的算法。
对每个数字计数
如: 1 3 2 4 1 2 4 5 7
数字 计数1 22 23 14 25 17 1
实现
package com.algorithm.demo.sorts;import com.alibaba.fastjson.JSONObject;/*** @Author leijs* @date 2022/4/15*/public class CountSort {public static void main(String[] args) {int[] array = new int[] {7,2,5,4,1,9,17,11, 12,3};sort(array, 18);System.out.println("final result:" + JSONObject.toJSONString(array));}public static void sort(int[] li, int maxCount) {int[] count = new int[maxCount];for (int num : li) {count[num] = count[num] + 1;}int index = 0;for (int i = 0; i < count.length; i++) {int total = count[i];if (total == 0) {continue;}for (int j = 0; j < total; j++) {li[index] = i;index++;}}}}
总结
时间复杂度:O(n) 缺点:需要知道数的范围;对小数不是很友好。如果知道0-100,就需要一个长度101的数组,如果是1个亿?
桶排序
首先将数划分为不同的范围,相当于丢在不同的桶,然后每个桶的元素保持有序。
实现
package com.algorithm.demo.sorts;import com.alibaba.fastjson.JSONObject;import java.util.Arrays;/*** @Author leijs* @date 2022/4/15*/public class BucketSort {public static void main(String[] args) {int[] array = new int[]{7, 2, 5, 4, 1, 9, 17, 11, 12, 3};bucketSort(array, 4, 17);System.out.println("final result:" + JSONObject.toJSONString(array));}public static void bucketSort(int[] array, int bucketNums, int maxNum) {// 创建N个桶,每个桶放maxNum/bucketNums个数int range = maxNum / bucketNums;int[][] buckets = new int[bucketNums][];for (int i = 0; i < array.length; i++) {int value = array[i];int j = Math.min(value / range, bucketNums - 1);int[] currentBucketVals = buckets[j];if (null == currentBucketVals) {currentBucketVals = new int[1];buckets[j] = currentBucketVals;currentBucketVals[0] = value;continue;}int[] newElements = Arrays.copyOf(currentBucketVals, currentBucketVals.length + 1);newElements[currentBucketVals.length] = value;if (newElements.length > 1) {insetSort(newElements);}buckets[j] = newElements;}// 最后回写arrayint curIndex = 0;for (int i = 0; i < buckets.length; i++) {for (int j = 0; j < buckets[i].length; j++) {array[curIndex] = buckets[i][j];curIndex++;}}}public static void insetSort(int[] array) {// 第i趟for (int i = 1; i < array.length; i++) {// j指的是手里的牌int j = i - 1;// 摸到的牌int temp = array[i];if (temp > array[j]) {continue;}while (j >= 0 && temp < array[j]) {array[j + 1] = array[j];j -= 1;}array[j + 1] = temp;}}}
- 桶排序的表现取决于数据的分布,也就是需要对不同数据排序时采取不同的分桶策略。
- 平均情况时间复杂度:O(n+k)
- 最坏情况时间复杂度:O(n^2 * k)
- 空间复杂度:O(nk)
对k,代表一个桶可以有多少数据
基数排序
多关键字排序,比方说员工表,先按照薪资排序,再按薪资相同按照年龄排序。
思路: 94和32, 看十位,9比3大,所以94大于32. 94和94,十位相同,比较个位
实现
package com.algorithm.demo.sorts;import com.alibaba.fastjson.JSONObject;import java.util.Arrays;/*** @Author leijs* @date 2022/4/15*/public class RadixSort {public static void main(String[] args) {int[] array = new int[]{7, 2, 5, 4, 1, 9, 17, 11, 12, 3};radixSort(array, 17);System.out.println("final result:" + JSONObject.toJSONString(array));}public static void radixSort(int[] li, int max) {// 最大值:99-> 2次排序,999 -> 需要3次int it = 0;while (Math.pow(10, it) < max) {int[][] buckets = new int[10][];for (int value : li) {int curBucket = (int) (value / (Math.pow(10, it)) % 10);int[] currentBucketVals = buckets[curBucket];if (null == currentBucketVals) {currentBucketVals = new int[1];buckets[curBucket] = currentBucketVals;currentBucketVals[0] = value;continue;}int[] newElements = Arrays.copyOf(currentBucketVals, currentBucketVals.length + 1);newElements[currentBucketVals.length] = value;if (newElements.length > 1) {insetSort(newElements);}buckets[curBucket] = newElements;}// 最后回写arrayint curIndex = 0;for (int i = 0; i < buckets.length; i++) {if (null == buckets[i]) {continue;}for (int j = 0; j < buckets[i].length; j++) {li[curIndex] = buckets[i][j];curIndex++;}}it += 1;}}public static void insetSort(int[] array) {// 第i趟for (int i = 1; i < array.length; i++) {// j指的是手里的牌int j = i - 1;// 摸到的牌int temp = array[i];if (temp > array[j]) {continue;}while (j >= 0 && temp < array[j]) {array[j + 1] = array[j];j -= 1;}array[j + 1] = temp;}}}
时间复杂度
- 时间复杂度:O(kn)
- 空间复杂度:O(k+n)
- k表示数字位数






