Merging in merge sort algorithm
我知道合并排序算法的基本概念,但是当它通过递归实现时,我很难理解它是如何工作的。据我所知,merge sort函数将当前数组拆分为两半,使用递归,我们一直这样做,直到每边有一个元素为止。
如果我们的数组是38、27、43、3、9、82、10,那么递归将从使用子数组(原始数组的左侧)调用自身开始,并每次重复该过程,将数组减半并存储最左侧,直到我们到达一个元素:
1 2 3 4 5 6 | 38 27 43 3 9 82 10 38 27 43 3 <-split <---first subroutine/recursion 38 27 <-split <---second subroutine/recursion 38 <---only 1 element left so we return the value back to the first subroutine that called |
然后,在第二个子例程中,我们转到下一行:right=merge_sort(right),它再次调用自身来拆分子数组并存储最右侧:
1 2 3 4 | 38 27 <-split <---second subroutine/recursion 27 <---only 1 element left so we return the value back to the first subroutine that called |
然后在第二个子例程中,我们转到下一行:result=merge(左,右),它调用merge函数来排序我们的左数组和右数组,它们只有38和27个。merge函数根据较小的值对两个值进行排序,然后将第一个值添加到数组中,尽管我不确定哪个数组。(我需要这方面的规范;我们不应该每次合并两个以前的数组时都有一个新的数组吗?)然后,merge函数将"result"从调用merge函数返回到merge sort函数中的另一个result变量。我假设这个结果是按顺序排序的新数组38和27。然后,看起来我们再次将结果返回到任何称为合并排序函数的函数,但是我很困惑,因为这不会结束所有的操作吗?对于为左侧递归而暂停的第一个子例程呢?我不知道会发生什么:
1 2 3 4 5 6 | 38 27 43 3 43 3 43 and 43 3 3 |
伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function merge_sort(m) if length(m) ≤ 1 return m var list left, right, result var integer middle = length(m) / 2 for each x in m up to middle add x to left for each x in m after middle add x to right left = merge_sort(left) right = merge_sort(right) result = merge(left, right) return result |
在编写合并排序函数之后,需要合并上面创建的左列表和右列表。merge()函数有几种变体;一种可能是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | function merge(left,right) var list result while length(left) > 0 or length(right) > 0 if length(left) > 0 and length(right) > 0 if first(left) ≤ first(right) append first(left) to result left = rest(left) else append first(right) to result right = rest(right) else if length(left) > 0 append first(left) to result left = rest(left) else if length(right) > 0 append first(right) to result right = rest(right) end while return result |
http://www.princeton.edu/~achaney/tmve/wiki100k/docs/merge_sort.html
我不确定您是否在寻找它,但是您可以通过在主要条件下用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | while length(left) > 0 and length(right) > 0 if first(left) ≤ first(right) append first(left) to result left = rest(left) else append first(right) to result right = rest(right) end while # You know that one of left and right is empty # Copy the rest of the data from the other while length(left) > 0 append first(left) to result left = rest(left) end while while length(right) > 0 append first(right) to result right = rest(right) end while |
是的,有三个循环,但最后两个循环中只有一个被执行。
在伪代码的基础上紧密地工作C99代码因此,代码使用C99可变长度数组(C11中的可选功能)。如果使用
对于生产质量代码,我的诊断打印功能还接受一个
Shouldn't we have a new array every time we merge two previous arrays?
在函数式编程语言中,您将拥有新的数组。在C语言中,也使用输入数组作为输出数组。代码将原始输入数组复制到单独的较小数组中,对这些较小的数组进行排序,并将排序后的较小数组合并到原始数组中。
My other question is what procedure in the code allows us to go back to before the recursion where we split the left side of our array so we can work on the right side to get 43 a 3 in order to merge them as well.
拆分过程将创建输入数组的副本(因此原始数据中的信息暂时多余)。合并过程将(现在已排序)拆分的数组复制回原始数组。(主要是重复自己的话。)
来源1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | #include <stddef.h> extern void merge_sort(int *array, size_t arrlen); /* Debug */ #ifdef DEBUG static void dump_array(const char *tag, int *array, size_t len); static void enter_func(const char *func); static void exit_func(const char *func); #else #define dump_array(t, a, l) ((void)0) #define enter_func(f) ((void)0) #define exit_func(f) ((void)0) #endif /* function merge(left, right) var list result while length(left) > 0 and length(right) > 0 if first(left) ≤ first(right) append first(left) to result left = rest(left) else append first(right) to result right = rest(right) end while # You know that one of left and right is empty # Copy the rest of the data from the other while length(left) > 0 append first(left) to result left = rest(left) end while while length(right) > 0 append first(right) to result right = rest(right) end while return result end function */ static void merge(int *left, size_t l_len, int *right, size_t r_len, int *output) { size_t r_pos = 0; size_t l_pos = 0; size_t o_pos = 0; enter_func(__func__); dump_array("Left:", left, l_len); dump_array("Right:", right, r_len); while (r_pos < r_len && l_pos < l_len) { if (right[r_pos] < left[l_pos]) output[o_pos++] = right[r_pos++]; else output[o_pos++] = left[l_pos++]; } while (r_pos < r_len) output[o_pos++] = right[r_pos++]; while (l_pos < l_len) output[o_pos++] = left[l_pos++]; dump_array("Output:", output, r_len + l_len); exit_func(__func__); } /* function merge_sort(m) if length(m) ≤ 1 return m var list left, right, result var integer middle = length(m) / 2 for each x in m up to middle add x to left for each x in m after middle add x to right left = merge_sort(left) right = merge_sort(right) result = merge(left, right) return result */ void merge_sort(int *array, size_t len) { if (len <= 1) return; int left[(len+1)/2]; int l_pos = 0; int right[(len+1)/2]; int r_pos = 0; size_t mid = len / 2; enter_func(__func__); dump_array("Input:", array, len); for (size_t i = 0; i < mid; i++) left[l_pos++] = array[i]; for (size_t i = mid; i < len; i++) right[r_pos++] = array[i]; dump_array("Left:", left, l_pos); dump_array("Right:", right, r_pos); merge_sort(left, l_pos); merge_sort(right, r_pos); merge(left, l_pos, right, r_pos, array); dump_array("Result:", array, len); exit_func(__func__); } /* Test code */ #include <stdio.h> #ifdef DEBUG static void enter_func(const char *func) { printf("-->> %s ", func); } static void exit_func(const char *func) { printf("<<-- %s ", func); } #endif /* dump_array is always used */ #undef dump_array static void dump_array(const char *tag, int *array, size_t len) { printf("%-8s", tag); for (size_t i = 0; i < len; i++) printf(" %2d", array[i]); putchar(' '); } int main(void) { int array[] = { 38, 27, 43, 3, 9, 82, 10 }; size_t arrlen = sizeof(array) / sizeof(array[0]); dump_array("Before:", array, arrlen); merge_sort(array, arrlen); dump_array("After:", array, arrlen); return 0; } |
样本输出
非调试
1 2 | Before: 38 27 43 3 9 82 10 After: 3 9 10 27 38 43 82 |
调试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | Before: 38 27 43 3 9 82 10 -->> merge_sort Input: 38 27 43 3 9 82 10 Left: 38 27 43 Right: 3 9 82 10 -->> merge_sort Input: 38 27 43 Left: 38 Right: 27 43 -->> merge_sort Input: 27 43 Left: 27 Right: 43 -->> merge Left: 27 Right: 43 Output: 27 43 <<-- merge Result: 27 43 <<-- merge_sort -->> merge Left: 38 Right: 27 43 Output: 27 38 43 <<-- merge Result: 27 38 43 <<-- merge_sort -->> merge_sort Input: 3 9 82 10 Left: 3 9 Right: 82 10 -->> merge_sort Input: 3 9 Left: 3 Right: 9 -->> merge Left: 3 Right: 9 Output: 3 9 <<-- merge Result: 3 9 <<-- merge_sort -->> merge_sort Input: 82 10 Left: 82 Right: 10 -->> merge Left: 82 Right: 10 Output: 10 82 <<-- merge Result: 10 82 <<-- merge_sort -->> merge Left: 3 9 Right: 10 82 Output: 3 9 10 82 <<-- merge Result: 3 9 10 82 <<-- merge_sort -->> merge Left: 27 38 43 Right: 3 9 10 82 Output: 3 9 10 27 38 43 82 <<-- merge Result: 3 9 10 27 38 43 82 <<-- merge_sort After: 3 9 10 27 38 43 82 |