《排序的底层逻辑:比较、交换与分治》
0.总览

一.插入排序
1.1直接插入排序
1).首先我们将元素划分为两组,分别是已排序区间0,bound)和待排序区间\[bound,size)
2).取待排序区间的第一个元素\[bound往已排序区间插入
3).中间要与已排序区间的元素排大小,不断覆盖
时间复杂度:o(n2)
空间复杂度:o(1);
是否稳定:稳定
java
public static void insertSort(int[] arr){
int bound = 1;
for(;bound<arr.length;bound++){
int value = arr[bound];
int cur = bound-1;
for(;cur>=0;cur--){
if(arr[cur]>value){
arr[cur+1]= arr[cur];
}
else{
break;
}
}
arr[cur+1] = value;
}
}
测试:
java
public static void main(String[] args){
// 测试直接插入排序
int[] arr = new int[]{9,1,2,5,7,4,8,1,3,5};
insertSort(arr);
System.out.println(Arrays.toString(arr));
}

1.2希尔排序
这个希尔排序其实就是直接插入排序的延伸,它是分成好多的组,好多个组在进行插入排序
1)首先要分间隔,例如元素
== 9,1,2,5,7,4,8,1,3,5==
当gap取5


可以看到明显有序多了

看下标元素,然后再继续逐步缩小gap,一直到整体有序
最后保底gap为1,其实就是直接插入排序
时间复杂度取决于BAP,最好能达到o(n1.3)
gap大:分组的元素比较少
gap下:分组的元素整体比较有序
是否稳定:不稳定
二.选择排序
2.1选择排序
- .选择排序的代码非常简单,就类似于打擂台,给一个最大值,谁打赢了就是最大的,;
java
private static void swap(int[] arr,int i,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void selectSort(int[] arr){
int bound = 0;
for(;bound < arr.length;bound++){
int minindex = bound;
for(int cur = bound+1;cur<arr.length;cur++) {
if (arr[cur] < arr[minindex]) {
minindex = cur;
}
}
swap(arr,bound,minindex);
}
}
2.2堆排序
1)把数组创建成优先级队列堆;
2)这里取从小到大的堆排序,将跟节点与最后一个有效节点heapSize节点交换,heapSiz--;
3)不断循环;
建堆
java
public static void creatHeap(int[] arr){
for(int i = (arr.length-1-1)/2;i>=0;i--){
shifDown(arr,arr.length,i);
}
}
向下
java
public static void shifDown(int[] arr,int size,int root){
int parent = root;
int child = 2*parent+1;
while(child<size){
if(child+1<size&&arr[child+1]>arr[child]){
child = child+1;
}
if(arr[parent]<arr[child]){
swap(arr,parent,child);
child = parent;
child = 2*parent+1;
}
else{
break;
}
}
}
堆排序
java
// 堆排序
public static void heapSort(int[] arr){
int heapSize = arr.length;
creatHeap(arr);
for(int i = 0;i<arr.length-1;i++){
swap(arr,0,heapSize-1);
heapSize--;
shifDown(arr,heapSize,0);
}
}
测试
java
// 测试堆排序
heapSort(arr);
System.out.println(Arrays.toString(arr));

三.交换排序
3.1冒泡排序
时间复杂度:o(n2)
空间复杂度:o(1)
是否稳定:稳定
java
// 冒泡排序
public static void bubbleSort(int[] arr){
for(int i = 0;i<arr.length;i++){
boolean fla = true;
for (int j = 0; j < arr.length-1-i; j++) {
if(arr[j]>arr[j+1]){
swap(arr,j,j+1);
fla = false;
}
}
if(fla == true){
break;
}
}
}
测试
java
bubbleSort(arr);
System.out.println(Arrays.toString(arr));

3.2快速排序
这个非常重要,至少在古法编程中是非常重要的;
1)先写快速排序的辅助方法,寻找基准值
2)根据基准值划分两个区间,然后递归的进行区分拆解;
3)基准值取最右边
java
// 快速排序
public static void quickSort(int[] arr){
quickSortHelper(arr,0,arr.length-1);
}
public static void quickSortHelper(int[] arr,int left,int right) {
if(left>=right){
return ;
}
// 先拿到基准值,分组大致整体有序
int index = partition(arr, left, right);
quickSortHelper(arr,left,index-1);
quickSortHelper(arr,index+1,right);
}
public static int partition(int[] arr,int left,int right){
int l = left;
int r = right;
while(l<r){
while(l<r&&arr[l]<=arr[right]){
l++;
}
while(l<r&&arr[r]>=arr[right]){
r--;
}
swap(arr,l,r);
}
swap(arr,l,right);
return l;
}
测试
java
// 测试快速排序
quickSort(arr);
System.out.println(Arrays.toString(arr));
}

3.3快速排序非递归实现
1)先将区间左右界限依次入栈;
2)找到基准值,把基准值的左右排好整体顺序
3)注意栈是后进先出,不然出入栈会出现问题
java
// 非递归快速排序;
public static void quickSortByloop(int[] arr){
Stack<Integer> stack = new Stack<>();
stack.push(arr.length-1);
stack.push(0);
while(!stack.empty()){
int left = stack.pop();
int right = stack.pop();
if(left>=right){
continue;
}
int index = partition(arr,left,right);
//先传右边区间
stack.push(right);
stack.push(index+1);
// 在传左边区间
stack.push(index-1);
stack.push(left);
}
}
测试
java
// 测试非递归快速排序
quickSortByloop(arr);
System.out.println(Arrays.toString(arr));

四.归并排序
归并排序和快速排序的思想是一样的,都是分治思想
4.1归并排序
1)图解

手绘,可以看到先分出来,然后再往回归并,最后变得有序
1)找到中间值,进行区间划分,一直到最后不能分
2)运用递归不断划分区间
java
// 归并排序
public static void mergeSort(int[] arr){
mergeSortHelper(arr,0,arr.length);
}
public static void mergeSortHelper(int[] arr,int left,int right){
if(right-left>=1){
return;
}
int mid = (left+right)/2;
mergeSortHelper(arr,left,mid);
mergeSortHelper(arr,mid,right);
merge(arr,left,mid,right);
}
public static void merge(int[] arr,int left,int mid,int right){
int[] temp = new int[right-left];
int tempIndex = 0;
int cur1 = left;
int cur2 = mid;
while(cur1<cur2&&cur2<right){
if(arr[cur1]<=cur2){
arr[tempIndex] = arr[cur1];
tempIndex++;
}
else{
arr[tempIndex] = arr[cur2];
tempIndex++;
}
}
while(cur1<cur2){
arr[tempIndex] = arr[cur1];
tempIndex++;
}
while(cur2<right){
arr[tempIndex] = arr[cur2];
tempIndex++;
}
}
4.2归并排序非递归实现
这个很容易理解,就是合并尺寸为1的数组,有了尺寸为2的数组然后依次往后合并
1)初始设置尺寸为1;
2)合并完成在合并2的数组
java
public static void mergeSortByloop(int[] arr){
int size = 1;
for(;size<arr.length;size*=2) {
for (int i = 0; i < arr.length; i += 2 * size) {
int left = i;
int mid = i + size;
if (mid > arr.length) {
mid = arr.length;
}
int right = i + 2 * size;
if (right > arr.length) {
right = arr.length;
}
merge(arr, left, mid, right);
}
}
}