• 归并排序、堆排序与快速排序分析(1)


    有段时间没看算法了,今天拿起算法书,随手就翻到排序这一章,排序是几乎每本data structure中都要提到的一部分内容,排序方法可谓林林总总。翻到其中的归并排序和快速排序,由于这两个排序算法都可以用到递归,这里将二者一同列出,进行比较,加深理解。

    首先从归并开始

    1、归并排序

      归并排序,从字面上理解,将多个部分进行"归并"在一起,即Merge起来,事实,归并过程中就是对已经有序的部分merge起来的,即两路归并为例,假设需要归并的两个子数组中数据均已经有序,比如,如下的两个子数组。

    sub_a[] = {1,3,,5,7}
    sub_b[] = {2,4,6,8}

    现在需要将两个子数组进行merge,这里可以假设这样的情景:sub_a是一部分扑克牌、sub_b是一部分扑克牌,均按照从小到大,在桌面上反面排列起来,那么merge的过程就是同时翻开最上面的两张牌,比较大小,将小者放到输出中,然后重复上一个操作,其中某一个牌被全部翻完,将另一个剩下的,逐一翻开到输出中即可,从这个角度,很容易理解,merge的时间复杂度是O(n)。

    这里因为要check,是否有一列已经翻完,这里为了不用每次判断,某个列是否先翻完所有的牌,可以在每列牌最底部设置一个哨兵,比如设置为最大值0xffffffff,那么每次和最大值比较接口,当翻开的牌数之和达到sizeof(sub_a) + sizeof(sub_b),那么停止即可,此时,剩下的是两列牌的哨兵,而所有的牌均已经到输出中了,并且已经有序。

    这里需要建立buffer来储存输出内容,对应的代码实现如下:

    #ifndef NOVA_CTR_INCLUDE/MERGE_SORT_H
    #define NOVA_CTR_INCLUDE/MERGE_SORT_H
    
    #include <iostream>
    
    using namespace std;
    
    namespace crafet {
    
    namespace sort {
    
    
    
    int merge(int* arr, int p, int q, int r);
    int merge_sort(int* arr, int p, int r);
    
    } // end of namespace sort
    } // end of namespace crafet
    #include "merge_sort.h"
    
    namespace crafet {
    namespace sort {
    
    
    int print_arr(int*arr, int len)
    {
        cout << "=====start print array ======" << endl;
        if (NULL == arr) {
            cout << "arr is null" << endl;
        }
    
        for (int i = 0; i < len; i++) {
            cout << arr[i] << " ";
        }
    
        cout << endl;
    
        cout << "=====end print array=====" << endl;
    
        return 0;
    }
    
    int merge(int* arr, int p, int q, int r)
    {
        if (NULL == arr) {
            cout << "arr is null" << endl;
        }
    
    
        int llen = q - p + 1;
        int rlen = r - q;
    
    //    cout << llen << " " << rlen << endl;
        int llen_sentry = llen + 1;
        int rlen_sentry = rlen + 1;
    
    
        int* ltmp = new int[llen_sentry];
    
        int* rtmp = new int[rlen_sentry];
    
    //    cout << "alloc space succ" << endl;
        memset(ltmp, 0, sizeof(int) * llen_sentry);
        memset(rtmp, 0, sizeof(int) * rlen_sentry);
    
        int i = 0;
        for( i = 0;i < llen; i++) {
            ltmp[i] = arr[p + i];
        }
        ltmp[i] = 0xffff;
        
        for (i = 0;i < rlen; i++) {
            rtmp[i] = arr[q + 1 + i];
        }
        rtmp[i] = 0xffff;
    
        print_arr(ltmp, llen_sentry);
        print_arr(rtmp, rlen_sentry);
    
    
        int arr_len = llen + rlen;
        int* merged_arr = new int[llen + rlen];
    
        int total = 0;
        i = 0;
        int j = 0;
        while (total < arr_len) {
            if (ltmp[i] <= rtmp[j]) {
                merged_arr[total] = ltmp[i];
                i++;
            } else {
                merged_arr[total] = rtmp[j];
                j++;
            }
    
            ++total;
        }
    
        print_arr(merged_arr, arr_len);
        delete [] ltmp;
        delete [] rtmp;
        delete [] merged_arr;
    
        return 0;
    
    }
    
    int merge_sort(int*arr, int p, int r)
    {
        if(NULL == arr) {
            cout << "arr is null" << endl;
        }
    
        if (p < r) {
            int q = (p + r)/2;
            merge_sort(arr, p, q);
            cout << "==reverse==" << endl;
            merge_sort(arr, q+1, r);
            merge(arr, p, q, r);
        }
    
        return 0;
    }
    
    } // end of namespace crafet
    } // end of namespace sort

    2、以大堆为例,先上代码

    #include <iostream>
    using namespace std;
    
    
    namespace crafet {
    namespace sort {
    
    class CrafetHeapSort {
    public:
    
        CrafetHeapSort();
        virtual ~CrafetHeapSort();
    
    public:
    
        int init();
    
        int set_arr_len(int arr_len);
        /**
         * @brief load data into array
         */
        int load_data(int* given_arr, int arr_len);
    
        /**
         * @brief adjust parent node to build local max heap
         * @return 0 succ, others -1
         */
        int adjust_max_heap(int parent);
    
        /**
         * @brief build a max heap given array
         */
        int build_max_heap();
    
        /**
         * @brief max heap sort for array
         */
        int max_heap_sort();
    private:
    
        int arr_swap(int left, int right);
    
        int print_heap_arr();
    private:
    
        int* _heap_arr;
        int _arr_len;
        
    
    };
    
    } // end of namespace sort
    } // end of namespace crafet
    #include "heap_sort.h"
    
    namespace crafet {
    namespace sort {
    
    CrafetHeapSort::CrafetHeapSort()
    {
    }
    
    CrafetHeapSort::~CrafetHeapSort()
    {
    }
    
    int CrafetHeapSort::init()
    {
        _heap_arr = NULL;
        _arr_len = 1;
    }
    
    int CrafetHeapSort::set_arr_len(int arr_len)
    {
        _arr_len = arr_len;
    }
    
    int CrafetHeapSort::load_data(int* given_arr, int arr_len)
    {
        if (NULL == given_arr) {
            cout << "given_arr is null" << endl;
        }
    
        _heap_arr = new int[arr_len];
    
        memset(_heap_arr, 0, sizeof(int) * arr_len);
    
        for (int i = 0; i < arr_len; i++) {
            *(_heap_arr + i) = given_arr[i];
        }
    
        set_arr_len(arr_len);
    
        print_heap_arr();
    
        return 0;
    }
    
    int CrafetHeapSort::adjust_max_heap(int parent)
    {   
        if (NULL == _heap_arr) {
            cout << "arr is null" << endl;
            return -1;
        }
    
        if (parent >= _arr_len) {
            cout << "index [" << parent << "] exceed bound" << endl;
            return -1;
        }
    
        int largest = parent;
        
        int left = (parent << 1) + 1;
        int right = (parent << 1) + 2;
    
        cout << "left " << left << " right " << right << endl; 
        int key = _heap_arr[parent];
        if (left < _arr_len && _heap_arr[left] > key) {
            largest = left;
        }
    
        if (right < _arr_len && _heap_arr[right] > _heap_arr[largest]) {
            largest = right;
        }
    
        cout << "largest " << largest << endl;
        //two if above tiggered
        if (largest != parent) {
            arr_swap(parent, largest);
            
            print_heap_arr();
            //adjust changed child node
            adjust_max_heap(largest);
    
        }
    
        return 0;
    }
    
    int CrafetHeapSort::arr_swap(int left, int right)
    {
        if (left >= _arr_len || right >= _arr_len) {
            cout << "index exceed" << endl;
            return -1;
        }
    
        int tmp = 0;
        tmp = _heap_arr[left];
        _heap_arr[right] = _heap_arr[left];
        tmp = _heap_arr[right];
    
        return 0;
    }
    
    int CrafetHeapSort::print_heap_arr()
    {
        cout << "===== start to print heap =====" << endl;
        for (int i = 0; i < _arr_len ; i++) {
            cout << _heap_arr[i] << " ";
        }
    
        cout << endl;
    
        cout << "===== end to print heap =====" << endl;
        return 0;
    }
    
    
    } // end of namespace sort
    } // end of namespace crafet

    在编码过程中遇到一个bug,

    在最初的编码为:

        //two if above tiggered
        if (largest != parent) {
            arr_swap(parent, largest);
        
        }   
            
        print_heap_arr();
        //adjust changed child node
        adjust_max_heap(largest);

    明显,当父节点与子节点并没有交换的时候,即可以正常退出的时候,还要继续进行尾递归,结果造成死循环,所以

    需要尾递归的前提是,有节点发生交换。

    即应该是

        //two if above tiggered
        if (largest != parent) {
            arr_swap(parent, largest);
            
            print_heap_arr();
            //adjust changed child node
            adjust_max_heap(largest);
    
        }

    同时发现,largest成员变量的初始化也有微妙的trick,

    最初初始化形式为:

    int largest = 0;

    这里会带来一次多余的比较,即当parent并没有任何子节点进行swap的时候,此时largest=0,这样largest != parent 就会成立,

    因为会发生一次,与堆顶元素的交换,这次交换明显是不对的。

    因此,调整largest的初始化形式:

    int largest = parent;

    这样,当父节点与子节点无任何swap的时候, largest仍然等于parent,此时不满足再次递归的条件,从而正确退出。

    给定如下的测试用例:

    #include "heap_sort.h"
    
    using namespace crafet::sort;
    
    int main()
    {
        int arr[] = {12, 3, 6, 8, 21, 24, 17, 9, 2};
    
        CrafetHeapSort chs;
    
        chs.init();
    
        int arr_len = 9;
        chs.load_data(arr, 9);
    
        chs.adjust_max_heap(3);
    }

    这里为了测试,许多地方编码没有考虑更多的扩展,如:用构造的数组来初始化堆数组,更有扩展性的设计应该是数据放入文件,load即可,这不是当前的重点,因此仅仅给出测试用例。

    当前的堆排序,仅仅实现到对某一个节点进行调整形成局部的最大堆,后面会继续基于此进行扩展,建立完成的最大堆,同时,实现堆排序。

  • 相关阅读:
    [CareerCup] 4.6 Find Next Node in a BST 寻找二叉搜索树中下一个节点
    Android 接入支付宝支付实现
    Android 设置软键盘搜索键以及监听搜索键点击事件
    Android 应用监听自身卸载,弹出用户反馈调查
    ndk制作so库,ndk-build不是内部或外部命令。。。的错误
    Error: Your project contains C++ files but it is not using a supported native build system
    Android开发之——依赖冲突Program type already present
    基于Python的开源人脸识别库:离线识别率高达99.38%
    Android5.0以后,materialDesign风格的加阴影和裁剪效果
    Android 5.0 以上监听网络变化
  • 原文地址:https://www.cnblogs.com/crafet/p/3102859.html
Copyright © 2020-2023  润新知