java - k个部分的归并排序
问题描述
我真的很感谢对这项任务的一些帮助。任务是开发一种合并排序算法,将输入数组递归地“拆分”为 k 个数组。我最初编写了两种方法来合并排序,分为两部分,即合并排序和合并。我试图概括这个算法
public class MergeSort {
public static int[] mergeSort(int[] a, int p, int r) {
// p is left bound
// r is right bound
if (p < r) {
int q = (int)Math.floor((p + r) / 2);
mergeSort(a, p, q);
mergeSort(a, q + 1, r);
return merge(a, p, q, r);
} else
return a;
}
// p ist linke grenze
// r ist rechte grenze
public static int[] merge(int[] a, int p, int q, int r) {
int n1 = q - p + 1; //length of first array
int n2 = r - q; //length of second array
int[] lt = new int[n1 + 1];
int[] rt = new int[n2 + 1];
for (int i = 0; i < n1; i++) {
lt[i] = a[p + i];
}
for (int j = 0; j < n2; j++) {
rt[j] = a[q + j + 1];
}
lt[n1] = 1000000000; //sentinels
rt[n2] = 1000000000;
int i = 0;
int j = 0;
for (int k = p; k <= r; k++) { //comparing the values of the arrays and merging
if (lt[i] <= rt[j]) {
a[k] = lt[i];
i++;
} else {
a[k] = rt[j];
j++;
}
}
return a;
}
public static int[] mergeSortK(int[] a, int k, int p, int r) {
// k number of steps; p is first index of array; r is last index of array;
if (p < r) {
int[] pos = new int[k + 1]; //array for saving the indices of the "splits"
for (int i = 0; i <= k; i++) {
pos[i] = (int) Math.floor(p + (r - p) / k * i); //saving the array indices
}
for (int i = 0; i < k; i++) {
mergeSortK(a, k, pos[i], pos[i + 1]); //sorting the arrays
}
for (int i = 0; i < k - 1; i++) {
merge(a, pos[i], pos[i + 1], pos[i + 2]); //merging the arrays pairwise
}
}
return a;
}
public static void main(String[] args) {
// task 2.1.a)
// Example Values:
int[] list = { 2, 1, 5, 6, 2, 12 };
int k = 4;
// use MergeSort
int[] newlist = mergeSortK(list, k, 0, list.length);
printList(newlist);
}
// Helper function to print the elements of a list
private static void printList(int[] list) {
for(int i = 0; i < list.length; i++) {
System.out.println(list[i]);
}
}
}
main 方法中给出的输入导致{2, 1, 2, 5, 6, 12}
非常感谢任何帮助!对不起,如果我做错了,我是来学习的,我真的希望你们能帮助我!
解决方案
你的代码有几个问题:
- 不需要
Math.floor()
截断整数除法的结果,与 Javascript 不同,Java/
对int
参数和浮点参数使用不同的语义。(p + r) / 2
是积分商,但您可能需要编写p + (r - p) / 2
以避免潜在的溢出p + r
。 - 当您
list.length
从main()
. 这实际上是一种非常方便的约定,可以避免+1
在计算切片大小时进行调整。删除那些错误的调整并依赖包含/排除的约定。 - 不要使用哨兵:使用哨兵会阻止您正确排序包含大于或等于哨兵值的值的数组
1000000000
。这种方法没有必要,应该被禁止。只需将索引变量与切片长度进行比较,并在其中一个切片用完时复制剩余的元素。 - 您对切片边界的计算
mergeSortK
不正确:p + (r - p) / k * i
使用整数算术计算,因此(r - p) / k
在乘法之前四舍五入。r
如果r - p
不是 的倍数,则最后一个切片结束索引将不相等k
。在除法之前进行乘法可以解决此问题,但可能会超出 type 的范围int
。 mergeSortK
不进行 k 路合并,而是进行一系列对 . 不够的部分合并k > 2
。- 你的测试集有点小。
这是一个更正的版本:
public class MergeSort {
public static int[] mergeSort(int[] a, int p, int r) {
// p is left bound (included)
// r is right bound (excluded)
if (r - p >= 2) {
int q = p - (r - p) / 2;
mergeSort(a, p, q);
mergeSort(a, q, r);
return merge(a, p, q, r);
} else {
return a;
}
}
// p is left bound (included)
// q is start of right slice
// r is end of right slice (excluded)
public static int[] merge(int[] a, int p, int q, int r) {
int n1 = q - p; // length of first array
int n2 = r - q; // length of second array
int[] lt = new int[n1];
for (int i = 0; i < n1; i++) {
lt[i] = a[p + i];
}
int i = 0; // index into lt
int j = q; // index into a for right slice
int k = p; // index into a for merged list
while (i < n1 && j < r) { //comparing the values of the arrays and merging
if (lt[i] <= a[j]) {
a[k] = lt[i];
i++;
k++;
} else {
a[k] = a[j];
j++;
k++;
}
}
while (i < n1) { // copy remaining elements from right slice
a[k] = lt[i];
i++;
k++;
}
// remaining elements from right slice are already in place
return a;
}
public static int[] mergeSortK(int[] a, int k, int p, int r) {
// k amount of steps; p is first index of slice; r is last index of slice (excluded);
if (r - p >= 2) {
if (k > r - p)
k = r - p;
int[] pos = new int[k + 1]; //array for saving the indices of the "splits"
for (int i = 0; i <= k; i++) {
pos[i] = p + (r - p) * i / k; //saving the array indices
}
for (int i = 0; i < k; i++) {
mergeSortK(a, k, pos[i], pos[i + 1]); //sorting the arrays
}
while (k > 1) {
int i, n = 1;
for (i = 0; i < k - 1; i += 2) {
// merge slices 2 at a time: this will produce the expected output
// but is not a direct k-way merge.
merge(a, pos[i], pos[i + 1], pos[i + 2]);
pos[n++] = pos[i + 2];
}
if (i < k)
pos[n++] = pos[i + 1];
k = n - 1;
}
}
return a;
}
public static void main(String[] args) {
// task 2.1.a)
// Example Values:
int[] list = {
64, 36, 46, 31, 45, 52, 4, 48, 74, 59,
12, 16, 70, 67, 71, 26, 73, 34, 46, 84,
60, 16, 26, 68, 56, 57, 97, 6, 39, 74,
25, 69, 29, 69, 77, 26, 44, 53, 20, 6,
77, 31, 71, 91, 28, 6, 24, 75, 26, 33,
3, 20, 55, 94, 17, 81, 88, 32, 94, 32,
3, 90, 76, 69, 9, 96, 76, 53, 78, 14,
97, 32, 17, 15, 61, 63, 21, 0, 16, 14,
61, 4, 81, 86, 29, 29, 27, 57, 85, 5,
91, 54, 6, 68, 40, 88, 41, 9, 90, 51 };
int k = 4; // must be at least 2
// use MergeSort
int[] newlist = mergeSortK(list, k, 0, list.length);
printList(newlist);
}
// Helper function to print the elements of a list
private static void printList(int[] list) {
for (int i = 0; i < list.length; i++) {
System.out.println(list[i]);
}
}
}
我没有在末尾写一个正确的 k 路合并阶段,mergeSortK
上面的代码应该可以工作,但会合并 ceil(log2(k)) 通道中的 k 个切片。直接单程 k 路合并很棘手,通常不值得。
推荐阅读
- php - 如何统计在线用户?
- c# - C# 在 SqlDataReader 中禁用自动格式化
- java - 如何使用 Tika API 查找 .bat 文件的 mime 类型?
- reactjs - 设置 React Router 的优先级
- idris - 我可以避免在 Idris 的全部功能中明确排除无效案例吗?
- android - 使用 mockk 的测试用例无法正常工作
- python - 将聊天符号转换为表情符号或文本
- node.js - 使隐藏的 vc 变为可见的 discord.js
- java - 接口引用是否需要将强制转换分配给类对象引用?
- asp.net - 无法仅从我的 asp .net 日历控件中获取日期