C++学习笔记_21 优先级队列实现-堆积树-堆排序 2021-05-24

//C++学习笔记_21 优先级队列实现-堆积树-堆排序
#include<vector>
#include<iostream>
#include<string>
#include<deque>
#include<queue>
using namespace std;

class MyPriQue_V0
{
private:
    vector<int>  elems; //使用 vector 来存储优先级队列中的数据
public:
    MyPriQue_V0(){}

    int size() { return elems.size();}
    bool empty() { return elems.empty();}

    void push(int x)
    {
        //1:  把新插入元素 x  放在最后面
        elems.push_back(x);

        int iChild = size() - 1; //x这个节点的下标
        int iFather = (iChild - 1) / 2; //它的父节点下标

        while (iChild > 0) {//iChlid 是根节点了,就不要往上找了
            //2:x 与 它的父节点比较        
            if (elems[iChild] > elems[iFather]){
                //2 - 1:大于父节点,则 交换 x 和 父节点的值
                int tmp = elems[iChild];
                elems[iChild] = elems[iFather];
                elems[iFather] = tmp;
            }
            else break;//2 - 2:小于等于父节点, 则  x  元素插入完成
            //3: 循环执行:变量轮换,重新找父节点
            iChild = iFather; //交换完成后,继续往上找父节点
            iFather = (iChild - 1) / 2;
        }//3:循环执行 第二步,直到x位于根节点        
    }

    void pop(){ 
		//1: 交换 首尾 元素, 删除最后一个元素(最大的值)
        swap(elems.front(), elems.back());
        elems.pop_back();

        int iFather = 0; //这是需要调整的节点下标
        int iChild = 1;  //左子节点 

        while (iChild < size()) {//存在子节点的情况才进行比较,无子节点就没有比较的必要了
            //2: 调整堆:根节点元素 x 和 左右两个子节点中最大的 subMax 进行比较
            //我们需要找左右两个子节点中最大的(右子节点有可能不存在)
            if (iChild + 1 < size() && elems[iChild + 1] > elems[iChild]){
                //右子节点既存在,也大于左子节点,则子节点去右子节点
                iChild++;
            }
            //2 - 1:如果 x 大于等于 subMax ,结束调整(已经是大堆)
            if (elems[iFather] >= elems[iChild]) break;
            else{//2 - 2:如果 subMax 比 x 大,交换 x  和 subMax
                int tmp = elems[iFather];
                elems[iFather] = elems[iChild];
                elems[iChild] = tmp;
            }
            //3: 循环第二步,变量轮换,继续找下一个子节点
            iFather = iChild;
            iChild = 2 * iFather + 1; //左子节点
        }//4: 循环第二步,直到 x 没有子节点
    }

    int& top() { return elems.front();}//返回堆顶的元素

    void Print(){
        vector<int>::iterator it;
        for (it = elems.begin(); it != elems.end(); it++){
            cout << *it << " ";
        }
        cout << endl;
    }
};

//写成模板形式
template<typename T>
class MyPriQue_V1
{
private:
    vector<T>  elems; //使用 vector 来存储优先级队列中的数据
public:
    MyPriQue_V1(){}

    int size() { return elems.size();}
    bool empty() { return elems.empty();}

    void push(T x) {
        //1:  把新插入元素 x  放在最后面
        elems.push_back(x);

        int iChild = size() - 1; //x这个节点的下标
        int iFather = (iChild - 1) / 2; //它的父节点下标

        while (iChild > 0) {//iChlid 是根节点了,就不要往上找了
            //2:x 与 它的父节点比较        
            if (elems[iChild] > elems[iFather]) {
                //2 - 1:大于父节点,则 交换 x 和 父节点的值
                T tmp = elems[iChild];
                elems[iChild] = elems[iFather];
                elems[iFather] = tmp;
            }
            else break;//2 - 2:小于等于父节点, 则  x  元素插入完成

            //3: 循环执行:变量轮换,重新找父节点
            iChild = iFather; //交换完成后,继续往上找父节点
            iFather = (iChild - 1) / 2;

        }//4:循环执行 第二步,直到x位于根节点        
    }

    void pop(){//1: 交换 首尾 元素, 删除最后一个元素(最大的值)
        swap(elems.front(), elems.back());
        elems.pop_back();

        int iFather = 0; //这是需要调整的节点下标
        int iChild = 1;  //左子节点 

        while (iChild < size()) {//存在子节点的情况才进行比较,无子节点就没有比较的必要了
            //2: 调整堆:根节点元素 x 和 左右两个子节点中最大的 subMax 进行比较
            //我们需要找左右两个子节点中最大的(右子节点有可能不存在)
            if (iChild + 1 < size() && elems[iChild + 1] > elems[iChild]){
                //右子节点既存在,也大于左子节点,则子节点去右子节点
                iChild++;
            }
            //2 - 1:如果 x 大于等于 subMax ,结束调整(已经是大堆)
            if (elems[iFather] >= elems[iChild]) break;
            else{//2 - 2:如果 subMax 比 x 大,交换 x  和 subMax
                T tmp = elems[iFather];
                elems[iFather] = elems[iChild];
                elems[iChild] = tmp;
            }
            //3: 循环第二步,变量轮换,继续找下一个子节点
            iFather = iChild;
            iChild = 2 * iFather + 1; //左子节点
        }//4: 循环第二步,直到 x 没有子节点
    }

    T& top()   { return elems.front();}//返回堆顶的元素

    void Print(){
        typename vector<T>::iterator it;
        for (it = elems.begin(); it != elems.end(); it++){
            cout << *it << " ";
        }
        cout << endl;
    }
};


//流程优化
//插入:只需要找新插入节点的最终位置
//      找最终位置的过程,把这条线路的节点下移 (V1版本的新节点上移)
//删除:只需要找新的根节点的最终位置
//      找最终位置的过程,把这条线路上的节点上移 (V1 版本的头结点下移)
template<typename T>
class MyPriQue_V2
{
private:
    vector<T>  elems; //使用 vector 来存储优先级队列中的数据
public:
    MyPriQue_V2(){}

    int size() { return elems.size();}
    bool empty() { return elems.empty();}

    void push(T x) { //1:  把新插入元素 x  放在最后面
        elems.push_back(x); //或者 elems.resize(size()+1); 扩充一个元素

        int iChild = size() - 1; //最后一个节点的下标
        int iFather = (iChild - 1) / 2; //它的父节点下标

        while (iChild > 0) {//iChlid 是根节点了,就不要往上找了
            //2:x 与 当前节点的父节点比较        
            if (x > elems[iFather]){
                //流程优化,不做交换,只是把父节点下移
                //2 - 1:大于父节点,则 把父节点下移
                elems[iChild] = elems[iFather];
            }
            else break;//2 - 2:小于等于父节点, 则  x  元素插入完成

            //3: 循环执行:变量轮换,重新找父节点
            iChild = iFather; //交换完成后,继续往上找父节点
            iFather = (iChild - 1) / 2;
        }//3:循环执行 第二步,直到x位于根节点        
        //循环完成后,iChild 位置,就是 x 这个值的需要保存的最终位置
        elems[iChild] = x;
    }    

    void pop(){
        //1: 交换 首尾 元素, 删除最后一个元素(最大的值)
        //先不删除最后一个节点,用于暂存最终需要保存的元素
        //处理完毕后,再删除

        int iFather = 0; //这是需要调整的节点下标
        int iChild = 1;  //左子节点 

        while (iChild < size()) {//存在子节点的情况才进行比较,无子节点就没有比较的必要了
            //2: 调整堆:根节点元素 x 和 左右两个子节点中最大的 subMax 进行比较
            //我们需要找左右两个子节点中最大的(右子节点有可能不存在)
            if (iChild + 1 < size() && elems[iChild + 1] > elems[iChild]){
                //右子节点既存在,也大于左子节点,则子节点去右子节点
                iChild++;
            }
            if (elems.back() >= elems[iChild]) break;//直接个最后一个节点比较
            else  elems[iFather] = elems[iChild];
            //3: 循环第二步,变量轮换,继续找下一个子节点
            iFather = iChild;
            iChild = 2 * iFather + 1; //左子节点
        }//4: 循环第二步,直到 没有子节点
        elems[iFather] = elems.back();
        elems.pop_back();//最后,删除末尾节点
    }

    T& top()  { return elems.front();}//返回堆顶的元素

    void Print() {
        typename vector<T>::iterator it;
        for (it = elems.begin(); it != elems.end(); it++) {
            cout << *it << " ";
        }
        cout << endl;
    }
};

//统一比较运算符,元素比较,全部用 < 符号来比较
//底层容器使用模板
template<typename T, typename Container=vector<T>>
class MyPriQue_V3
{
private:
    Container  elems; //使用 vector 来存储优先级队列中的数据
public:
    MyPriQue_V3(){}

    int size() { return elems.size();}
    bool empty() { return elems.empty();}

    void push(T x) {
        //1:  把新插入元素 x  放在最后面
        elems.push_back(x); //或者 elems.resize(size()+1); 扩充一个元素

        int iChild = size() - 1; //最后一个节点的下标
        int iFather = (iChild - 1) / 2; //它的父节点下标

        while (iChild > 0) { //iChlid 是根节点了,就不要往上找了
            //2:x 与 当前节点的父节点比较        
            if (elems[iFather] < x) {
                //流程优化,不做交换,只是把父节点下移
                //2 - 1:大于父节点,则 把父节点下移
                elems[iChild] = elems[iFather];
            }
            else  break; //2 - 2:小于等于父节点, 则  x  元素插入完成
            //3: 循环执行:变量轮换,重新找父节点
            iChild = iFather; //交换完成后,继续往上找父节点
            iFather = (iChild - 1) / 2;
        }//4:循环执行 第二步,直到x位于根节点        
        //循环完成后,iChild 位置,就是 x 这个值的需要保存的最终位置
        elems[iChild] = x;
    }

    void pop(){
        //1: 交换 首尾 元素, 删除最后一个元素(最大的值)
        //先不删除最后一个节点,用于暂存最终需要保存的元素
        //处理完毕后,再删除

        int iFather = 0; //这是需要调整的节点下标
        int iChild = 1;  //左子节点 

        while (iChild < size()) //存在子节点的情况才进行比较,无子节点就没有比较的必要了
        {
            //2: 调整堆:根节点元素 x 和 左右两个子节点中最大的 subMax 进行比较
            //我们需要找左右两个子节点中最大的(右子节点有可能不存在)
            if (iChild + 1 < size() && elems[iChild] < elems[iChild+1]){
                //右子节点既存在,也大于左子节点,则子节点去右子节点
                iChild++;
            }
            if (!(elems.back() < elems[iChild]))   break;//直接个最后一个节点比较
            else elems[iFather] = elems[iChild];
            //3: 循环第二步,变量轮换,继续找下一个子节点
            iFather = iChild;
            iChild = 2 * iFather + 1; //左子节点
        }//3: 循环第二步,直到 没有子节点
        elems[iFather] = elems.back();
        elems.pop_back();//最后,删除末尾节点
    }

    T& top() { return elems.front();} //返回堆顶的元素

    void Print() {
        typename Container::iterator it;
        for (it = elems.begin(); it != elems.end(); it++){
            cout << *it << " ";
        }
        cout << endl;
    }
};

//增加比较规则,使用 仿函数,作为一个模板类型
template<typename T, typename Container = vector<T>, typename RULE=less<T>>
class MyPriQue_V4
{
private:
    Container  elems; //使用 vector 来存储优先级队列中的数据
public:
    MyPriQue_V4(){}

    int size() { return elems.size();}
    bool empty() { return elems.empty(); }

    void push(T x) {
        //1:  把新插入元素 x  放在最后面
        elems.push_back(x); //或者 elems.resize(size()+1); 扩充一个元素

        int iChild = size() - 1; //最后一个节点的下标
        int iFather = (iChild - 1) / 2; //它的父节点下标

        while (iChild > 0){ //iChlid 是根节点了,就不要往上找了
            //2:x 与 当前节点的父节点比较        
            if (/*elems[iFather] < x*/
                RULE()(elems[iFather], x)
                ) //构造堆使用的比较规则,比较规则改变了,则堆也改变了
            {
                //流程优化,不做交换,只是把父节点下移
                //2 - 1:大于父节点,则 把父节点下移
                elems[iChild] = elems[iFather];
            }
            else break;//2 - 2:小于等于父节点, 则  x  元素插入完成
            //3: 循环执行:变量轮换,重新找父节点
            iChild = iFather; //交换完成后,继续往上找父节点
            iFather = (iChild - 1) / 2;
        }//3:循环执行 第二步,直到x位于根节点        
        //循环完成后,iChild 位置,就是 x 这个值的需要保存的最终位置
        elems[iChild] = x;
    }

    void pop(){
        //1: 交换 首尾 元素, 删除最后一个元素(最大的值)
        //先不删除最后一个节点,用于暂存最终需要保存的元素
        //处理完毕后,再删除

        int iFather = 0; //这是需要调整的节点下标
        int iChild = 1;  //左子节点 

        while (iChild < size()) {//存在子节点的情况才进行比较,无子节点就没有比较的必要了
            //2: 调整堆:根节点元素 x 和 左右两个子节点中最大的 subMax 进行比较
            //我们需要找左右两个子节点中最大的(右子节点有可能不存在)
            if (iChild + 1 < size() 
                && /*elems[iChild] < elems[iChild + 1]*/
                RULE()(elems[iChild], elems[iChild + 1])
                )
            {
                //右子节点既存在,也大于左子节点,则子节点去右子节点
                iChild++;
            }
            if (/*!(elems.back() < elems[iChild])*/
                !RULE()(elems.back(), elems[iChild])
                )   break;//直接个最后一个节点比较
            else  elems[iFather] = elems[iChild];
            //3: 循环第二步,变量轮换,继续找下一个子节点
            iFather = iChild;
            iChild = 2 * iFather + 1; //左子节点
        }//4: 循环第二步,直到 没有子节点
        elems[iFather] = elems.back();
        elems.pop_back(); //最后,删除末尾节点
    }

    T& top()  { return elems.front(); }//返回堆顶的元素

    void Print(){
        typename Container::iterator it;
        for (it = elems.begin(); it != elems.end(); it++){
            cout << *it << " ";
        }
        cout << endl;
    }
};

//增加比较规则,使用 仿函数,作为一个模板类型
//支持函数指针的使用
template<typename T, typename Container = vector<T>, typename RULE = less<T>>
class MyPriQue
{
private:
    RULE  myRule;  //定义私有成员来存储比较规则
                   //如果是仿函数  --》 生成一个默认的对象
                   //如果是函数指针--》 定义一个函数指针,构造的时候,函数指针赋值
    Container  elems; //使用 vector 来存储优先级队列中的数据
public:
    MyPriQue(){}
    MyPriQue(RULE rule) :myRule(rule){} //构造函数初始化比较规则

    int size(){ return elems.size(); }
    bool empty() { return elems.empty(); }

    void push(T x) {
        //1:  把新插入元素 x  放在最后面
        elems.push_back(x); //或者 elems.resize(size()+1); 扩充一个元素

        int iChild = size() - 1; //最后一个节点的下标
        int iFather = (iChild - 1) / 2; //它的父节点下标

        while (iChild > 0) //iChlid 是根节点了,就不要往上找了
        {
            //2:x 与 当前节点的父节点比较        
            if (/*elems[iFather] < x*/
                myRule(elems[iFather], x)
                ) //构造堆使用的比较规则,比较规则改变了,则堆也改变了
            {
                //流程优化,不做交换,只是把父节点下移
                //2 - 1:大于父节点,则 把父节点下移
                elems[iChild] = elems[iFather];
            }
            else  break;//2 - 2:小于等于父节点, 则  x  元素插入完成
            //3: 循环执行:变量轮换,重新找父节点
            iChild = iFather; //交换完成后,继续往上找父节点
            iFather = (iChild - 1) / 2;
        }//3:循环执行 第二步,直到x位于根节点        
        //循环完成后,iChild 位置,就是 x 这个值的需要保存的最终位置
        elems[iChild] = x;
    }

    void pop()
    {
        //1: 交换 首尾 元素, 删除最后一个元素(最大的值)
        //先不删除最后一个节点,用于暂存最终需要保存的元素
        //处理完毕后,再删除

        int iFather = 0; //这是需要调整的节点下标
        int iChild = 1;  //左子节点 

        while (iChild < size()) //存在子节点的情况才进行比较,无子节点就没有比较的必要了
        {
            //2: 调整堆:根节点元素 x 和 左右两个子节点中最大的 subMax 进行比较
            //我们需要找左右两个子节点中最大的(右子节点有可能不存在)
            if (iChild + 1 < size()
                && /*elems[iChild] < elems[iChild + 1]*/
                myRule(elems[iChild], elems[iChild + 1])
                )
			{
                //右子节点既存在,也大于左子节点,则子节点去右子节点
                iChild++;
            }
            if (/*!(elems.back() < elems[iChild])*/
                !myRule(elems.back(), elems[iChild])
                )  break;//直接个最后一个节点比较
            else  elems[iFather] = elems[iChild];
            //3: 循环第二步,变量轮换,继续找下一个子节点
            iFather = iChild;
            iChild = 2 * iFather + 1; //左子节点
        }//3: 循环第二步,直到 没有子节点
        elems[iFather] = elems.back();
        elems.pop_back();//最后,删除末尾节点
    }

    T& top()  { return elems.front(); }//返回堆顶的元素

    void Print(){
        typename Container::iterator it;
        for (it = elems.begin(); it != elems.end(); it++){
            cout << *it << " ";
        }
        cout << endl;
    }
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class AAA
{
private:
    int x;
    int y;
public:
    AAA() :x(0), y(0){}
    AAA(int a, int b) :x(a), y(b){}

    bool operator < (const AAA &A) const { return x < A.x;}

    friend ostream& operator << (ostream& os, const AAA& A){
        os << "(" << A.x << ", " << A.y << ")";
        return os;
    }
};

class MyRule
{
public:
    bool operator ()(int x, int y) {  return y < x; }
};

bool MyComp(int x, int y) { return y < x;}

void TestMyPriQue()
{
    MyPriQue<int>  iPriQue;
    MyPriQue<string, deque<string>> sPriQue;

    MyPriQue<int, vector<int>, MyRule>  iPriQue2;

    int arr[] = { 1, 3, 5, 2, 4, 6, 7, 8, 9 };
    string sArr[] = { "AAA", "BBBB", "CC", "aa", "bbb", "cccc" };    

    cout << "iPriQue 入队列: ";
    for (int i = 0; i < sizeof(arr)/sizeof(arr[0]); i++) {
        cout << arr[i] << " ";
        iPriQue.push(arr[i]);
        iPriQue2.push(arr[i]);
    }
    cout << endl;

    cout << "sPriQue 入队列: ";
    for (int i = 0; i < sizeof(sArr) / sizeof(sArr[0]); i++) {
        cout << sArr[i] << " ";
        sPriQue.push(sArr[i]);
    }
    cout << endl;   

    /*
    cout << "大堆  : ";
    iPriQue.Print();
    sPriQue.Print();
    cout << endl;
    */

    cout << "iPriQue 出队列: ";
    while (!iPriQue.empty()) {
        cout << iPriQue.top() << " ";
        iPriQue.pop();
    }
    cout << endl; 

    cout << "iPriQue2出队列: ";
    while (!iPriQue2.empty()) {
        cout << iPriQue2.top() << " ";
        iPriQue2.pop();
    }
    cout << endl;

    cout << "sPriQue 出队列: ";
    while (!sPriQue.empty()) {
        cout << sPriQue.top() << " ";
        sPriQue.pop();
    }
    cout << endl;
    cout << endl;

    ///*
    //二进制“>”:“AAA”不定义该运算符或到预定义运算符可接收的类型的转换
    MyPriQue<AAA>    aPriQue;
    AAA  aArr[] = { AAA(1, 0), AAA(), AAA(0, 1), AAA(1, 2), AAA(1, 1) };

    cout << "aPriQue入队列:";
    for (int i = 0; i < sizeof(aArr) / sizeof(aArr[0]); i++) {
        cout << aArr[i] << " ";
        aPriQue.push(aArr[i]);
    }
    cout << endl;

    cout << "aPriQue出队列:";
    while (!aPriQue.empty()) {
        cout << aPriQue.top() << " ";
        aPriQue.pop();
    }
    cout << endl;
    //*/

    //没有这个构造函数
    //MyPriQue_V4<int, vector<int>, bool(*)(int, int)> iPriQue3(MyComp);
    MyPriQue<int, vector<int>, bool(*)(int, int)> iPriQue3(MyComp);
    cout << "iPriQue3入队列: ";
    for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) {
        cout << arr[i] << " ";
        iPriQue3.push(arr[i]);
    }
    cout << endl;

    cout << "iPriQue3入队列: ";
    while (!iPriQue3.empty()) {
        cout << iPriQue3.top() << " ";
        iPriQue3.pop();
    }
    cout << endl;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//作业:
//定义 4 个优先级队列,存储string对象  {"AA", "BBBB", "CCC", "aaaa", "cc", "bbb"}
//要求:
//sPriQue1: 出队列的时候,按照字符串 ASCII 降序排列
//sPriQue2: 出队列的时候,按照字符串 ASCII 升序排列
//sPriQue3: 出队列的时候,按照字符串长度 升序排列(长度一样再比较 ASCII 升序)
//sPriQue4: 出队列的时候,按照字符串长度 降序排列(长度一样再比较 ASCII 降序)
class MyRuleS_2
{
public:
    bool operator ()(const string& s1, const string& s2) const
    { return s2 < s1; }
};

class MyRuleS_3
{
public:
    bool operator ()(const string& s1, const string& s2) const{
        if (s1.length() == s2.length()) {  return s2 < s1; }
        else { return s2.length() < s1.length(); }
    }
};

class MyRuleS_4
{
public:
    bool operator ()(const string& s1, const string& s2) const {
        if (s1.length() == s2.length())  
			return s1 < s2; 
        else
            return s1.length() < s2.length();
    }
};

void HomeWork()
{
    string sArr[] = { "AA", "BBBB", "CCC", "aaaa", "cc", "bbb" };
    MyPriQue_V4<string>   sPriQue1;
    MyPriQue_V4<string, vector<string>, MyRuleS_2>   sPriQue2;
    MyPriQue_V4<string, vector<string>, MyRuleS_3>   sPriQue3;
    MyPriQue_V4<string, vector<string>, MyRuleS_4>   sPriQue4;

    for (int i = 0; i < sizeof(sArr) / sizeof(sArr[0]); i++) {
        sPriQue1.push(sArr[i]);
        sPriQue2.push(sArr[i]);
        sPriQue3.push(sArr[i]);
        sPriQue4.push(sArr[i]);
    }

    cout << "sPriQue1:";
    while (!sPriQue1.empty()) {
        cout << sPriQue1.top() << " ";
        sPriQue1.pop();
    }
    cout << endl;

    cout << "sPriQue2:";
    while (!sPriQue2.empty()) {
        cout << sPriQue2.top() << " ";
        sPriQue2.pop();
    }
    cout << endl;

    cout << "sPriQue3:";
    while (!sPriQue3.empty()) {
        cout << sPriQue3.top() << " ";
        sPriQue3.pop();
    }
    cout << endl;

    cout << "sPriQue4:";
    while (!sPriQue4.empty()) {
        cout << sPriQue4.top() << " ";
        sPriQue4.pop();
    }
    cout << endl;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//堆排序的实现:实现升序排列
//第一步:把序列构造成大堆
//        从最后一个父节点开始,做调整,把这个父节点看成子树的根节点,这个子树调整成大堆
//        依次往上,知道把根节点调整完毕
//第二步:从最后一个节点开始首位交换,调整堆 (排除最后的节点)
//        直到  堆 只剩一个元素

//作业,完成下面两个函数  HeapSort 实现对 arr 数组的堆排序
void HeapFixDown(int  arr[], int iSize, int iPos)
{
    //把 arr 数组的 iPos 位置元素下沉,确保iPos 节点往下,都是大堆
    //最多下沉到 iSize位置
    int iFather = iPos;  
    int iChild = iPos * 2 + 1; //左子节点

    int iTmp = arr[iPos]; //待寻找位置的值

    while (iChild < iSize) {
        //右子节点是否存在,右子节点是否大于左子节点
        if (iChild + 1 < iSize && arr[iChild] < arr[iChild + 1]) {
            iChild++; //取右子节点进行交换 (最大的子节点)
        }

        //优化方案:所谓的交换,只需要把最大的子节点上移
        //          最终位置,保存 arr[iPos]
        if (iTmp < arr[iChild]) 
            arr[iFather] = arr[iChild];
        else  break;
        //变量置换,继续找下一个最大的子节点
        iFather = iChild;
        iChild = 2 * iFather + 1;
    }
    //最后,无论是break ,还是 (iChild < iSize) 为false
    //iFather 的位置就是  arr[iPos] 应该保存的位置
    arr[iFather] = iTmp;
}

void  HeapSort(int arr[], int iSize)
{
    //从最后一个父节点开始,调用 HeapFixDown
    //从最后一个节点开始,交换到根节点,并 调用 HeapFixDown下沉根节点

    //数组堆化: 从最后一个父节点开始,到根节点结束,一次把这个子树,做下沉操作
    //最后一个父节点 --- 最后一个节点的父节点
    // size - 1  ---> (size-1-1)/2  = size/2 - 1
    for (int i = iSize / 2 - 1; i >= 0; i--)
        HeapFixDown(arr, iSize, i);
    //--> arr 变成大堆的形式

    //堆排序:
    //1: 把最大的交换到末尾
    //2: 根节点下沉
    //3: 末尾位置前移,循环
    for (int i = iSize - 1; i > 0; i--){
        std::swap(arr[0], arr[i]); //交换首尾
        HeapFixDown(arr, i, 0);//把根节点,下沉到 i - 1 位置 
    }
    //HeapFixDown 时间复杂度 O(log(N))
    //所以 HeapSort 的时间复杂度是 O(N*log(N))
    //冒泡,选择 --》时间复杂度是 O(N^2)
}
//留一个问题: 怎么修改这个函数,使它支持升序,降序,或者自定义排序?

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

    HeapSort(arr, sizeof(arr) / sizeof(arr[0]));

    for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main()
{
    //TestMyPriQue();
    //HomeWork();
    TestHeapSort();
    system("pause");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/lybc2019/article/details/117225227