排序效率:
我认为单次遍历结果越有序,效率越高
Arrays.sort()——>底层默认是快速排序——>但是极容易造成栈溢出
排序舞蹈(别点)

1 插入排序
原理
性能
时间复杂度
最好情况——>本身有序——>O(n)——>越有序越快
最坏情况——>本身逆序——>O(n^2)
空间复杂度
O(1)
稳定性
——>if (arr[j] > tmp) {稳定
——>if (arr[j] >= tmp) {不稳定
public static void insertionSort(int[] arr) {for (int i = 0; i < arr.length; i++) {int tmp = arr[i];int j = i - 1;for (; j >= 0; j--) {if (arr[j] > tmp) {arr[j + 1] = arr[j];} else {//可以省略arr[j+1] = tmp;break;}}arr[j + 1] = tmp;}}
2 希尔排序
原理
优化的插入排序,主体部分分组,gap/3+1个组,i从gap开始,外层i++,内层j从i-gap开始(第一次i=gap也就是第一次j=0),每次减gap,直到最后gap = 1也就是一次正式的插入排序
性能
时间复杂度:
O(n1.5)——>最坏情况O(n^2)
空间复杂度:
O(1)
稳定性:
因其跳着分组不能保证相同数据的原有顺序,所以不稳定
public static void shellSort(int[] array) {int gap = array.length;while (gap > 1) {shell(array, gap);gap = (gap / 3) + 1;//或者 gap/2}shell(array, 1);/*//分组次数(length)以及个数(元素)int[] drr = {5, 3, 1};//依次分组——>遍历drrfor (int value : drr) {shell(array, value);}*/}private static void shell(int[] array, int gap) {int i = gap;//i++每组都进行插入排序,i+=gap则是按组进行,可能不能完全遍历for (; i < array.length; i++) {int tmp = array[i];int j = i - gap;for (; j >= 0; j = j - gap) {if (array[j] > tmp) {array[j + gap] = array[j];} else {//可以省略arr[j+1] = tmp;break;}}array[j + gap] = tmp;}}
3 选择排序
原理
外层i内层j,每次遇见前大于后的也就是arr[i]>arr[j]的就互换
性能
时间复杂度:
O(n^2)(好坏都是)
空间复杂度:
O(1)
稳定性:
稳定
static void selectSort(int[] array) {for (int i = 0; i < array.length-1 ; i++) {for (int j = i + 1; j < array.length; j++) {if (array[j] < array[i]) {int tmp = array[i];array[i] = array[j];array[j] = tmp;}}}
4 堆排序

从小到大排序——大堆
从大到小排序——小堆
每一次排序,确定一个当前最大的数,useSize--;
_
原理
使用end = array.length-1最后的结点与0下标交换
调用adjust方法调整使其再次成为大根堆
轮转至end = 0意味着每个位置都放到堆顶调整过,保证其数组整体顺序
adjust调整
传入树,parent结点,以及长度len
利用双亲结点得到其子节点下标
在确认其子节点存在后,进行调整
子节点val大于双亲结点val,则交换,同时交换下标
否则,跳出循环
_
createHeap创建大根堆
将二叉树的每个双亲结点放入调整函数
创建出来的尽管是大根堆,但是并不能够保证其左右同高度结点之间的顺序是否正确
heapSort堆排序主体
创建大根堆
end遍历调整堆
得到顺序数组
性能
时间复杂度 O(n*logn)
空间复杂度O(1)
稳定性
方法:_
- adjust——>调整
- createHeap——>创建堆
heapSort——>堆排序主体 ```java public static void adjust(int[] array, int parent, int len) {
int child = parent*2+1;while (child < len) {if(child+1 < len ) {child++;}if(array[child] > array[parent]) {int tmp = array[child];array[child] = array[parent];array[parent] = tmp;parent = child;child = 2*parent+1;}else{break;}}
}
public static void createHeap(int[] array){
for (int i = (array.length-1-1)/2; i >= 0; i--) {//i为每棵树的parent结点adjust(array,i,array.length);}
}
public static void heapSort(int[] array) {
//大堆——>O(n)createHeap(array);//排序——>O(n*logn)int end = array.length-1;//最后一位,用于交换while(end > 0) {//当前节点与0位置交换位置int tmp = array[0];array[0] = array[end];array[end] = tmp;//换完调整adjust(array,0,end);end--;}
}
<a name="8pC3k"></a>## 5 冒泡排序<a name="HfjRj"></a>#### 原理外层i内层j,i是趟数,j是具体每趟操作,内层循环中,每挨着的两个前大于后,对调<a name="FuYAw"></a>#### 优化外层加一个flg默认false,如果进了内部的if则置true,外层循环最后加一个if判断,为假,则意味着没进入已经正序了,直接跳出时间复杂度:<br />O(n^2)(未优化则好坏都是)<br /> 优化后,最好情况O(n),最坏情况O(n^2)<br />空间复杂度:<br />O(1)<br />稳定性:<br />if (array[j] > array[j + 1]) {稳定<br /> if (array[j] >= array[j + 1]) {不稳定```javapublic static void bubbleSort(int[] array) {//趟数for (int i = 0; i < array.length; i++) {//每趟,-1意味着最后一位没必要和自己比,-i意味着后i个数据已经是有序的了boolean flg = false;for (int j = 0; j < array.length - 1 - i; j++) {if (array[j] > array[j + 1]) {//交换int tmp = array[j];array[j] = array[j + 1];array[j + 1] = tmp;flg = true;}}//优化,如果某一次没有发生交换,则flg没有置trueif(!flg) break;}}
6 快速排序

public static int partation(int[] array, int start, int end) {int tmp = array[start];while (start < end) {while(start < end && tmp <= array[end]){end--;}if(start >= end){array[start] = tmp;break;}else{array[start] = array[end];}while(start < end && array[start] <= tmp){start++;}if(start >= end){array[start] = tmp;break;}else{array[start] = array[end];}}return start;}public static void quick(int[] array, int low, int high) {//终止条件if (low >= high) return;int par = partation(array, low, high);//par左quick(array, low, par - 1);//par右quick(array, par + 1, high);}public static void quickSort(int[] array) {quick(array,0,array.length-1);}
原理
- 在待排序区间选择一个基准值
1. 选择左边或者右边
2. 随机选取
3. 几数取中法
2. 做 partition,使得小的数在左,大的数在右
1. hoare
2. 挖坑
3. 前后遍历
4. 将基准值相等的也选择出来(了解)
3. 分治处理左右两个小区间,直到小区间数目小于一个阈值,使用插入排序
优化
1)选择边上(左或者右)
2)random随机一个基准
3)三数取中,头、中、尾,找中间的数字作为基准
4)相差不到100时,直接插入排序
//优化3的方法public static void medianOfThree(int[] array, int start,int mid, int end){if(array[start] < array[mid]){int tmp = array[start];array[start] = array[mid];array[mid] = tmp;}//array[mid] <= array[start]if(array[start] > array[end]){int tmp = array[start];array[start] = array[end];array[end] = tmp;}//array[start] <= array[end]if(array[mid] > array[end]){int tmp = array[mid];array[mid] = array[end];array[end] = tmp;}}//优化4的insert插入排序public static void insertSort(int[] array,int start,int end){for (int i = 0; i < array.length; i++) {int tmp = array[i];int j = i-1;for (; j >= start ; j--) {if(array[j] > tmp){array[j+1] = array[j];}else{break;}array[j+1] = tmp;}}}//优化4,中间差了不到100个了就用插入排序if(high-low+1 < 100){insertSort(array,low,high);return;}//优化3,三数取中int mid = (low+high)>>>1;//中间相加除以2medianOfThree(array,low,mid,high);
优化总结
- 选择基准值很重要,通常使用几数取中法
2. partition 过程中把和基准值相等的数也选择出来
3. 待排序区间小于一个阈值时(例如 48),使用直接插入排序快排总结
可能出的题目
下列选项可能是快速排序第一趟排序结果的是:
7 归并排序
原理
大问题化小,分而治之——>是不是就是递归的思想原则
1.分解2.重排序3.合并
分组原则
每次长度除以二,直到只剩下自己一组
性能
时间复杂度
O(nlogn)
空间复杂度
O(n) 每次递归都申请了数组
稳定性
if (array[s1] <= array[s2]) {// 有=稳定,没=不稳定
public class MergeSort {//递归体,用于前半程的拆分public static void mergeSortRec(int[] array, int low, int high) {//递归终止条件if (low >= high) return;int mid = (high + low) >>> 1;mergeSortRec(array, low, mid);//左mergeSortRec(array, mid + 1, high);//右merge(array, low, mid, high);}//合并函数,用户后半程的合并public static void merge(int[] array, int low, int mid, int high) {//临时数组用于存放合并的数组int[] tmp = new int[high - low + 1];int k = 0;//数组下标//合并两个有序数组int s1 = low;int e1 = mid;int s2 = mid + 1;int e2 = high;while (s1 <= e1 && s2 <= e2) {if (array[s1] <= array[s2]) {//可不可以取等于 有=稳定,没=不稳定tmp[k++] = array[s1++];} else {tmp[k++] = array[s2++];}}//谁剩下了谁继续while (s1 <= e1) {tmp[k++] = array[s1++];}while (s2 <= e2) {tmp[k++] = array[s2++];}//合并完成for (int i = 0; i < tmp.length; i++) {array[i + low] = tmp[i];//+low原因:按位拷贝}}public static void mergeSort(int[] array) {mergeSortRec(array, 0, array.length - 1);}}
8 排序总结
排序分类
海量数据排序
内部排序:将数据全部都存储在了内存上 内存条8G
外部排序:将数据全部在外存上排序的 磁盘
如若现有40G文件,将其切割成我内存放得下大小的若干块,挨个读取排序,再按层归并
(或是说无论给定多大的一个数据,一语归并解决问题)
1.切割文件,使得每个文件当中的数据很小
2.读取文件里面的数据
3.排序
4.合并
按方式分类
比较排序
非比较排序
| 排序方法 | 最好 | 平均 | 最坏 | 空间复杂度 | 稳定性 |
|---|---|---|---|---|---|
| 冒泡排序 | O(n) | O(n) | O(n) | O(1) | 稳定 |
| 插入排序 | O(n) | O(n) | O(n) | O(1) | 稳定 |
| 选择排序 | O(n) | O(n) | O(n) | O(1) | 不稳定 |
| 希尔排序 | O(n) | O(n) | O(n) | O(1) | 不稳定 |
| 堆排序 | O(n*log2n) | O(n*log2n) | O(n*log2n) | O(1) | 不稳定 |
| 快速排序 | O(n*log2n) | O(n*log2n) | O(n) | O(log2n) - O(n) | 不稳定 |
| 归并排序 | O(n*log2n) | O(n*log2n) | O(n*log2n) | O(n) | 稳定 |
9 其他非基于比较的排序
计数排序
基数排序
List<Queue<Integer>> a = new ArrayList<>();数组每个元素是一个队列
或者链表
十个桶,0.1.2.3.4…8.9
按位比较,个十百千,按当前位上数字大小顺序入桶
