使用合并排序查找数组的所有反转
Finding all inversions of an array using Merger-Sort
我正在尝试实现使用合并排序查找数组所有反转的流行算法,但它一直输出错误的答案,它计算的反转太多了 - 我相信部分或全部子数组正在被在重复调用中迭代了太多次?我不能完全确定它 - 我会很感激一些关于为什么会发生这种情况的指示。请在下面的 java 中查看我的实现:
public class inversionsEfficient {
public int mergeSort(int[] list, int[] temp, int left, int right) {
int count = 0;
int mid = 0;
if(right > left) {
mid = (right+left)/2;
count += mergeSort(list, temp, left, mid);
count += mergeSort(list, temp, mid+1, right);
count += merge(list, temp, left, mid+1, right);
}
return count;
}
public int merge(int[] list, int[] temp, int left, int mid, int right) {
int count = 0;
int i = left;
int j = mid;
int k = left;
while((i<=mid-1) && (j<=right)) {
if(list[i] <= list[j]) {
temp[k] = list[i];
k += 1;
i += 1;
}
else {
temp[k] = list[j];
k += 1;
j += 1;
count += mid-1;
}
}
while(i<=mid-1) {
temp[k] = list[i];
k += 1;
i += 1;
}
while(j<=right) {
temp[k] = list[j];
k += 1;
j += 1;
}
for(i=left;i<=right;i++) {
list[i] = temp[i];
}
return count;
}
public static void main(String[] args) {
int[] myList = {5, 3, 76, 12, 89, 22, 5};
int[] temp = new int[myList.length];
inversionsEfficient inversions = new inversionsEfficient();
System.out.println(inversions.mergeSort(myList, temp, 0, myList.length-1));
}
}
该算法基于 Cormen 的算法简介中的伪代码:
[1]: https://i.stack.imgur.com/ea9No.png
而不是-
count += mid - 1;
尝试-
count += mid - i;
整个解决方案变成如下图:-
public class inversionsEfficient {
public int mergeSort(int[] list, int[] temp, int left, int right) {
int count = 0;
int mid = 0;
if (right > left) {
mid = (right + left) / 2;
count += mergeSort(list, temp, left, mid);
count += mergeSort(list, temp, mid + 1, right);
count += merge(list, temp, left, mid + 1, right);
}
return count;
}
public int merge(int[] list, int[] temp, int left, int mid, int right) {
int count = 0;
int i = left;
int j = mid;
int k = left;
while ((i <= mid - 1) && (j <= right)) {
if (list[i] <= list[j]) {
temp[k] = list[i];
k += 1;
i += 1;
} else {
temp[k] = list[j];
k += 1;
j += 1;
count += mid - i; // (mid - i), not (mid - 1)
}
}
while (i <= mid - 1) {
temp[k] = list[i];
k += 1;
i += 1;
}
while (j <= right) {
temp[k] = list[j];
k += 1;
j += 1;
}
for (i = left; i <= right; i++) {
list[i] = temp[i];
}
return count;
}
public static void main(String[] args) {
int[] arr = {5, 3, 76, 12, 89, 22, 5};
int[] temp = new int[arr.length];
inversionsEfficient inversions = new inversionsEfficient();
System.out.println(inversions.mergeSort(arr, temp, 0, arr.length - 1));
}
}
题中提到的示例数组,上面代码生成的输出是8,这是正确的,因为数组有8次反转[5, 3, 76, 12, 89, 22, 5]
-
1. (5, 3)
2. (76, 12)
3. (76, 22)
4. (76, 5)
5. (12, 5)
6. (89, 22)
7. (89, 5)
8. (22, 5)
代码更改说明
本算法计算需要反转的次数为左子数组反转次数+右子数组反转次数+合并过程反转次数之和。
如果list[i] > list[j]
,则有(mid – i)
个倒置,因为左右子数组是有序的。这意味着左子数组 (list[i+1], list[i+2] … list[mid])
中的所有剩余元素也将大于 list[j]
.
有关更详细的说明,请查看 GeeksForGeeks article on Counting Inversions。
我正在尝试实现使用合并排序查找数组所有反转的流行算法,但它一直输出错误的答案,它计算的反转太多了 - 我相信部分或全部子数组正在被在重复调用中迭代了太多次?我不能完全确定它 - 我会很感激一些关于为什么会发生这种情况的指示。请在下面的 java 中查看我的实现:
public class inversionsEfficient {
public int mergeSort(int[] list, int[] temp, int left, int right) {
int count = 0;
int mid = 0;
if(right > left) {
mid = (right+left)/2;
count += mergeSort(list, temp, left, mid);
count += mergeSort(list, temp, mid+1, right);
count += merge(list, temp, left, mid+1, right);
}
return count;
}
public int merge(int[] list, int[] temp, int left, int mid, int right) {
int count = 0;
int i = left;
int j = mid;
int k = left;
while((i<=mid-1) && (j<=right)) {
if(list[i] <= list[j]) {
temp[k] = list[i];
k += 1;
i += 1;
}
else {
temp[k] = list[j];
k += 1;
j += 1;
count += mid-1;
}
}
while(i<=mid-1) {
temp[k] = list[i];
k += 1;
i += 1;
}
while(j<=right) {
temp[k] = list[j];
k += 1;
j += 1;
}
for(i=left;i<=right;i++) {
list[i] = temp[i];
}
return count;
}
public static void main(String[] args) {
int[] myList = {5, 3, 76, 12, 89, 22, 5};
int[] temp = new int[myList.length];
inversionsEfficient inversions = new inversionsEfficient();
System.out.println(inversions.mergeSort(myList, temp, 0, myList.length-1));
}
}
该算法基于 Cormen 的算法简介中的伪代码: [1]: https://i.stack.imgur.com/ea9No.png
而不是-
count += mid - 1;
尝试-
count += mid - i;
整个解决方案变成如下图:-
public class inversionsEfficient {
public int mergeSort(int[] list, int[] temp, int left, int right) {
int count = 0;
int mid = 0;
if (right > left) {
mid = (right + left) / 2;
count += mergeSort(list, temp, left, mid);
count += mergeSort(list, temp, mid + 1, right);
count += merge(list, temp, left, mid + 1, right);
}
return count;
}
public int merge(int[] list, int[] temp, int left, int mid, int right) {
int count = 0;
int i = left;
int j = mid;
int k = left;
while ((i <= mid - 1) && (j <= right)) {
if (list[i] <= list[j]) {
temp[k] = list[i];
k += 1;
i += 1;
} else {
temp[k] = list[j];
k += 1;
j += 1;
count += mid - i; // (mid - i), not (mid - 1)
}
}
while (i <= mid - 1) {
temp[k] = list[i];
k += 1;
i += 1;
}
while (j <= right) {
temp[k] = list[j];
k += 1;
j += 1;
}
for (i = left; i <= right; i++) {
list[i] = temp[i];
}
return count;
}
public static void main(String[] args) {
int[] arr = {5, 3, 76, 12, 89, 22, 5};
int[] temp = new int[arr.length];
inversionsEfficient inversions = new inversionsEfficient();
System.out.println(inversions.mergeSort(arr, temp, 0, arr.length - 1));
}
}
题中提到的示例数组,上面代码生成的输出是8,这是正确的,因为数组有8次反转[5, 3, 76, 12, 89, 22, 5]
-
1. (5, 3)
2. (76, 12)
3. (76, 22)
4. (76, 5)
5. (12, 5)
6. (89, 22)
7. (89, 5)
8. (22, 5)
代码更改说明
本算法计算需要反转的次数为左子数组反转次数+右子数组反转次数+合并过程反转次数之和。
如果list[i] > list[j]
,则有(mid – i)
个倒置,因为左右子数组是有序的。这意味着左子数组 (list[i+1], list[i+2] … list[mid])
中的所有剩余元素也将大于 list[j]
.
有关更详细的说明,请查看 GeeksForGeeks article on Counting Inversions。