归并排序
介绍
归并排序(MERGE SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide- and- conquer)策略(分治法将问题分(divide) 成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案”修补”在一起,即分而治之)。
也就是该算法的核心思想是分治思想
动态图解
我们发现我们的分并没有做什么其他的功能,只是将我们的数组拆分开来
为我们下面的治提供条件
我们再来看看他的复杂度,我们是8个数据,但是合并总共是7次
所以说他的复杂度是线性增长,不像其他的拍需要算法那样是平方增长
我们看最后一次合并做了什么
也就是说我们的归并排序是需要一个辅助数组的,这也是我们代码中最复杂的一部分
代码实现
package 数据结构;
import java.lang.reflect.Array;
import java.util.Arrays;
//归并排序
//@author 王庆华
public class MergetSort {
public static void main(String[] args) {
//创建数组
int arr[] = {
8,4,5,7,1,3,6,2};
int temp[] = new int[arr.length];//归并排序需要一个额外的空间的
megeSort(arr, 0, arr.length-1, temp);
System.out.println("归并排序后"+Arrays.toString(arr));
}
//分解+合并方法
public static void megeSort(int[] arr,int left,int right, int[] temp){
if(left<right){
int mid = (left+right)/2;//中间索引
//向左递归进行分解
megeSort(arr, left, mid, temp);
//向右递归分解
megeSort(arr, mid+1, right, temp);
//每分解一次就合并一次
merge(arr, left, right, mid, temp);
}
}
//合并的方法
/**
*
* @param arr 需要排序的初始的数组
* @param left 左边有序子序列的初始索引
* @param right 右边有序子序列的索引
* @param mid 中间索引
* @param temp 辅助数组
*/
public static void merge(int[] arr,int left,int right,int mid,int[] temp){
int i = left;//初始化i 表示左边有序子序列的初始索引
int j = mid + 1;//j是我们右边子序列的初始索引
int t = 0; //这个是我们辅助数组的当前索引,来帮助我们在那个地方插入值
//(1)先把左右两边(有序)的数据按照规则填充到辅助数组temp中
//直到左右两边的有序序列有一边处理完毕为止
while(i <= mid && j <= right){
if(arr[i] <= arr[j]){
//左边有序子序列的当前元素<=右边有序序列的当前元素
//左边的元素填充到辅助数组
temp[t] = arr [i];
t+=1;
i+=1;
}else{
//反之就是右边进行拷贝操作
temp[t] = arr[j];
t+=1;
j+=1;
}
}
//(2)
//把有剩余数据的一边,依次填充到辅助数组中
while(i <= mid){
//说明左边有序子序列有剩余元素
temp[t] = arr[i];
t+=1;
i+=1;
}
while(j <= right){
//右边有序子序列有剩余
temp[t] = arr[j];
t+=1;
j+=1;
}
//(3)将辅助数组中的数据拷贝到arr中
//注意并不是每一次都是拷贝所有的元素
t = 0;
int tempLeft = left;//
while(tempLeft <= right){
//第一次合并时 tempLeft=0 , right = 1
//第二次 tempLeft = 2 riht = 3
//最后一次tempLeft = 0 ,right = 7
arr[tempLeft] = temp[t];
t+=1;
tempLeft+=1;
}
}
}
我们需要注意的是,拷贝数组的时候我们是用递归的,也就是说我们一开始是两个两个合并,所以辅助数组一开始不是直接拷贝8个数据的,也就是我们的tempLeft和right并不是0和7,而是0 1 2 3 然后合并成为0 3 也就是前四个数据,后面肯定会有4 7 合并,最后才是我们的0 7 整个数组