我看到的大多数mergesort实现与此类似。算法书介绍以及我搜索的在线实施。我的递归方法并没有比弄乱Fibonacci一代(这很简单),所以也许是多次递归让我大吃一惊,但是我甚至无法介绍代码并理解甚至在我击中之前发生的事情。合并功能。
如何踩过这个?我是否应该采取一些策略或阅读来更好地理解这个过程?
void mergesort(int *a, int*b, int low, int high)
{
int pivot;
if(low<high)
{
pivot=(low+high)/2;
mergesort(a,b,low,pivot);
mergesort(a,b,pivot+1,high);
merge(a,b,low,pivot,high);
}
}
和合并(虽然坦率地说,在我到达这一部分之前,我在精神上陷入困境)
void merge(int *a, int *b, int low, int pivot, int high)
{
int h,i,j,k;
h=low;
i=low;
j=pivot+1;
while((h<=pivot)&&(j<=high))
{
if(a[h]<=a[j])
{
b[i]=a[h];
h++;
}
else
{
b[i]=a[j];
j++;
}
i++;
}
if(h>pivot)
{
for(k=j; k<=high; k++)
{
b[i]=a[k];
i++;
}
}
else
{
for(k=h; k<=pivot; k++)
{
b[i]=a[k];
i++;
}
}
for(k=low; k<=high; k++) a[k]=b[k];
}
答案 0 :(得分:18)
答案 1 :(得分:14)
我认为&#34;排序&#34; MergeSort中的函数名称有点用词不当,它应该被称为&#34; divide&#34;。
以下是正在进行的算法的可视化。
每次函数递归时,它都会在输入数组的较小和较小的细分上工作,从它的左半部分开始。每次函数从递归返回时,它将继续运行并开始在右半部分工作,或者再次递增并处理更大的一半。
喜欢这个
[************************]mergesort
[************]mergesort(lo,mid)
[******]mergesort(lo,mid)
[***]mergesort(lo,mid)
[**]mergesort(lo,mid)
[**]mergesort(mid+1,hi)
[***]merge
[***]mergesort(mid+1,hi)
[**]mergesort*(lo,mid)
[**]mergesort(mid+1,hi)
[***]merge
[******]merge
[******]mergesort(mid+1,hi)
[***]mergesort(lo,mid)
[**]mergesort(lo,mid)
[**]mergesort(mid+1,hi)
[***]merge
[***]mergesort(mid+1,hi)
[**]mergesort(lo,mid)
[**]mergesort(mid+1,hi)
[***]merge
[******]merge
[************]merge
[************]mergesort(mid+1,hi)
[******]mergesort(lo,mid)
[***]mergesort(lo,mid)
[**]mergesort(lo,mid)
[**]mergesort(mid+1,hi)
[***]merge
[***]mergesort(mid+1,hi)
[**]mergesort(lo,mid)
[**]mergesort(mid+1,hi)
[***]merge
[******]merge
[******]mergesort(mid+1,hi)
[***]mergesort(lo,mid)
[**]mergesort*(lo,mid)
[**]mergesort(mid+1,hi)
[***]merge
[***]mergesort(mid+1,hi)
[**]mergesort(lo,mid)
[**]mergesort(mid+1,hi)
[***]merge
[******]merge
[************]merge
[************************]merge
答案 2 :(得分:8)
一个显而易见的事情是在一个小阵列上尝试这种合并排序,比如8号(2的功率在这里很方便),在纸上。假装你是一台执行代码的计算机,看看它是否开始变得更清晰了。
你的问题有点含糊不清,因为你没有解释你发现令人困惑的内容,但听起来你正试图在脑海中展开递归调用。这可能是好事,也可能不是好事,但我认为这很容易导致你的头脑过多。而不是试图从头到尾跟踪代码,看看你是否能够抽象地理解这个概念。合并排序:
(1)对你来说应该是相当明显和直观的。对于步骤(2),关键见解是这样,数组的左半部分是一个数组。 假设您的合并排序工作,它应该能够对数组的左半部分进行排序。对?步骤(4)实际上是算法的一个非常直观的部分。一个例子应该使它变得微不足道:
at the start
left: [1, 3, 5], right: [2, 4, 6, 7], out: []
after step 1
left: [3, 5], right: [2, 4, 6, 7], out: [1]
after step 2
left: [3, 5], right: [4, 6, 7], out: [1, 2]
after step 3
left: [5], right: [4, 6, 7], out: [1, 2, 3]
after step 4
left: [5], right: [6, 7], out: [1, 2, 3, 4]
after step 5
left: [], right: [6, 7], out: [1, 2, 3, 4, 5]
after step 6
left: [], right: [7], out: [1, 2, 3, 4, 5, 6]
at the end
left: [], right: [], out: [1, 2, 3, 4, 5, 6, 7]
因此,假设你理解(1)和(4),另一种思考合并排序的方法就是这样。想象一下,其他人写了mergesort()
并且您确信它有效。然后你可以使用mergesort()
的实现来写:
sort(myArray)
{
leftHalf = myArray.subArray(0, myArray.Length/2);
rightHalf = myArray.subArray(myArray.Length/2 + 1, myArray.Length - 1);
sortedLeftHalf = mergesort(leftHalf);
sortedRightHalf = mergesort(rightHalf);
sortedArray = merge(sortedLeftHalf, sortedRightHalf);
}
请注意sort
不使用递归。它只是说“将两半分开然后合并它们”。如果您理解上面的合并示例,那么希望您直观地看到这个sort
函数似乎按照它所说的那样...排序。
现在,如果你仔细看一下...... sort()
看起来非常像mergesort()
!那是因为它是mergesort()
(除了它没有基本情况,因为它不是递归的!)。
但这就是我喜欢考虑递归函数的方法 - 假设函数在调用它时起作用。将它视为一个黑盒子,可以满足您的需求。当你做出这个假设时,弄清楚如何填写那个黑盒子通常很容易。对于给定的输入,您可以将其分解为较小的输入以馈送到黑匣子吗?解决之后,唯一剩下的就是在函数开始时处理基本情况(这是你不需要进行任何递归调用的情况。例如,mergesort([])
只返回一个空数组;它不会对mergesort()
进行递归调用。
最后,这有点抽象,但理解递归的一个好方法实际上是使用归纳法编写数学证明。用于通过归纳编写证明的相同策略用于编写递归函数:
数学证明:
n
n
递归函数:
n
n
答案 3 :(得分:6)
关于合并排序的递归部分,我发现这个page非常有帮助。您可以按照正在执行的代码进行操作。它会向您显示首先执行的操作以及接下来的操作。
汤姆
答案 4 :(得分:4)
mergesort()
只是将数组分成两半,直到if
条件失败为low < high
。当您拨打mergesort()
两次时:low
至pivot
,第二次pivot+1
至high
,这将进一步划分子阵列。< / p>
让我们举一个例子:
a[] = {9,7,2,5,6,3,4}
pivot = 0+6/2 (which will be 3)
=> first mergesort will recurse with array {9,7,2} : Left Array
=> second will pass the array {5,6,3,4} : Right Array
它将重复,直到每个left
以及right
数组中有1个元素。
最后你会得到类似的东西:
L : {9} {7} {2} R : {5} {6} {3} {4} (each L and R will have further sub L and R)
=> which on call to merge will become
L(L{7,9} R{2}) : R(L{5,6} R{3,4})
As you can see that each sub array are getting sorted in the merge function.
=> on next call to merge the next L and R sub arrays will get in order
L{2,7,9} : R{3,4,5,6}
Now both L and R sub array are sorted within
On last call to merge they'll be merged in order
Final Array would be sorted => {2,3,4,5,6,7,9}
请参阅@roliu给出的答案中的合并步骤
答案 5 :(得分:3)
如果以这种方式回答我的道歉。我承认这只是一个草图,而不是一个深刻的解释。
虽然看到实际代码如何映射到递归并不明显,但我能够通过这种方式理解递归。
以未分类集{2,9,7,5}
为例输入。为简洁起见,merge_sort算法用“ms”表示。然后我们可以将操作绘制为:
步骤1:ms(ms(ms(2),ms(9)),ms(ms(7),ms(5)))
第2步:ms(ms({2},{9}),ms({7},{5}))
第3步:ms({2,9},{5,7})
第4步:{2,5,7,9}
重要的是要注意,单线态的merge_sort(如{2}
)只是单线态(ms(2)= {2}
),所以在最深层次的递归中我们得到第一个回答。当内部递归结束并合并在一起时,其余的答案就像多米诺骨牌一样翻滚。
算法的一部分天才是它通过构造自动构建步骤1的递归公式的方式。帮助我的是思考如何将上面的步骤1从静态公式转换为一般递归。
答案 6 :(得分:1)
尝试计算出递归的每一步通常不是一个理想的方法,但对于初学者来说,理解递归背后的基本思想,以及更好地编写递归函数,绝对有帮助。
这是合并排序的 C 解决方案:-
#include <stdio.h>
#include <stdlib.h>
void merge_sort(int *, unsigned);
void merge(int *, int *, int *, unsigned, unsigned);
int main(void)
{
unsigned size;
printf("Enter the no. of integers to be sorted: ");
scanf("%u", &size);
int * arr = (int *) malloc(size * sizeof(int));
if (arr == NULL)
exit(EXIT_FAILURE);
printf("Enter %u integers: ", size);
for (unsigned i = 0; i < size; i++)
scanf("%d", &arr[i]);
merge_sort(arr, size);
printf("\nSorted array: ");
for (unsigned i = 0; i < size; i++)
printf("%d ", arr[i]);
printf("\n");
free(arr);
return EXIT_SUCCESS;
}
void merge_sort(int * arr, unsigned size)
{
if (size > 1)
{
unsigned left_size = size / 2;
int * left = (int *) malloc(left_size * sizeof(int));
if (left == NULL)
exit(EXIT_FAILURE);
for (unsigned i = 0; i < left_size; i++)
left[i] = arr[i];
unsigned right_size = size - left_size;
int * right = (int *) malloc(right_size * sizeof(int));
if (right == NULL)
exit(EXIT_FAILURE);
for (unsigned i = 0; i < right_size; i++)
right[i] = arr[i + left_size];
merge_sort(left, left_size);
merge_sort(right, right_size);
merge(arr, left, right, left_size, right_size);
free(left);
free(right);
}
}
/*
This merge() function takes a target array (arr) and two sorted arrays (left and right),
all three of them allocated beforehand in some other function(s).
It then merges the two sorted arrays (left and right) into a single sorted array (arr).
It should be ensured that the size of arr is equal to the size of left plus the size of right.
*/
void merge(int * arr, int * left, int * right, unsigned left_size, unsigned right_size)
{
unsigned i = 0, j = 0, k = 0;
while ((i < left_size) && (j < right_size))
{
if (left[i] <= right[j])
arr[k++] = left[i++];
else
arr[k++] = right[j++];
}
while (i < left_size)
arr[k++] = left[i++];
while (j < right_size)
arr[k++] = right[j++];
}
这是递归的分步说明:-
Let arr be [1,4,0,3,7,9,8], having the address 0x0000.
In main(), merge_sort(arr, 7) is called, which is the same as merge_sort(0x0000, 7).
After all of the recursions are completed, arr (0x0000) becomes [0,1,3,4,7,8,9].
| | |
| | |
| | |
| | |
| | |
arr - 0x0000 - [1,4,0,3,7,9,8] | | |
size - 7 | | |
| | |
left = malloc() - 0x1000a (say) - [1,4,0] | | |
left_size - 3 | | |
| | |
right = malloc() - 0x1000b (say) - [3,7,9,8] | | |
right_size - 4 | | |
| | |
merge_sort(left, left_size) -------------------> | arr - 0x1000a - [1,4,0] | |
| size - 3 | |
| | |
| left = malloc() - 0x2000a (say) - [1] | |
| left_size = 1 | |
| | |
| right = malloc() - 0x2000b (say) - [4,0] | |
| right_size = 2 | |
| | |
| merge_sort(left, left_size) -------------------> | arr - 0x2000a - [1] |
| | size - 1 |
| left - 0x2000a - [1] <-------------------------- | (0x2000a has only 1 element) |
| | |
| | |
| merge_sort(right, right_size) -----------------> | arr - 0x2000b - [4,0] |
| | size - 2 |
| | |
| | left = malloc() - 0x3000a (say) - [4] |
| | left_size = 1 |
| | |
| | right = malloc() - 0x3000b (say) - [0] |
| | right_size = 1 |
| | |
| | merge_sort(left, left_size) -------------------> | arr - 0x3000a - [4]
| | | size - 1
| | left - 0x3000a - [4] <-------------------------- | (0x3000a has only 1 element)
| | |
| | |
| | merge_sort(right, right_size) -----------------> | arr - 0x3000b - [0]
| | | size - 1
| | right - 0x3000b - [0] <------------------------- | (0x3000b has only 1 element)
| | |
| | |
| | merge(arr, left, right, left_size, right_size) |
| | i.e. merge(0x2000b, 0x3000a, 0x3000b, 1, 1) |
| right - 0x2000b - [0,4] <----------------------- | (0x2000b is now sorted) |
| | |
| | free(left) (0x3000a is now freed) |
| | free(right) (0x3000b is now freed) |
| | |
| | |
| merge(arr, left, right, left_size, right_size) | |
| i.e. merge(0x1000a, 0x2000a, 0x2000b, 1, 2) | |
left - 0x1000a - [0,1,4] <---------------------- | (0x1000a is now sorted) | |
| | |
| free(left) (0x2000a is now freed) | |
| free(right) (0x2000b is now freed) | |
| | |
| | |
merge_sort(right, right_size) -----------------> | arr - 0x1000b - [3,7,9,8] | |
| size - 4 | |
| | |
| left = malloc() - 0x2000c (say) - [3,7] | |
| left_size = 2 | |
| | |
| right = malloc() - 0x2000d (say) - [9,8] | |
| right_size = 2 | |
| | |
| merge_sort(left, left_size) -------------------> | arr - 0x2000c - [3,7] |
| | size - 2 |
| | |
| | left = malloc() - 0x3000c (say) - [3] |
| | left_size = 1 |
| | |
| | right = malloc() - 0x3000d (say) - [7] |
| | right_size = 1 |
| | |
| | merge_sort(left, left_size) -------------------> | arr - 0x3000c - [3]
| left - [3,7] was already sorted, but | | size - 1
| that doesn't matter to this program. | left - 0x3000c - [3] <-------------------------- | (0x3000c has only 1 element)
| | |
| | |
| | merge_sort(right, right_size) -----------------> | arr - 0x3000d - [7]
| | | size - 1
| | right - 0x3000d - [7] <------------------------- | (0x3000d has only 1 element)
| | |
| | |
| | merge(arr, left, right, left_size, right_size) |
| | i.e. merge(0x2000c, 0x3000c, 0x3000d, 1, 1) |
| left - 0x2000c - [3,7] <------------------------ | (0x2000c is now sorted) |
| | |
| | free(left) (0x3000c is now freed) |
| | free(right) (0x3000d is now freed) |
| | |
| | |
| merge_sort(right, right_size) -----------------> | arr - 0x2000d - [9,8] |
| | size - 2 |
| | |
| | left = malloc() - 0x3000e (say) - [9] |
| | left_size = 1 |
| | |
| | right = malloc() - 0x3000f (say) - [8] |
| | right_size = 1 |
| | |
| | merge_sort(left, left_size) -------------------> | arr - 0x3000e - [9]
| | | size - 1
| | left - 0x3000e - [9] <-------------------------- | (0x3000e has only 1 element)
| | |
| | |
| | merge_sort(right, right_size) -----------------> | arr - 0x3000f - [8]
| | | size - 1
| | right - 0x3000f - [8] <------------------------- | (0x3000f has only 1 element)
| | |
| | |
| | merge(arr, left, right, left_size, right_size) |
| | i.e. merge(0x2000d, 0x3000e, 0x3000f, 1, 1) |
| right - 0x2000d - [8,9] <----------------------- | (0x2000d is now sorted) |
| | |
| | free(left) (0x3000e is now freed) |
| | free(right) (0x3000f is now freed) |
| | |
| | |
| merge(arr, left, right, left_size, right_size) | |
| i.e. merge(0x1000b, 0x2000c, 0x2000d, 2, 2) | |
right - 0x1000b - [3,7,8,9] <------------------- | (0x1000b is now sorted) | |
| | |
| free(left) (0x2000c is now freed) | |
| free(right) (0x2000d is now freed) | |
| | |
| | |
merge(arr, left, right, left_size, right_size) | | |
i.e. merge(0x0000, 0x1000a, 0x1000b, 3, 4) | | |
(0x0000 is now sorted) | | |
| | |
free(left) (0x1000a is now freed) | | |
free(right) (0x1000b is now freed) | | |
| | |
| | |
| | |
答案 7 :(得分:0)
process to divide the problem into subproblems 给出的例子将帮助您理解递归。 int A [] = {要短路的元素数。},int p = 0; (情人指数)。 int r = A.length - 1;(更高的指数)。
class DivideConqure1 {
void devide(int A[], int p, int r) {
if (p < r) {
int q = (p + r) / 2; // divide problem into sub problems.
devide(A, p, q); //divide left problem into sub problems
devide(A, q + 1, r); //divide right problem into sub problems
merger(A, p, q, r); //merger the sub problem
}
}
void merger(int A[], int p, int q, int r) {
int L[] = new int[q - p + 1];
int R[] = new int[r - q + 0];
int a1 = 0;
int b1 = 0;
for (int i = p; i <= q; i++) { //store left sub problem in Left temp
L[a1] = A[i];
a1++;
}
for (int i = q + 1; i <= r; i++) { //store left sub problem in right temp
R[b1] = A[i];
b1++;
}
int a = 0;
int b = 0;
int c = 0;
for (int i = p; i < r; i++) {
if (a < L.length && b < R.length) {
c = i + 1;
if (L[a] <= R[b]) { //compare left element<= right element
A[i] = L[a];
a++;
} else {
A[i] = R[b];
b++;
}
}
}
if (a < L.length)
for (int i = a; i < L.length; i++) {
A[c] = L[i]; //store remaining element in Left temp into main problem
c++;
}
if (b < R.length)
for (int i = b; i < R.length; i++) {
A[c] = R[i]; //store remaining element in right temp into main problem
c++;
}
}
答案 8 :(得分:0)
我知道这是一个古老的问题,但想要了解有助于我理解合并排序的想法。
合并排序有两个重要部分
重新定义的作用只是分界部分。
我认为让大多数人感到困惑的是他们认为分裂中有很多逻辑并决定分割什么,但大多数实际的排序逻辑都发生在合并上。递归只是分开并完成上半部分,然后下半部分实际上只是循环,复制过程。
我看到一些提及枢轴的答案但是我建议不要将“pivot”与合并排序相关联,因为这是一种简单的方法来混淆合并排序和快速排序(这很大程度上依赖于选择“枢”)。它们都是“分而治之”的算法。对于合并排序,除法总是发生在中间,而对于快速排序,在选择最佳枢轴时,你可以聪明地进行划分。
答案 9 :(得分:0)
当你调用递归方法时,它不会在它堆叠到堆栈内存的同时执行实际函数。当条件不满意时,它会进入下一行。
考虑这是你的阵列:
def bonus_details
p params
respond_to do |format|
format.json
end
end
所以你的方法合并排序将如下所示:
int a[] = {10,12,9,13,8,7,11,5};
所以所有排序值都存储在空arr中。 它可能有助于理解递归函数的工作原理