归并排序
归并排序是一种分治法思想实现的排序,假如有一个数组9,3,7,2,8,5,1,4
先分割到最小,然后每一块各自排序。看图
分到只有一个数的时候(一个数肯定有序)开始合并,这就是“合并两个有序数组”
那先学一下合并两个有序数组这个算法
LeetCode.88
为什么要叫“非递减顺序”,直接叫“递增”不好吗。。
改成,一个数组中的x个有序区间
用递归,把这个数组分为两个有序区间,然后每次比较起点位置的数,小的放进结果数组
/**
* Description 将一个数组分为两个有序数组,依次互相比较,小的放进结果数组
* date 2023/10/17 17:21
* @param arr 待排序的原数组
* @param i 第一个区间的左边界
* @param iEnd 第一个区间的右边界
* @param j 第二个区间的左边界
* @param jEnd 第二个区间的有边界
* @param rs 结果数组
* @param k 结果数组的参数
* @author zqh
* @since JDK 1.8
*/
public static void merge(int[] arr,int i,int iEnd,int j,int jEnd,int [] rs,int k){
}
第一次分割后,i=1,j=2,比较这两个起点,谁更小谁放进结果数组,第一次递归调用后,rs有一个1。
第二次递归调用,i=5,j=2,把2放进结果数组,rs=1,2…
假如有一个区间没有元素了(当i>iEnd或者j>jEnd),另一个区间还剩下大于1个元素,像这样,就不用比较了,直接把剩下的元素复制到结果数组中。这也是递归的结束条件
最终
import com.sun.istack.internal.NotNull;
import java.util.Arrays;
/**
* Description 归并排序学习,B站黑马数据结构课
* date 2023/10/17 18:29
*
* @author zqh
* @since JDK 1.8
*/
public class MergeSort {
public static void main(String[] args) {
int[] arr = new int[]{12, 5, 8, 7, 3, 2, 9, 4, 10, 6, 1};
mergeSort(arr);
for (int i : arr) {
System.out.print(" " + i);
}
}
/**
* Description 归并排序
* date 2023/10/17 18:28
*
* @param arr 待排序数组
* @author zqh
* @since JDK 1.8
*/
public static void mergeSort(int[] arr){
// 创建临时数组
int[] rs = new int[arr.length];
// 初始范围
split(arr,0,arr.length-1,rs);
}
/**
* Description 递归分割数组
* date 2023/10/17 16:46
* @param arr 待排序数组
* @param left 数组左边界
* @param right 数组有边界
* @author zqh
* @since JDK 1.8
*/
private static void split(int[] arr,int left,int right,int[] rs){
// left=right,证明范围内只有一个数据了,停止递归
if (left == right){
return;
}
// 找中间点,分割一半
int mid = (left+right)/2;
// 递归,分割后左边的范围为left到中间点
split(arr,left,mid,rs);
// 右边的范围为mid+1到right
split(arr,mid+1,right,rs);
// 合并两个有序数组,注意临时数组的启始位置为left
merge(arr,left,mid,mid+1,right,rs,left);
// 把rs数组的元素复制到原arr数组
System.arraycopy(rs,left,arr,left,right-left+1);
}
/**
* Description 将一个数组分为两个有序数组并分别排序再合并
* date 2023/10/17 17:21
* @param arr 待排序的原数组
* @param i 第一个区间的左边界
* @param iEnd 第一个区间的右边界
* @param j 第二个区间的左边界
* @param jEnd 第二个区间的有边界
* @param rs 结果数组
* @param k 结果数组复制时用的下标,启始为0
* @author zqh
* @since JDK 1.8
*/
public static void merge(int[] arr,int i,int iEnd,int j,int jEnd,
int [] rs,int k){
// 递归终止条件
if (i>iEnd){
// 复制另一个区间的元素到结果元素
System.arraycopy(arr,j,rs,k,jEnd-j+1);
return;
}
if (j>jEnd){
System.arraycopy(arr,i,rs,k,iEnd-i+1);
return;
}
// 左边小于右边的情况,i<j
if (arr[i] < arr[j]){
rs[k] = arr[i];
merge(arr,i+1,iEnd,j,jEnd,rs,k+1);
}else {
// j小的情况
rs[k] = arr[j];
merge(arr,i,iEnd,j+1,jEnd,rs,k+1);
}
}
}
快速排序
在一个数组里选择有边界为基准元素,然后比它小的放在它左边,比它大的放在它右边,然后再在子数组里重复这个过程。
具体过程,找的基准元素后,找一个比他大的数,设置为第二指针。
然后把基准元素和第二指针后面的元素比较,如果后面的元素小于基准元素,就交换第二指针和这个较小元素的位置。然后第二指针+1,循环。最后找完后,交换基准元素和第二指针的位置,把基准元素方中间。
import java.util.Arrays;
/**
* Description 快速排序
* date 2023/10/18 15:02
*
* @author zqh
* @since JDK 1.8
*/
public class QuickSort {
public static void main(String[] args) {
int[] arr = new int[]{2,5,9,6,1,3,8};
quickSort(arr,0,arr.length-1);
System.out.println(Arrays.toString(arr));
}
/**
* Description
* date 2023/10/18 11:03
* @param arr 待排序数组
* @author zqh
* @since JDK 1.8
*/
public static void quickSort(int[] arr,int left,int right){
if (left<right){
// 分区
int position = partition(arr, left, right);
// 递归调用,左子数组
quickSort(arr,left,position-1);
// 递归调用,右子数组
quickSort(arr,position+1,right);
}
}
/**
* Description 分区排序,排完一次就代表一个基准点有序
* date 2023/10/18 10:48
* @param arr 待排数组
* @param left 左边界
* @param right 有边界
* @return 基准元素排好后的位置
* @author zqh
* @since JDK 1.8
*/
private static int partition(int[] arr,int left,int right){
// 定义基准元素为右边界
int p = arr[right];
// 定义比基准元素大的第二指针,初始化为左边界
int pointer = left;
// 遍历数组中所有元素,进行比较
for (int i = left; i < right; i++) {
// 如果有元素小于基准元素
if (arr[i] < p){
// 交换这个元素和第二指针的位置
int temp = arr[i];
arr[i] = arr[pointer];
arr[pointer] = temp;
// 第二指针+1
pointer++;
}
}
// 排序结束后,要把基准元素和第二指针交换。让基准元素到中间
int temp = arr[pointer];
arr[pointer] = arr[right];
arr[right] = temp;
// 返回第二指针的位置
return pointer;
}
}