【校招VIP】归并排序

05月12日 收藏 0 评论 1 java开发

【校招VIP】归并排序

转载声明:文章来源https://blog.csdn.net/weixin_45857153/article/details/110474615

一、归并排序的介绍

基本介绍

归并排序(MERGE- SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治( divide-
and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案修补”在一起,即分而治之)。

基本思想

1.把数组从中间划分成两个子数组;
2.一直递归地把子数组划分成更小的子数组,直到子数组里面只有一个元素
3.依次按照递归的返回顺序,不断地合并排好序的子数组,直到最后把整个数组的顺序排好。

看看治阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现的详细步骤

二、代码实现思想

按图所示实现分解方法

//int arr[]={8,4,5,7,1,3,6,2};
//int temp[]=new int[arr.length];
//分解方法
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
if (left < right) {
int mid = (left + right) / 2; //中间索引
//向左递归进行分解
//0 - mid 即是0 - 3 {8,4,5,7}
mergeSort(arr, left, mid, temp);
//向右递归进行分解
//mid+1 - midright 即是4 - 7 {1,3,6,2}
mergeSort(arr, mid + 1, right, temp);
}
}

按图所示实现合并方法

//合并的方法
/**
*
* @param arr 排序的原始数组
* @param left 左边有序序列的初始索引
* @param mid 中间索引
* @param right 右边索引
* @param temp 做中转的数组
**/
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
//初始化i,左边有序序列的初始索引
int i = left;
//初始化j,右边有序序列的初始索引
//为什么要mid+1?
//因为假设数组arr{1,3,5,6,2,4} mid=(left+right)/2 = 2
//此时左边i=left mid左边的就是 0 - mid 即是{1,3,5}
//此时右边就是mid+1 - right 即是{6,2,4}
int j = mid+1;

int t = 0;//指向temp数组的当前索引

//(-)
//先把左右两边(有序)的数据按照规则填充到temp数组
//直到左右两边的有序序列,有一边处理完毕为止
//i <= mid 代表左边有序序列有值
//j <= right 代表右边有序序列有值
while (i <= mid & j <= right) {//继续

//如果左边的有序序列的当前元素,小于等于右边有序序列的当前元素
//即将左边的当前元素,拷贝到temp数组
//假设数组arr{1,3,5,6,2,4}
//左边 0 - mid 即是{1,3,5}
//右边 mid+1 -right 即是{6,2,4}
//若arr[i]<= arr[j] 即是1 <= 6
if (arr[i] <= arr[j]) {
temp[t] = arr[i];//temp[0]=arr[i];
t += 1;//指向temp数组下一位
i += 1;//指向左边下一位arr[i+1]...
}else{
//反之arr[i] >= arr[j] 左边大于右边
//则进行右边赋值给temp数组
temp[t] = arr[j];//temp[0]=arr[i];
t += 1;//指向temp数组下一位
j += 1;//指向右边边下一位arr[j+1]...
}
}

//(二)
//把有剩余数据的一边的数据依次全部填充到temp
//左边的有序序列还有剩余的元素,就全部填充到temp
while( i <= mid){
temp[t] = arr[i];
t += 1;
i += 1;
}
//右边的有序序列还有剩余的元素,就全部填充到temp
while( j <= right){
temp[t] = arr[j];
t += 1;
j += 1;
}

//(三)
//将temp数组的元素拷贝到arr
//为什么 t=0 ?
//因为合并的时候按图所示数组:{8,4,5,7,1,3,6,2}
//最先进入的是84 left=0 right = 1
//经过上面的左边与右边比较,得出temp数组:4,8
// 此时清空指向temp数组的下标指针t 重新回到0
//tempLeft = 0 进行将temp数组里的4,8 赋值给arr数组
t = 0;
int tempLeft= left;
while( tempLeft <= right){
arr[tempLeft]=temp[t];
t += 1;//赋值成功后指向temp数组的下标指针t往后移
tempLeft +=1;//84 完成后到57 此时left=2 right = 3 ...
}
}

分+合实现归并排序

//int arr[]={8,4,5,7,1,3,6,2};
//int temp[]=new int[arr.length];
//mergeSort(arr,0,arr.length-1,temp);
//System.out.println("并归排序后"+ Arrays.toString(arr));
//分解方法
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
if (left < right) {
int mid = (left + right) / 2; //中间索引
//向左递归进行分解
//0 - mid 即是0 - 3 {8,4,5,7}
mergeSort(arr, left, mid, temp);
//向右递归进行分解
//mid+1 - midright 即是4 - 7 {1,3,6,2}
mergeSort(arr, mid + 1, right, temp);
//进行合并
merge(arr,left,mid,right,temp);
}
}

运行结果如下:
tempLeft:0 rigth:1
tempLeft:2 rigth:3
tempLeft:0 rigth:3
tempLeft:4 rigth:5
tempLeft:6 rigth:7
tempLeft:4 rigth:7
tempLeft:0 rigth:7
并归排序后[1, 2, 3, 4, 5, 6, 7, 8]

三、复杂度分析

时间复杂度: T(n)
归并算法是一个不断递归的过程,假设数组的元素个数是n。
时间复杂度是T(n)的函数: T(n) = 2*T(n/2) + O(n)

怎么解这个公式呢?
对于规模为n的问题,一共要进行log(n)层的大小切分;
每一层的合并复杂度都是O(n);
所以整体的复杂度就是O(nlogn)

空间复杂度: O(n)
由于合并n个元素需要分配一个大小为n的额外数组,合并完成之后,这个数组的空间就会被释放。

C 1条回复 评论
一只小鹿哈

太好了,明了易懂,感谢

发表于 2022-08-03 23:00:00
0 0