哈佛大学公开课:计算机科学cs50 学习笔记及代码实现(第9集:归并排序)

递归算法实现归并排序

伪代码如下:

if n < 2

     return;

else

     sort left half of elements;

     sort right half of elements;

     merge sorted halves;

例子:4,2,6,8,1,3,7,5 从小到大排序

n < 2? no

sort left half: 4,2,6,8

n< 2? no

sort left half: 4,2

n< 2? no

sort left half

4

n < 2? yes

return and sort right half

2

n < 2? yes

return and merge sorted halves

2, 4

sort right half: 6,8

n < 2? no

sort left half

6

n < 2? yes

return and sort right half

8

n < 2? yes

return and merge

6, 8

merge (2,4) and (6,8)

2 与 6 比,2小

4 与 6比, 4小

结束:2,4,6,8

sort right half: 1,3,7,5

n < 2? no

sort left half: 1,3

n < 2? no

sort left half

1

n < 2? yes

return and sort right half

3

n < 2? yes

return and merge

1, 3

sort right half: 7, 5

n < 2? no

sort left half

7

n < 2? yes

return and sort right half

5

n < 2? yes

return and merge

5, 7

merge (1, 3) and (5, 7)

1 与 5 比,1小

3 与 5 比,3小

结束

1,3,5,7

merge (2,4,6,8) and (1,3,5,7)

2 与 1比,1小

2 与 3 比,2小

4 与 3 比,3小

4 与 5 比,4小

6 与 5 比,5小

6 与 7 比,6小

8 与 7 比,7小

结束:1,2,3,4,5,6,7,8

算法时间复杂度 是 O(n*logn)

因为将n个数每次除以二直至只剩一个数需要logn次,另外每一个层次需要比较n次大小。

代码实现:

数组是以指针形式传递给函数的,所以函数一开始并不知道数组的确切尺寸,所以应该提供一些额外的信息。C++中常用方法有两种:1. 显示地传递一个表示数组大小的形参,这也是C语言和C++11标准之前常用的; 2. 在C++11标准中,可以传递指向数组首元素和尾后元素的指针,下面的代码我用了这种方法。这类似于容器类型的迭代器,但因为数组不是类类型,所以这两个函数不是成员函数,使用方法为:

int * beg = std::beging(array_name);

int * end = std::end(array_name);

这两个函数在iterator头文件中。这两个函数使得指针的使用更简单,更安全,避免下标越界或者访问非法指针造成segmentation error。

但要特别注意,尾后指针不可以解引用和递增操作。

#include <iostream>
#include <iterator>

void swap(int &a, int &b)
{
    int tmp = a;
    a = b;
    b = tmp;
}

void print_array(const int *beg, const int *end)
{
    while(beg != end)
    {
        std::cout << *beg++ << " ";
    }
    std::cout << "\n";
}

void merge(const int *beg, const int *end, int *result_ptr)
{
    int left_length = (end - beg) / 2;//左部分区间的数据元素的个数
    const int *left_ptr = beg;
    const int *right_ptr = beg + left_length;
    while(left_ptr != beg + left_length && right_ptr != end)
    {
        //对分别已经排好序的左区间和右区间进行合并
        if(*left_ptr <= *right_ptr)
            *result_ptr++ = *left_ptr++;
        else
            *result_ptr++ = *right_ptr++;
    }
    while(left_ptr != beg + left_length)
        *result_ptr++ = *left_ptr++;
    while(right_ptr != end)
        *result_ptr++ = *right_ptr++;
}

void merge_sort(int * beg, int * end, int *result_ptr)
{
    if((end - beg) == 1 || beg == end)//如果只有1或0个元素,则不用排序
        return;
    
    else if (end - beg == 2)
    {
        if (*beg > *(end-1))
        {
            swap(*beg, *(end-1));
        }
    }
    
    else
    {
        //继续划分子区间,分别对左右子区间进行排序
        merge_sort(beg, (end-beg)/2+beg, result_ptr);
        merge_sort((end-beg)/2+beg, end, result_ptr);
        //开始归并已经排好序的start到end之间的数据
        merge(beg, end, result_ptr);
        //把排序后的区间数据复制到原始数据中去
        while(beg != end)
            *beg++ = *result_ptr++;
    }
}


int main(void)
{
    int array[9] = {9,7,5,8,2,4,1,3,6};
    int result[9];

    int * beg = std::begin(array);
    int * end = std::end(array);
    int * result_ptr = std::begin(result);

    std::cout << "before sorting" << std::endl;
    print_array(beg, end);

    merge_sort(beg, end, result_ptr);

    beg = std::begin(result);
    end = std::end(result);
    std::cout << "after sorting" << std::endl;
    print_array(beg, end);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/shaozhenghan/article/details/81294912