有苦有乐的算法 --- 小和问题

题目

一个无序数组,获取每个元素在左面比这个元素小的个数,求这些个数的和
例:
[3,2,5,1,4] ==> 6
[4,7,0,8,2,4,9,4] ==> 14

解析

准备一个无序数组,

将这个数组平均分成两部分,再把每一部分在平均分成两部分,以此类推,直到分成只剩一个数为止;

最右侧的[1,4]先合并,[1]为左部分,[4]为右部分,合并时左部分的1比右部分的4小,小和个数+1;

之后[3,2]合并,[3]为左部分,[2]为右部分,合并时左部分的[3]不比右部分的[2]小,小和个数不加;
[5,1,4]合并,[5]为左部分,[1,4]为右部分,合并时左部分[5]先和右部分的[1]比,右侧小,小和个数不加,[1]拿走,左部分[5]先和右部分的[4]比,右侧小,小和个数不加,再拿[5];

最后[2,3]和[1,4,5]合并,[2,3]为左部分,[1,4,5]为右部分,[2]比[1]大,小和个数不加[1]拿走;[2]比[4]小,右部分剩几个小和个数加多少(+2),[2]拿走;[3]比[4]小,右部分剩几个小和个数加多少(+2),[3]拿走;左部分没有数据右侧全部拿走

到此得到了所有小和个数

代码

public static int process(int[] arr, int l, int r) {
	if (l == r) {
		return 0;
	}

	int mid = l + ((r - l) >> 1);
	
	return  process(arr, l, mid) + process(arr, mid + 1, r) + merge(arr, l, mid, r);
}

public static int merge(int[] arr, int L, int m, int r) {
	int[] help = new int[r - L + 1];
	int i = 0;
	int p1 = L;
	int p2 = m + 1;
	int res = 0;
	while (p1 <= m && p2 <= r) {
		res += arr[p1] < arr[p2] ? (r - p2 + 1) * arr[p1] : 0;
		help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
	}
	while (p1 <= m) {
		help[i++] = arr[p1++];
	}
	while (p2 <= r) {
		help[i++] = arr[p2++];
	}
	for (i = 0; i < help.length; i++) {
		arr[L + i] = help[i];
	}
	return res;
}
posted @ 2022-03-07 18:03  叕叕666  阅读(45)  评论(0)    收藏  举报