归并排序的原理:将数据一分为2,一直分到不可再分,让左边的数据有序,右边的数据也有序,然后两边合并
public class Demo {
public static void main(String[] args) {
int[] nums={98,45,23,31,2,66,88};
//一般归并是写在方法中的,所以要先判断数组中的元素个数,如果是空或1,那么不必排序
if(nums.length<=1){
return;
}
//开始下标
int start = 0;
//结束下标
int end = nums.length-1;
//将数组start与end之间的数据进行递归
process(nums,start,end);
//输出打印数组
print(nums);
}
public static void print(int[] nums) {
for(int i = 0;i<nums.length;i++){
System.out.print(nums[i]+"\t");
}
}
/**
* 归并
* @param nums :要进行递归的数组
* @param start :传入要进行递归的数组的开始下标
* @param end :传入要进行递归的数组的结束下标
* */
public static void process(int[] nums, int start, int end) {
//如果开始下标和结束下标一致,那么就返回
if(start == end){
return;
}
//中点
int middle = start + ((end - start)>>1);
//左边有序
process(nums,start,middle);
//右边有序
process(nums,middle+1,end);
//合并
merge(nums,start,middle,end);
/*
* 将一个数组先一分为2进行递归,但因为刚开始一分为2的时候开始下标不等于结束下标,所以一直递归到单数
* 分析:
* 设数组元素为 a b c d e
* 主函数调用递归: a,b,c d,e(这个时候左边已经有序,该执行右边有序的递归函数,执行过程如左边一致)
* 左边有序: a,b c(merge(nums,0,1,2),merge完成之后,结束当前递归,返回到上一级)
* 左边有序: a b(merge(nums,0,0,1),merge完成之后,结束当前递归,返回到上一级)
* 当元素个数为单个的时候,结束当前递归,此时进行merge函数的调用
* */
}
/**
* 合并并排序
* @param nums :要进行合并的数组
* @param start :要进行合并的元素的范围的开始下标
* @param middle :要进行合并的元素的范围的中间下标
* @param end :要进行合并的元素的范围的结束下标
* */
public static void merge(int[] nums, int start, int middle, int end) {
//定义一个长度为end+1的数组,因为end是结束下标,所以数组长度为end+1
int[] newArray = new int[end+1];
/**
* 因为要进行merge函数的第一个范围一定是【0-1】
* 每次排序是要把开始下标-结束下标之间的数排好
* 所以i一定要等于start
* */
int i = start;
//左边部分的指针
/**
* 因为是要比较左边部分和右边部分的大小
* 若左边指针比右边指针所指的数大或小,左边指针或右边指针进行++操作,
* 若左边指针指向0,每次归并的时候都是拿0下标的数和右边指针进行比较,会出现真正需要排序比较的部分没有和右边指针进行排序
* 所以左边指针不能指向0
* */
int tempLeft = start;
//右边部分的指针
int tempRight = middle+1;
//从小到大归并
while(tempLeft<=middle && tempRight<=end){
newArray[i++] = nums[tempLeft] <= nums[tempRight] ? nums[tempLeft++]:nums[tempRight++];
}
//若右边部分已经排好序,左边还有剩余,则将剩下的元素移下来
while(tempLeft<=middle){
newArray[i++]=nums[tempLeft++];
}
while(tempRight<=end){
newArray[i++]=nums[tempRight++];
}
//哪部分进行了排序将哪部分正确的顺序赋值给原数组
for(i=start;i<newArray.length;i++){
nums[i]=newArray[i];
}
}
}
归并排序的时间复杂度分析:
忽略打印输出,归并排序只需要一个递归函数和一个merge合并函数,递归函数是自己调自己,merge函数只进行了一个比较并赋值的操作
归并函数的时间复杂度是:T(N)=2*T(N/2)+O(1)+O(N)
- 2*T(N/2):递归函数里面的两个自己调自己部分
- 1:递归函数自己调自己上面的判断和定义中点变量所耗费时间复杂度
- N:merge函数所耗费时间复杂度,因为merge函数就定义了两个指针,两个指针都是从左到右进行比较并赋值给原数组,所以是2个O(N),忽略系数
符合递归函数的公式,所以归并函数的时间复杂度是O(N*logN)