算法思想
归并的思想
归并排序(merge-sort)是利用归并的思想实现的排序算法,该方法采用经典的分治(divide-and-conquer)策略:分治法将问题分(divide)成一些小的问题,然后递归求解。而治(conquer)的阶段则将分的阶段得到的各答案“修补”在一起。
=>即:分而治之。
实现要点
归并算法的实现,分为两个过程:分、合
- “分”的过程:
=>也就是实现递归调用的过程。
1)确定递归的终止条件:
left >= right
只有待划分序列的左侧索引 left ,小于右侧索引 right时,才能保证待划分序列有1个以上的元素。也就才有递归划分的必要。 - 合并的过程的思路是这样的:
------主要分为3大步,进行相应的操作:
1、在arr[]中,定义相应的下标l=left、r=middle+1,开始对arr进行遍历,遍历的条件是:l<=middle && r<=right。
=>即:对左右序列同时进行遍历,依次取出其中较小的元素,并将该值存入到temp临时数组中去。直到左右序列中至少有一个已经被遍历完
2、将没有遍历完的序列中,剩下的元素,按照顺序,填充到temp临时数组中去(因为既然是由上一步的归并操作得到的序列,那么,在该序列内部一定是有序的)
3、将temp临时数组中的元素,copy到arr[]中的对应区域内。
=>需要注意的是:并不是每次都将整个的temp临时数组中的元素都copy到arr[]中!每次实际上,只拷贝本次合并的元素!!! - 因为归并的思想是:分治的思想----先分后治
=>所以,在实现的时候,采用的是:
对于一段待排序序列,先实现左右递归,进行划分,然后再实现对于该段序列的“合并”(也就是对该段序列排序的过程)。
算法实现
1、合并的过程
//合并的过程
/**
*
* @param arr 需要进行归并排序的数组
* @param left 合并的序列的最左侧元素的下标
* @param middle 合并的序列的中间值:middle = (left + right)/2
* @param right 合并的序列的最右侧元素的下标
* @param temp 合并过程中,用到的临时数组
*/
public static void merge(int[] arr,int left,int middle,int right,int[] temp) {
int l = left;//左边序列的起始下标
int r = middle + 1;//右边序列的起始下标
int t = 0;//临时数组temp[]的索引,temp[]每次都是从下标为0处开始存储(方便)
//1、在arr[]中,定义相应的下标l=left、r=middle+1,开始对arr进行遍历,遍历的条件是:l<=middle && r<=right。
while(l<=middle && r<=right) {
if(arr[l] < arr[r]) {
//如果l对应的元素小,将它加入到temp[]中去
temp[t] = arr[l];
t += 1;
l +=1;
}else {
//r对应的元素小,或与l对应的元素相等
temp[t] = arr[r];
t += 1;
r += 1;
}
}
//2、将没有遍历完的序列中,剩下的元素,按照顺序,填充到temp临时数组中去
while (l<=middle) {
//左边序列没有遍历完
temp[t] = arr[l];
t += 1;
l += 1;
}
while(r<=right) {
//右边的序列没有遍历完
temp[t] = arr[r];
t += 1;
r += 1;
}
//3、将temp临时数组中的元素,copy到arr[]中的对应区域内
/*
* 将元素copy回去的时候,可以依据arr[]中元素的下标,
* 进行合并操作的下标的总范围,所包含的元素个数,就是需要从temp[]向arr[]中copy的元素个数
*/
t = 0;
while(left <= right) {
arr[left] = temp[t];
left += 1;
t += 1;
}
}
2、划分的过程(在划分结束之后,调用 合并的过程)
//分+调用“合”的过程
/**
* 这是实现递归调用(对应“分”)的方法
* @param arr 需要进行归并排序的数组
* @param left 先进行递归划分,再在递归之后进行合并操作 的范围的左侧边界索引
* @param right 右侧边界索引
* @param temp 临时数组,用于 合并的过程
*/
public static void divide(int[] arr,int left,int right,int[] temp) {
//进行递归的条件
if(left < right) {
int middle = (left + right) / 2;
// 向左递归
divide(arr, left, middle, temp);
// 向右递归
divide(arr, middle + 1, right, temp);
// 进行“合并”的过程
merge(arr, left, middle, right, temp);
}
}