📅  最后修改于: 2023-12-03 14:58:32.778000             🧑  作者: Mango
GATE-IT-2004是印度计算机协会在2004年组织的一项全国性计算机科学考试,是印度考取计算机相关学位课程的重要指标之一。本题目位于第86章,测试程序员对数据结构和算法的熟练程度。
给定一个由n个整数组成的数组a[1...n],设计一个算法,找到所有满足i < j < k同时满足a[i] < a[k] < a[j]的(i,j,k)的总数。时间复杂度应该为O(nlogn)或O(n),空间复杂度应该为O(n)。
首先,可以使用暴力枚举的方法,将每一个i,j,k情况都枚举一遍,判断是否符合条件,但是时间复杂度达到O(n^3),显然会TLE。
接着,可以使用一种类似于归并排序的方法,通过在归并的同时计算结果。对于每个区间[l,r],首先求出左半边[1,mid]和右半边[mid+1,r]的答案,然后再求出跨越[left,mid]和[mid+1,right]的答案。最后将上述三个数加起来就是该区间的答案。对于符合条件的数对(i,j,k),假设i和k已经来自左半边,j已经来自右半边,考虑如何统计满足条件的(i,j,k)数目。设p和q是左右半边都是从小到大排序后的位置指针。初始时,p指向最左边,q指向fist+1位置。此时考虑当前q能与p形成多少个数对。如果a[q]>=a[p],那么p可以向右移动到中间点,此时j∈(mid,q]都是符合条件的数,k∈[p,mid]也都是符合条件的数对。如果a[q]<a[p],那么q继续向右移动。此时将移动之前,q的前面所有元素都和a[q]可以形成数对,因为这些元素都比a[q]小,其中i都来自左半边,j都来自右半边,k能在左半边中找到。
public class GATE_IT_2004 {
public static void main(String[] args) {
int[] a = { 6, 2, 5, 4, 3, 1 };
int res = getCount(a, 0, a.length - 1);
System.out.println("符合条件的数目为:" + res);
}
/*
* 递归计算包含当前区间a[left...right]的答案
*/
private static int getCount(int[] a, int left, int right) {
if (left >= right) {
return 0;
}
int mid = (left + right) >> 1; // 中断点
int res = getCount(a, left, mid) + getCount(a, mid + 1, right); // 递归计算左右两半的答案
// 记录左半边和右半边排序后的新数列
int[] tmp = new int[right - left + 1];
int p = left, q = mid + 1, k = 0;
while (p <= mid && q <= right) {
// 如果a[q]>=a[p],那么p可以向右移动到中间点
// 此时j∈(mid,q]都是符合条件的数,k∈[p,mid]也都是符合条件的数对
if (a[q] >= a[p]) {
for (int i = mid + 1; i <= q; i++) {
int l = p, r = mid;
while (l <= r) {
int y = (l + r) >> 1;
if (a[y] < a[i]) {
l = y + 1;
} else {
r = y - 1;
}
}
res += mid - l + 1; // 统计跨越j和k的数量
}
tmp[k++] = a[p++];
} else {
// a[q]<a[p],那么q继续向右移动
// 此时将移动之前,q的前面所有元素都和a[q]可以形成数对
// 因为这些元素都比a[q]小,其中i都来自左半边,j都来自右半边,k能在左半边中找到
for (int i = p; i <= mid; i++) {
int l = mid + 1, r = q;
while (l <= r) {
int y = (l + r) >> 1;
if (a[y] > a[i]) {
r = y - 1;
} else {
l = y + 1;
}
}
res += r - mid; // 统计跨越j和k的数量
}
tmp[k++] = a[q++];
}
}
while (p <= mid) {
tmp[k++] = a[p++];
}
while (q <= right) {
tmp[k++] = a[q++];
}
// 将新的数列赋值回原数组
for (int i = 0; i < tmp.length; i++) {
a[left + i] = tmp[i];
}
return res;
}
}