ChinaUnix首页 > 精华文章 > C/C++ > 正文

[原创] 合并排序,不使用递归,不使用栈的实现方法


http://www.chinaunix.net 作者:cugb_cat  发表于:2008-03-11 17:16:32
发表评论】 【查看原文】 【C/C++讨论区】【关闭

昨天晚上一直想到现在才搞定。

修改了MERGE函数,现在的复杂度应该在O(nlgn)了。
[table=95%][tr][td][font=FixedSys][color=#000000][color=#ff9900]/* array1与array2必须是连续的空间,且array1在array2之前 */[/color]
[color=#0000ff]static void MERGE(void *array1, size_t nmemb1, void *array2, size_t nmemb2,
        size_t size, int (*compar)(const void *, const void*))
{
    void *i = array1, *j = array2, *k = i;
    void *end1 = array1 + nmemb1 * size;
    void *end2 = array2 + nmemb2 * size;
    void *tmp_array = NULL;
    int len;
    
    while (i < end1 && j < end2) {
        if (compar(i, j) > 0) {
            if (tmp_array == NULL) {
                len = end1 - i;
                tmp_array = malloc(len);
                memcpy(tmp_array, i, len);
                i = tmp_array;
                end1 = i + len;
            }
            memcpy(k, j, size);
            j += size;
        }
        else {
            if (tmp_array) {
                memcpy(k, i, size);
            }
            i += size;
        }
        k += size;
        if (j == end2 && tmp_array) {
            memcpy(k, i, end1 - i);
        }
    }
    if (tmp_array)
        free(tmp_array);
}[/color][color=#0000cc][/color]
[color=#0000ff]void[/color] MERGE_SORT[color=#0000cc]([/color][color=#0000ff]void[/color] [color=#0000cc]*[/color]base[color=#0000cc],[/color] [color=#ff0000]size_t[/color] nmemb[color=#0000cc],[/color] [color=#ff0000]size_t[/color] size[color=#0000cc],[/color]
        [color=#0000ff]int[/color] [color=#0000cc]([/color][color=#0000cc]*[/color]compar[color=#0000cc])[/color][color=#0000cc]([/color][color=#0000ff]const[/color] [color=#0000ff]void[/color] [color=#0000cc]*[/color][color=#0000cc],[/color] [color=#0000ff]const[/color] [color=#0000ff]void[/color] [color=#0000cc]*[/color][color=#0000cc])[/color][color=#0000cc])[/color]
[color=#0000cc]{[/color]
    [color=#0000ff]if[/color] [color=#0000cc]([/color]nmemb [color=#0000cc]<[/color][color=#0000cc]=[/color] 0[color=#0000cc])[/color]
        [color=#0000ff]return[/color][color=#0000cc];[/color]
    [color=#0000ff]int[/color] depth [color=#0000cc]=[/color] [color=#0000cc]([/color][color=#0000ff]int[/color][color=#0000cc])[/color][color=#0000cc]([/color][color=#ff0000]ceil[/color][color=#0000cc]([/color]log2[color=#0000cc]([/color]nmemb[color=#0000cc])[/color][color=#0000cc])[/color][color=#0000cc])[/color][color=#0000cc];[/color]
    [color=#0000ff]int[/color] num1 [color=#0000cc]=[/color] 1[color=#0000cc],[/color] num2 [color=#0000cc]=[/color] 1[color=#0000cc],[/color] tmp[color=#0000cc],[/color] remain_num[color=#0000cc];[/color]
    [color=#0000ff]int[/color] node_num [color=#0000cc]=[/color] nmemb[color=#0000cc];[/color]
    [color=#0000ff]int[/color] end_marge_flag[color=#0000cc],[/color] end_num[color=#0000cc];[/color]
    [color=#0000ff]void[/color] [color=#0000cc]*[/color]array1 [color=#0000cc]=[/color] base[color=#0000cc],[/color] [color=#0000cc]*[/color]array2 [color=#0000cc]=[/color] base [color=#0000cc]+[/color] size[color=#0000cc];[/color]
    
    remain_num [color=#0000cc]=[/color] 0[color=#0000cc];[/color]
    end_num [color=#0000cc]=[/color] 1[color=#0000cc];[/color]
    [color=#0000ff]while[/color] [color=#0000cc]([/color]depth[color=#0000cc])[/color] [color=#0000cc]{[/color]
        tmp [color=#0000cc]=[/color] node_num [color=#0000cc]/[/color] 2[color=#0000cc];[/color]
        end_marge_flag [color=#0000cc]=[/color] [color=#0000cc]![/color][color=#0000cc]([/color]node_num [color=#0000cc]%[/color] 2[color=#0000cc])[/color][color=#0000cc];[/color]
        [color=#0000ff]for[/color] [color=#0000cc]([/color]array1 [color=#0000cc]=[/color] base[color=#0000cc],[/color] array2 [color=#0000cc]=[/color] base [color=#0000cc]+[/color] num1 [color=#0000cc]*[/color] size[color=#0000cc];[/color] tmp[color=#0000cc];[/color] tmp[color=#0000cc]-[/color][color=#0000cc]-[/color][color=#0000cc])[/color] [color=#0000cc]{[/color]
            [color=#0000ff]if[/color] [color=#0000cc]([/color]end_marge_flag[color=#0000cc])[/color] [color=#0000cc]{[/color]
                [color=#0000ff]if[/color] [color=#0000cc]([/color][color=#0000cc]![/color][color=#0000cc]([/color]tmp [color=#0000cc]-[/color] 1[color=#0000cc])[/color] [color=#0000cc]&[/color][color=#0000cc]&[/color] end_num[color=#0000cc])[/color] [color=#0000cc]{[/color]
                    num2 [color=#0000cc]=[/color] end_num[color=#0000cc];[/color]
                    end_num [color=#0000cc]=[/color] num1 [color=#0000cc]+[/color] num2[color=#0000cc];[/color]
                [color=#0000cc]}[/color]
            [color=#0000cc]}[/color]
            [color=#ff0000]MERGE[/color][color=#0000cc]([/color]array1[color=#0000cc],[/color] num1[color=#0000cc],[/color] array2[color=#0000cc],[/color] num2[color=#0000cc],[/color] size[color=#0000cc],[/color] compar[color=#0000cc])[/color][color=#0000cc];[/color]
            array1 [color=#0000cc]+[/color][color=#0000cc]=[/color] num1 [color=#0000cc]*[/color] size [color=#0000cc]+[/color] num2 [color=#0000cc]*[/color] size[color=#0000cc];[/color]
            array2 [color=#0000cc]+[/color][color=#0000cc]=[/color] num2 [color=#0000cc]*[/color] size [color=#0000cc]+[/color] num1 [color=#0000cc]*[/color] size[color=#0000cc];[/color]
        [color=#0000cc]}[/color]
        num1 [color=#0000cc]<[/color][color=#0000cc]<[/color][color=#0000cc]=[/color] 1[color=#0000cc];[/color]
        num2 [color=#0000cc]=[/color] num1[color=#0000cc];[/color]
        node_num [color=#0000cc]=[/color] [color=#0000cc]([/color]node_num [color=#0000cc]%[/color] 2[color=#0000cc])[/color] [color=#0000cc]?[/color] node_num [color=#0000cc]/[/color] 2 [color=#0000cc]+[/color] 1 [color=#0000cc]:[/color] node_num [color=#0000cc]/[/color] 2[color=#0000cc];[/color]
        depth[color=#0000cc]-[/color][color=#0000cc]-[/color][color=#0000cc];[/color]
    [color=#0000cc]}[/color]
[color=#0000cc]}[/color][/color][/font][/td][/tr][/table]

[ 本帖最后由 cugb_cat 于 2008-3-11 17:06 编辑 ]



 cugb_cat 回复于:2008-03-10 14:17:04

使用方法和qsort的使用方法一样。


 cjaizss 回复于:2008-03-10 14:35:25

昨天拿sed不用e命令写了个加法.........


 cugb_cat 回复于:2008-03-10 14:37:10

引用:原帖由 cjaizss 于 2008-3-10 14:35 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8059598&ptid=1063117]
昨天拿sed不用e命令写了个加法......... 


sed不会用。。。。。
有时间得学学。


 Edengundam 回复于:2008-03-10 15:35:52

引用:原帖由 cugb_cat 于 2008-3-10 14:17 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8059466&ptid=1063117]
使用方法和qsort的使用方法一样。 



你的思路是不是先2个一组排序, 然后4个, 8个...这样进行??

可是qsort不能这样分解吧? 我只见过qsort的递归算法, 每次选取分界的元素后, 对两个子集继续排序. 这个过程感觉上不可逆...

没看你的代码, MergeSort函数的变量名看着头大...


 cugb_cat 回复于:2008-03-10 15:41:28

引用:原帖由 Edengundam 于 2008-3-10 15:35 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8060069&ptid=1063117]


你的思路是不是先2个一组排序, 然后4个, 8个...这样进行??

可是qsort不能这样分解吧? 我只见过qsort的递归算法, 每次选取分界的元素后, 对两个子集继续排序. 这个过程感觉上不可逆...

没看你的代码,  ... 


不是,我是说用法和qsort一样,qsort是快速排序,我这个是合并排序,不一样的。
思路是从二叉树的树叶开始,向上一直到树根。


 Edengundam 回复于:2008-03-10 16:09:07

引用:原帖由 cugb_cat 于 2008-3-10 15:41 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8060110&ptid=1063117]

不是,我是说用法和qsort一样,qsort是快速排序,我这个是合并排序,不一样的。
思路是从二叉树的树叶开始,向上一直到树根。 



又看错了...看成了实现方法....最近精力严重不集中....


 tyc611 回复于:2008-03-10 18:36:19

“非递归非栈,原地排序”,很不错,不知道是否仍是稳定的?(应该还是O(nlgn)吧?)

下去研究下


 tyc611 回复于:2008-03-10 19:03:30


void *end1 = array1 + nmemb1 * size;

LZ,你的编译器不报错吗?:shock:


 cugb_cat 回复于:2008-03-10 19:08:48

引用:原帖由 tyc611 于 2008-3-10 18:36 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8061197&ptid=1063117]
“非递归非栈,原地排序”,很不错,不知道是否仍是稳定的?(应该还是O(nlgn)吧?)

下去研究下 


时间复杂度肯定还是O(nlgn),只不过不使用递归和栈,省了不少空间和一些额外开销。

补充,我测试倒是没出错,还是要拜托大家多给测测:) :) :) :)

[ 本帖最后由 cugb_cat 于 2008-3-10 19:13 编辑 ]


 cugb_cat 回复于:2008-03-10 19:09:55

引用:原帖由 tyc611 于 2008-3-10 19:03 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8061266&ptid=1063117]

void *end1 = array1 + nmemb1 * size;

LZ,你的编译器不报错吗?:shock: 


没报错啊,

rainlx@rainlx:~$ gcc -v
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --enable-languages=c,c++,fortran,objc,obj-c++,treelang --prefix=/usr --enable-shared --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --enable-nls --with-gxx-include-dir=/usr/include/c++/4.1.3 --program-suffix=-4.1 --enable-__cxa_atexit --enable-clocale=gnu --enable-libstdcxx-debug --enable-mpfr --enable-checking=release i486-linux-gnu
Thread model: posix
gcc version 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)

对了,用gcc编译,别用g++,这代码可能g++就出错了。

[ 本帖最后由 cugb_cat 于 2008-3-10 19:10 编辑 ]


 tyc611 回复于:2008-03-10 19:14:30

引用:原帖由 tyc611 于 2008-3-10 18:36 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8061197&ptid=1063117]
“非递归非栈,原地排序”,很不错,不知道是否仍是稳定的?(应该还是O(nlgn)吧?)

下去研究下 


看了下,LZ的算法仍是稳定的,但是合并操作中用时间换空间实现原地排序,增加了很多移动操作


 tyc611 回复于:2008-03-10 19:15:37

引用:原帖由 cugb_cat 于 2008-3-10 19:08 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8061295&ptid=1063117]

时间复杂度肯定还是O(nlgn),只不过不使用递归和栈,省了不少空间和一些额外开销。

补充,我测试倒是没出错,还是要拜托大家多给测测:) :) :) :) 


时间复杂度已经不是了,你可不能把
memmove(i + step, i, j - i);

当作O(1)啊


 tyc611 回复于:2008-03-10 19:18:06

引用:原帖由 cugb_cat 于 2008-3-10 19:09 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8061298&ptid=1063117]

没报错啊,

rainlx@rainlx:~$ gcc -v
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --enable-languages=c,c++,fortran,objc,obj-c++,treelang --prefix=/usr - ... 


平时都不用C了,已经忘了C中怎么规定的,但C++中肯定是错的,因为一个指针类型加一个偏移量的运算结果与指针类型密切相关,而现在指针类型未知


 cugb_cat 回复于:2008-03-10 19:29:23

引用:原帖由 tyc611 于 2008-3-10 19:15 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8061309&ptid=1063117]

时间复杂度已经不是了,你可不能把
memmove(i + step, i, j - i);
当作O(1)啊 


memmove其实是两次memcpy操作。仔细琢磨起来,这样是比使用额外的空间增加了移动内存的次数,不过可以修改这个MERGE程序,只要符合原来的接口定义就行。
其实先MERGE到一个额外的空间,然后再copy到原来的空间,其实也挺费劲。具体的时间复杂度没细考虑。


 cugb_cat 回复于:2008-03-10 19:30:12

引用:原帖由 tyc611 于 2008-3-10 19:18 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8061312&ptid=1063117]

平时都不用C了,已经忘了C中怎么规定的,但C++中肯定是错的,因为一个指针类型加一个偏移量的运算结果与指针类型密切相关,而现在指针类型未知 


C中没问题的,呵呵,我都不会C++。
那我改下吧,把类型强制转为char *就行了。


 lalala 回复于:2008-03-10 19:51:58

LZ的merge用时间换空间了。。。。。。。
merge的时间复杂度就变成O(n^2)了,
用这个merge实现的merge sort就不是O(nlogn)了而是O(n^2logn)

[ 本帖最后由 lalala 于 2008-3-10 20:06 编辑 ]


 cugb_cat 回复于:2008-03-10 20:04:16

引用:原帖由 lalala 于 2008-3-10 19:51 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8061364&ptid=1063117]
LZ的merge用时间换空间了。。。。。。。 


这个问题我还的好好想想,多谢各位的提醒。


 lalala 回复于:2008-03-10 20:09:29

我以前也想过这个,要使实现O(nlogn),就要实现merge的线性增长


 cugb_cat 回复于:2008-03-11 17:09:27

修改了MERGE函数,详见1楼。降低了merge的时间复杂度,不过还是借助了额外的空间,但本着尽量少的使用额外空间的原则,只有在必需时才申请空间。


 system888net 回复于:2008-03-11 17:16:32

:) 顶一下...




原文链接:http://bbs.chinaunix.net/viewthread.php?tid=1063117
转载请注明作者名及原文出处