1、分治法思想
分治思想主要通过递归来实现,每层递归中主要包括三个步骤:
分解:即将原问题划分为若干子问题,子问题的形式要保证和原问题一致,但规模更小。
解决:当划分子问题的规模足够小时,停止递归,求解子问题,获取子问题的解决结果并返回给上一层。
合并:将子问题的解决结果进行合并,得到原问题的解。
也就是说,分治思想就是将一个大问题用过递归层层分解为无数个子问题,这些子问题要保证与与原问题是一个类型,只不过规模更小更加容易求解,但求解过程相同。
2、归并排序算法思想
1、思考:如果给了我们两个已经做好排序的数组(相同排序规则)A和B,我们该如何将其合并到一个有序数组C?
这个问题很简单,依次从两个数组中取元素比较然后放进C中即可。而如果A、B数组都只有一个元素时,他们都成为了一个有序数组,则可以直接进行比较放在数组C中,返回有序的数组C。
2、通过分治思想对无序数组排序问题进行划分:对于一个无序的数组array
(1)分解:通过递归将无序数组进行划分,每次从array.length/2处划分为两个数组,直到划分后的子数组规模为1。
(2)解决:当子数组规模为1时,此时的最小规模数组已经自动变为一个有序数组,递归终止返回有序数组结果。
(3)合并:接收下一层递归所返回的两个有序子数组,将这两个有序子数组的数据进行合并为一个新的有序数组,然后返回该有序数组给上一层递归。
3、排序实例:为方便理解递归划分数组,可以建立一个递归树形图(理解递归比较方便)。以数组int[] arr = {3,23,4,13,4,5,63,6,2}为例,其数组划分树形图为
那么接下来就是将叶子节点已经排序好的数组返回给上一层递归,在上一层中处理合并一个新的有序数组再返回给上一层,重复该步骤直到根节点层就可以将原数组变为一个有序数组。
也就是说,归并算法中递归调用的方法主要为两部分,分别是分割数组以及数组合(将两个有序数组进行合并)。伪代码如下
merge(int[] arr){
if(arr.length < 2){
return arr;//如果传入的数组长度为1那么就直接返回
}
int[] left = splitLeft(arr);//划分获取左子数组
int[] right = spliteRight(arr);//划分获取右子数组
left = merge(left);//递归调用,获得排序后的左子数组
right = merge(right);//递归调用,获得排序后的右子数组
arr = sort(left,right);//将左右有序子数组合并为一个有序数组
return arr;//返回
}
具体代码实现(个人写的依据算法逻辑思想写的一个示例,划分左右子数组都是新创建数组进行赋值,空间复杂度很高,而且提高了时间复杂度,但是主要是便于理解)
个人实现:
/**
*
* @Description:分治算法,利用递归,将一个数组元素不断的分割为两个数组,直到数组分割到只剩下一个元素
* 然后返回给上一层,主要是保证返回给上一层中的两个数组是已经排好序的数组,在上一层中进行排序合并,然后再返回
* 就可以最终得到一个有序数组
*/
public class Sort {
public static int[] merge(int[] arr){
int left = 0;
int right = arr.length;
if (right == 1) {
int[] endArr = {arr[left]};
return endArr;
}
//数组划分
int mid = (left + right) / 2;
int[] leftArr = new int[mid-left];
int[] rightArr = new int[right - mid];
for (int i = 0; i <= mid-1; i++) {
leftArr[i] = arr[i];
}
for (int i = 0,j=mid; i < rightArr.length && j < arr.length; i++,j++) {
rightArr[i] = arr[j];
}
//递归调用,获取排序完成的左右子数组
leftArr = merge(leftArr);
rightArr = merge(rightArr);
//合并两个数组并排序
return sort(arr, leftArr, rightArr);
}
public static int[] sort(int[] arr, int[] left, int[] right){
int i = 0;
int j = 0;
for (int index = 0; index < arr.length; index++){
if (i >= left.length && j < right.length) {
arr[index] = right[j];
j++;
continue;
}
if (j >= right.length && i < left.length) {
arr[index] = left[i];
i++;
continue;
}
if (left[i] > right[j]){
arr[index] = right[j];
j++;
} else {
arr[index] = left[i];
i++;
}
}
return arr;
}
public static void main(String[] args) {
int[] arr = {3,23,4,13,4,5,63,6,2};
for(int i:merge(arr)) {
System.out.println(i);
}
}
}
较好的实现:
public class Solution {
/*
* @param A:an integer array
* @return:
*/
public voidsortIntegers2(int[] A) {
// writeyour code here
//利用归并排序对数组A进行排序
mergeSort(A,0,A.length-1);
}
//归并排序
public void mergeSort(int[] A,int start,int end){
if(start>=end) return;
int middle= (start+end)/2;
mergeSort(A,start,middle);
mergeSort(A,middle+1,end);
//归并排序需要分配的临时数组
//这是归并排序的核心
int []temp = new int[end-start+1];
inti=start;
int j =middle+1;
intindex=0;
while(i<=middle&&j<=end){
if(A[i]<=A[j]){
temp[index++] = A[i++];
}else{
temp[index++] = A[j++];
}
}
while(i<=middle){
temp[index++] =A[i++];
}
while(j<=end){
temp[index++] =A[j++];
}
i=start;
index = 0;
for(;i<=end;i++){
A[i] =temp[index++];
}
}
}