java从《《递归函数》》到《《归并排序》》再到《《最小和问题(归并排序的应用)》》:

一:我们首先来研究一下递归函数(使用递归函数求数组最大值):

我们开始把数组分为两半,分别找出最大值,那么这个最大值就是最后的最大值:同时我们左右两边继续细分,停止条件就是细分到单个数值为止。

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;
	}
}

控制台:

猜你喜欢

转载自blog.csdn.net/Handsome2013/article/details/83472434