基于分治思想的 Java 算法题练习,涵盖快速排序和归并排序的应用。内容包括颜色分类、排序数组、第 K 大元素、最小 K 个数、逆序对及翻转对等经典问题。通过随机基准值优化快排性能,利用归并过程统计逆序对数量,并解决右侧小元素计数问题。文章提供了完整的 Java 代码实现及复杂度分析,适合算法进阶学习。
题目解析:给一个数组让我们排序。分治:使用快速排序,找一个 key 基准值,根据基准值,让其数组变成三个模块,左边模块<key,中间模块=key,右边模块>key,这时候在对左右两边的模块,分别进行找基准值进行排序,左右两边每一个模块又可以分为这三个部分,就这样不断排序最终结果就变成有序的了。key 基准值如何取?这里采用随机取,随机其对应区间下标 r = new Random().nextInt(right - left + 1) + left,这样整个下标区间的取值就是 [left,right] 了。
classSolution {
publicint[] sortArray(int[] nums) {
qsort(nums, 0, nums.length - 1);
return nums;
}
publicvoidqsort(int[] nums, int l, int r) {
if (l >= r) {
return;
}
// 这里采用随机获取 key// 随机的下标是 [l,r];intkey= nums[newRandom().nextInt(r - l + 1) + l];
// 这里根据 key 将其数组分三块进行排序intleft= l - 1;
intright= r + 1;
inti= l;
while (i < right) {
if (nums[i] < key) {
swap(nums, ++left, i++);
} elseif (nums[i] == key) {
i++;
} else {
swap(nums, --right, i);
}
}
qsort(nums, l, left);
qsort(nums, right, r);
}
publicvoidswap(int[] nums, int i, int j) {
inttem= nums[i];
nums[i] = nums[j];
nums[j] = tem;
}
}
数组中第 K 个最大元素
题目解析:给了一个数组,找出其中第 K 个大的元素。快速排序,根据上一题的快速排序,这里我们仍然使用快排,但是这里不一样的是,快速排序以后将数组分为三个模块,这里我们可以判断其第 K 个大的元素在那个模块,此时我们根据其每一个模块元素个数进行判断,在一个模块中,只需要在这一个模块中找即可,剩下的两个模块就不用管了。
classSolution {
publicintfindKthLargest(int[] nums, int k) {
return qsort(nums, 0, nums.length - 1, k);
}
publicintqsort(int[] nums, int l, int r, int k) {
if (l == r) {
return nums[l];
}
intkey= nums[newRandom().nextInt(r - l + 1) + l];
intleft= l - 1;
intright= r + 1;
inti= l;
while (i < right) {
if (nums[i] < key) {
swap(nums, ++left, i++);
} elseif (nums[i] == key) {
i++;
} else {
swap(nums, --right, i);
}
}
// 这里将其分为了 [l,left] [left+1,right-1] [right,r]// 判断其第 k 大的元素在那个模块intb= right - left - 1;
intc= r - right + 1;
if (c >= k) {
// 只在右边找即可return qsort(nums, right, r, k);
} elseif ((b + c) >= k) {
return key;
} else {
return qsort(nums, l, left, k - b - c);
}
}
publicvoidswap(int[] nums, int i, int j) {
inttem= nums[i];
nums[i] = nums[j];
nums[j] = tem;
}
}
最小的 K 个数
题目解析:返回数组中最小的 k 个数。快速选择算法 + 随机获取 key 值。这题和上一题找出第 k 个最大的数类似,唯一不同的就是排序后三个模块如果处理,因为这里最小的 k 个数顺序是随机的,也是每次只需要调整一个模块即可,剩下的两个模块不用管了。
classSolution {
publicint[] smallestK(int[] arr, int k) {
// 快速选择算法
qsort(arr, 0, arr.length - 1, k);
int[] ret = newint[k];
for (inti=0; i < k; i++) {
ret[i] = arr[i];
}
return ret;
}
publicvoidqsort(int[] arr, int l, int r, int k) {
if (l >= r) {
return;
}
intkey= arr[newRandom().nextInt(r - l + 1) + l];
intleft= l - 1;
intright= r + 1;
inti= l;
while (i < right) {
if (arr[i] < key) {
swap(arr, ++left, i++);
} elseif (arr[i] == key) {
i++;
} else {
swap(arr, --right, i);
}
}
// [l,left] [left + 1, right - 1],[right , r]inta= left - l + 1;
intb= right - left - 1;
if (a > k) {
qsort(arr, l, left, k);
} elseif (a + b >= k) {
return;
} else {
qsort(arr, right, r, k - a - b);
}
}
publicvoidswap(int[] arr, int i, int j) {
inttem= arr[i];
arr[i] = arr[j];
arr[j] = tem;
}
}