一:我们首先来研究一下递归函数(使用递归函数求数组最大值):
我们开始把数组分为两半,分别找出最大值,那么这个最大值就是最后的最大值:同时我们左右两边继续细分,停止条件就是细分到单个数值为止。
package chapter1;
//使用递归求出一个数组中的最小值
public class FindMax {
public static void main(String[] args) {
int[] arr = { 1, 2, 5, 3, 2 };
int result = findMax(arr, 0, arr.length - 1);
System.out.println(result);
}
public static int findMax(int[] arr, int start, int len) {
if (start == len) {//首先是找到终止条件
return arr[start];
}
int mid = (len + start) / 2;
int LMax = findMax(arr, start, mid);//左边出去,像一个树形结构一样不断细分出去
int RMax = findMax(arr, mid + 1, len);//右边出去,像一个树形结构一样不断细分出去
//经历过了上边的代码,当走当了了if里面的时候,才会结束那个细分,然后我们的细分结构了全部完成了,
//分成了很多细分支,那么接下来就是对每个细分部分进行我们想要的操作,就是下边的return语句。
//每一个细分都有这个语句,那么我们从细到粗进行return语句操作
return Math.max(LMax, RMax);
}
}
控制台打印:
二:下边来分析我们归并排序:
归并排序可以分为两个部分,一个就是递归(就是不断的细分)
// 首先是递归操作,也就是我们的大的一个函数
public static void mergeSort(int[] arr, int start, int end) {
// 首先是终止条件
if (start == end) {
return;
}
int mid = start + ((end - start) >> 1);
mergeSort(arr, start, mid);// 左边细分出去
mergeSort(arr, mid + 1, end);// 右边细分出去
// 下边就是每一个细分需要进行的哪些操作,就是将细分的部分排序,然后替换数组中原有的部分
merge(arr, start, end);
}
第二个就是合并。
那么我们合并的时候就是假设是在处理数组的两个部分,同时这两个部分是分别有序的,然后我们的任务就是吧两个有序的数组合成一个有序的数组,比如,1,3,5和2,4,6合并完就是1,2,3,4,5,6。
public static void merge(int[] arr, int start, int end) {
// TODO Auto-generated method stub
int i = 0;//help数组的下标
int[] help = new int[end - start + 1];
int p1 = start;
int mid = start + ((end - start) >> 1);
int p2 = mid + 1;
// 下边就是p1,p2往后移动,然后一个一个比较(这里我们是把一个数组分两边,假设两边都分别排好序了,这里就是将两个已经排好序的进行合并一起
while (p1 <= mid && p2 <= end) {
help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
// 如果跳出了while循环就说明两边至少有一边越界了,那么我们就把还没越界的那边就添加到help后边即可
while (p1 <= mid) {
help[i++] = arr[p1++];
}
while (p2 <= end) {
help[i++] = arr[p2++];
}
// 经历过了上边几个while循环过后,我们的help就是我们已经排序过后的数组,那么我去原数组替换这部分即可(用有序的help去替换无序的原始部分)
for (int j = 0; j < help.length; j++) {
arr[start + j] = help[j];
}
}
完整测试代码如下:
package chapter1;
//归并排序
public class MergeSort {
public static void main(String[] args) {
System.out.println("排序前");
int[] arr = { 1, 2, 6, 5, 3 };
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
}
mergeSort(arr, 0, arr.length - 1);
System.out.println();
System.out.println("排序后");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]);
}
}
// 首先是递归操作,也就是我们的大的一个函数
public static void mergeSort(int[] arr, int start, int end) {
// 首先是终止条件
if (start == end) {
return;
}
int mid = start + ((end - start) >> 1);
mergeSort(arr, start, mid);// 左边细分出去
mergeSort(arr, mid + 1, end);// 右边细分出去
// 下边就是每一个细分需要进行的哪些操作,就是将细分的部分排序,然后替换数组中原有的部分
merge(arr, start, end);
}
public static void merge(int[] arr, int start, int end) {
// TODO Auto-generated method stub
int i = 0;
int[] help = new int[end - start + 1];
int p1 = start;
int mid = start + ((end - start) >> 1);
int p2 = mid + 1;
// 下边就是p1,p2往后移动,然后一个一个比较(这里我们是把一个数组分两边,假设两边都分别排好序了,这里就是将两个已经排好序的进行合并一起
while (p1 <= mid && p2 <= end) {
help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
// 如果跳出了while循环就说明两边至少有一边越界了,那么我们就把还没越界的那边就添加到help后边即可
while (p1 <= mid) {
help[i++] = arr[p1++];
}
while (p2 <= end) {
help[i++] = arr[p2++];
}
// 经历过了上边几个while循环过后,我们的help就是我们已经排序过后的数组,那么我去原数组替换这部分即可(用有序的help去替换无序的原始部分)
for (int j = 0; j < help.length; j++) {
arr[start + j] = help[j];
}
}
}
控制台:
三:归并排序的应用:
比如我们针对第一个数,后边如果有k个数比第一个数大,小和需要计算k次
法一:使用复杂度n*n方式,就是两个for循环来求解
package chapter1;
public class MinSum2 {
public static void main(String[] args) {
int[] arr = { 1, 3, 4, 2, 5 };
int result = 0;
// 搞两个for循环
for (int i = 0; i < arr.length - 1; i++) {
int n = 0;// 记录前边有几个数比当前i位置数字要大
for (int j = i + 1; j < arr.length; j++) {
if (arr[i] < arr[j]) {
n++;
}
}
result += n * arr[i];
}
System.out.println(result);
}
}
控制台如下:
法二:使用归并排序方式:复杂度n*log(n)
比如我们最开始细分到最底端,然后每次合并其实都是有计算我们的小和,只是开始是计算我们小组内的,然后合并大范围的时候,其实我们的小组内的小和已经求过了,下边只是求解合并时候右边小组对于左边小组会产生的小和。所以每次都是批次处理,而不是只处理一个数
package chapter1;
import java.util.concurrent.SynchronousQueue;
public class MinSum {
public static void main(String[] args) {
int[] arr = { 1, 3, 4, 2, 5 };
int result = mergeSort(arr, 0, arr.length - 1);// (注意)这里很容易搞成arr.length
System.out.println(result);
}
public static int mergeSort(int[] arr, int start, int end) {
// 结束条件
if (start == end) {
return 0;// 这里就是细分到每一个数据的时候,最小和是0
}
int mid = start + ((end - start) >> 1);
return mergeSort(arr, start, mid) + mergeSort(arr, mid + 1, end) + merge(arr, start, end);
}
private static int merge(int[] arr, int start, int end) {
// TODO Auto-generated method stub
int mid = start + ((end - start) >> 1);
// 首先定义两个指针
int p1 = start;
int p2 = mid + 1;
// 定义一个help数组以及一个下标i
int[] help = new int[end - start + 1];
int i = 0;
int result = 0;
while (p1 <= mid && p2 <= end) {// 如果都没越界,就一直循环
result += arr[p1] < arr[p2] ? arr[p1] * (end - p2 + 1) : 0;
help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
while (p1 <= mid) {
help[i++] = arr[p1++];
}
while (p2 <= end) {
help[i++] = arr[p2++];
}
return result;
}
}
控制台: