小大根交替堆

基于论文“Min-Max Heaps and Generalized Priority Queues”

  • 定义
  • 抽象数据类型
  • 源代码
  • 截图
  • 应用

定义

values stored at nodes on even (odd) levels are smaller than or equal to (respectively, greater than) values stored at their descendants.

抽象数据类型

template<class T>
class min_max_heap{
public:
    min_max_heap(int initialCapacity = 10);//默认参数为10
    ~min_max_heap() { delete[] heap; }//析构函数
    bool empty() const { return heapSize == 0; }//判断堆是否为空
    int size() const {//返回堆的大小
        return heapSize;
    }
    T* getHeap() const {
        return heap;
    }

    void  initialize(T *theHeap,int theSize);//根据已知数组建堆
    const T& getMax();//得到最大值
    const T& getMin();//得到最小值
    void insert(T&);//插入值
    const T& popMin();//删除最小值
    const T& popMax();//删除最大值
    void output(ostream& out);//输出
public:
    int heapSize;//堆大小
    int arrayLength;//堆的最大值
    T *heap;//堆数组
};

源代码

template<class T>
min_max_heap<T>::min_max_heap(int initialCapacity) {//构造函数
    if (initialCapacity < 1) {//初始大小必须大于0
        ostringstream s;
        s << "Error : Initial capacity = " << initialCapacity << " Must be > 0";
        throw s.str();
    }
    arrayLength = initialCapacity + 1;//
    heap = new T[arrayLength];
    heapSize = 0;
}

int is_min_level(int i) {//返回下标为i的数据的层(大层还是小层)
    if ((int)(floor(log(i) / log(2))) % 2) {
        return 0;
    }
    else {
        return 1;
    }
}

template<class T>
int index_min_child_grandchild(min_max_heap<T>* h ,int i) {//找出下标为i的结点的儿子和孙子中最小的值
    int a = first_child(i);
    int b = second_child(i);
    int d = second_child(a);
    int c = first_child(a);
    int f = second_child(b);
    int e = first_child(b);

    int min_idx = -1;
    if (a <= h->heapSize) min_idx = a;
    if (b <= h->heapSize && h->heap[b] < h->heap[min_idx]) min_idx = b;
    if (c <= h->heapSize && h->heap[c] < h->heap[min_idx]) min_idx = c;
    if (d <= h->heapSize && h->heap[d] < h->heap[min_idx]) min_idx = d;
    if (e <= h->heapSize && h->heap[e] < h->heap[min_idx]) min_idx = e;
    if (f <= h->heapSize && h->heap[f] < h->heap[min_idx]) min_idx = f;

    return min_idx;
}

template<class T>
int index_max_child_grandchild(min_max_heap<T>* h, int i) {//找出下标为i的结点的儿子和孙子中最小的值
    int a = first_child(i);
    int b = second_child(i);
    int d = second_child(a);
    int c = first_child(a);
    int f = second_child(b);
    int e = first_child(b);

    int max_idx = -1;
    if (a <= h->heapSize) max_idx = a;
    if (b <= h->heapSize && h->heap[b] > h->heap[max_idx]) max_idx = b;
    if (c <= h->heapSize && h->heap[c] > h->heap[max_idx]) max_idx = c;
    if (d <= h->heapSize && h->heap[d] > h->heap[max_idx]) max_idx = d;
    if (e <= h->heapSize && h->heap[e] > h->heap[max_idx]) max_idx = e;
    if (f <= h->heapSize && h->heap[f] > h->heap[max_idx]) max_idx = f;

    return max_idx;
}

template<class T>
void swap(min_max_heap<T>* h, int i, int m) {//交换堆中下标为i和m的数据
    T temp = h->heap[i];
    h->heap[i] = h->heap[m];
    h->heap[m] = temp;
if(flagPrint)
cout << "     heap[" << i << "]:" << h->heap[m] << "<-->" << "heap[" << m << "]:" << h->heap[i] << endl <<"h:\n"<< *h << endl;
}


/*
小层下滤,先找到自己儿子和孙子中最小的值。
如果该值为儿子,证明自己没有孙子,这时只需比较该最小值与自己,如果该最小值小,则与自己交换位置,下滤结束;否则不变化,下滤结束;
如果该值为孙子,则比较该最小值与自己,如果该最小值大于自己,则证明以该元素为根节点的树的最小值就是该元素,下滤完成;否则如果该最小值小于自己,则进行交换位置,交换后判断自己与父亲结点的大小关系:
    如果父亲节点(大层)小于自己,则交换位置,交换后只有该父亲节点元素位置不正确,所以对父亲节点元素重新进行下滤;
    否则父亲节点大于自己,则对自己重新进行下滤操作。
*/
template<class T>
void TrickleDownMin(min_max_heap<T>* h,int i) {//小层下滤(用于删除)

    int m = index_min_child_grandchild(h,i);//得到儿子和孙子中最小的值
    if (m <= -1)  return;

    if (m > second_child(i)) {//如果孙子最小
        if (h->heap[m] < h->heap[i]) {
            swap(h, i, m);
            if (h->heap[m] > h->heap[parent(m)]) {
                swap(h, m, parent(m));
            }
        TrickleDownMin(h, m);
        }

    }
    else {//如果儿子最小
        if (h->heap[m] < h->heap[i]) {
            swap(h, i, m);
        }
    }
}

template<class T>
void TrickleDownMax(min_max_heap<T>* h,int i) {//大层下滤(用于删除)

    int m = index_max_child_grandchild(h, i);//得到儿子和孙子最大值
    if (m <= -1) {
        return;
    }
    if (m > second_child(i)) {//如果孙子最大
        if (h->heap[m] > h->heap[i]) {
            swap(h, i, m);

            if (h->heap[m] < h->heap[parent(m)]) {
                swap(h, m, parent(m));
            }
            TrickleDownMax(h, m);
        }
    }
    else {//如果儿子最大
        if (h->heap[m] > h->heap[i]) {
            swap(h, i, m);
        }
    }
}

template<class T>
void trickleDown(min_max_heap<T>* h,int i) {//下滤,用于删除
//cout << "先判断是删除了最小元素,还是最大元素" << endl;
    if (is_min_level(i)) {
        TrickleDownMin(h, i);
    }
    else {
        TrickleDownMax(h, i);
    }

}

template<class T>
void bubbleup_min(min_max_heap<T>* h, int i) {//小层上滤

    int pp_i = parent(parent(i));
    if (pp_i <= 0) return;
    if (h->heap[i]<h->heap[pp_i]) {//如果祖父比自己大,则交换
        swap(h, i, pp_i);
        bubbleup_min(h, pp_i);
    }

}

template<class T>
void bubbleup_max(min_max_heap<T>* h, int i) {//大层上滤

    int pp_i = parent(parent(i));
    if (pp_i <= 0) return;
    if (h->heap[i]>h->heap[pp_i]) {//如果祖父比自己小则交换
        swap(h, i, pp_i);
        bubbleup_max(h, pp_i);
    }

}

template<class T>
void BubbleUp(min_max_heap<T>* h, int i) {//上滤,可用于插入
    int p_i = parent(i);
    if (p_i <= 0) return;
    if (is_min_level(i)) {//如果是小层,进行父亲节点与自己的值大小比较
        if (h->heap[i] > h->heap[p_i]) {//若父节点小,则交换,并进行大层上滤
            swap(h, i, p_i);
            bubbleup_max(h, p_i);
        }
        else {//如果父节点大则进行小层上滤
            bubbleup_min(h, i);
        }
    }
    else {//否则是大层
        if (h->heap[i] < h->heap[p_i]) {//若父亲节点大于自己,则交换,并进行小层上滤
            swap(h, i, p_i);
            bubbleup_min(h, p_i);
        }
        else {//否则父亲节点小于自己,进行大层交换
            bubbleup_max(h, i);
        }
    }
}



template<class T>
void min_max_heap<T>::initialize(T *theHeap, int theSize) {//初始化堆

    delete[] heap;
    heap = theHeap;
    heapSize = theSize;

    for (int root = heapSize / 2; root >= 1; root--)
    {
        trickleDown(this, root);
    }

}



template<class T>
void min_max_heap<T>::insert(T& Element) {//插入
if (flagPrint) {
    cout << "插入操作-插入";
    cout << Element;
    cout << ":" << endl;
}
    //如果堆满可增加数组的长度,此处扩展为原先的2倍;
    if (heapSize == arrayLength - 1) {
        changeArrayLength(heap, arrayLength, 2 * arrayLength);
        arrayLength *= 2;
    }

    int currentNode = ++heapSize;
    heap[currentNode] = Element;
if(flagPrint)
cout <<"h:\n"<<*this<<endl;
    BubbleUp(this, currentNode);
}

template<class T>
const T& min_max_heap<T>::getMax() {//返回最大值
    printf("得到最大值操作:");
    if (heapSize > 2) {//比较heap[2]和heap[3]

        printf("%d\n", heap[2] < heap[3] ? heap[3] : heap[2]);
        return heap[2] < heap[3] ? heap[3] : heap[2];
    }
    if (heapSize == 2) {

        printf("%d\n", heap[2]);
        return heap[2];
    }
    if (heapSize == 1) {

        printf("%d\n", heap[1]);
        return heap[1];
    }
    throw "错误:空堆不能得到最大值\n";

}


template<class T>
const T& min_max_heap<T>::getMin() {//返回根节点(即第一个元素)即最小元素
    if (heapSize > 0) {
cout << "得到最小值操作:" <<heap[1] << endl;
        return heap[1];
    }
    throw "错误:空堆不能得到最小值\n";
    //printf("错误:空堆\n");
    //return NULL;
}

template<class T>
const T& min_max_heap<T>::popMin() {//删除最小值
if (flagPrint)
cout << "删除最小值操作" << endl;
    if (heapSize > 1) {
        T d = heap[1];
//cout << "将根节点" << heap[1] << "(最小值)替换为最后一个元素" << heap[heapSize] << ",开始下滤:" << endl;
        heap[1] = heap[heapSize--];
if(flagPrint)
cout << "heap[1] = "<<heap[1]<< endl <<"h:\n"<<*this << endl;
        trickleDown(this, 1);
        return d;
    }
    if (heapSize == 1) {
if (flagPrint)
cout << "只有一个元素,故删除后堆为空" << endl;
        heapSize--;
        return heap[1];
    }
    throw "错误:空堆不能删除最小值\n";
    //printf("错误:空堆\n");
    //return NULL;
}

template<class T>
const T& min_max_heap<T>::popMax() {//删除最大值
if (flagPrint)
cout << "删除最大值操作" << endl;
    if (heapSize > 2) {
        int index = 2;
        if (heap[2] < heap[3]) index = 3;
        T d = heap[index];
        heap[index] = heap[heapSize--];
if (flagPrint)
cout << "heap["<<index<<"] = " << heap[index] << endl <<"h:\n"<<*this << endl;
        trickleDown(this, index);
        return d;
    }
    if (heapSize == 2) {
if (flagPrint)
cout << "两个元素,返回heap[2]即可" << endl;
        heapSize--;
        return heap[2];
    }
    if (heapSize == 1) {
if (flagPrint)
cout << "只有一个元素,故删除后堆为空" << endl;
        heapSize--;
        return heap[1];
    }
    throw "错误:空堆不能得到最小值\n";
}

//template<class T>

template<class T>
void min_max_heap<T>::output(ostream& out) {//输出堆元素
    T **heapTreeMatrix;
    if (heapSize == 0) throw "Error : empty heap not output!";
    int level = 0;
    int high = (int)(floor(log(heapSize) / log(2)));//high从0层开始
    int bottle_node_count_max = pow(2, high);//最下层最多可以拥有的结点数
    heapTreeMatrix = new T*[high + 1];
    for (int i = 0; i < high + 1; i++) {
        heapTreeMatrix[i] = new T[bottle_node_count_max * 2];
    }

    T *t = new T(INF);
    for (int i = 0; i < high+1 ; i++) {
        for (int j = 0; j < bottle_node_count_max*2; j++) {
            heapTreeMatrix[i][j] = *t;
        }
    }

    int index = 1;
    while (level < high + 1) {
        int inscrease = bottle_node_count_max * 2 / pow(2, level);
        for (int j = inscrease/2 ; j < bottle_node_count_max * 2; j += inscrease) {
            if(index<=heapSize)
            heapTreeMatrix[level][j] = heap[index++];
        }
        level++;
    }

    for (int i = 0; i < high + 1; i++) {
        for (int j = 0; j < bottle_node_count_max * 2; j++) {
            if (heapTreeMatrix[i][j] == *t) {
                printf("    ");
            }
            else {
                out << setw(4) << heapTreeMatrix[i][j];
            }

        }
        printf("\n");
    }

    for (int i = 0; i < high + 1; i++) {
        delete[] heapTreeMatrix[i];
    }
    delete[] heapTreeMatrix;

}

template<class T>
ostream& operator<<(ostream& out, min_max_heap<T>& x) {//<<运算符重载

    x.output(out);
    out << endl;
    return out;
}

测试代码

char choose;
min_max_heap<int> h(10);
int initSize = 0;//初始化数组元素个数
int array[2000];//初始化数组 
int main(){
    while (1) {

        printf("****************************************************************************************\n");
        printf("菜单 小大根交替堆实现双端优先队列\n");
        printf("** u 建立小大根交替堆\n");
        printf("** i 插入元素\n");
        printf("** b 删除最大值\n");
        printf("** s 删除最小值\n");
        printf("** g 得到最大值\n");
        printf("** h 得到最小值\n");
        printf("** q 退出\n");
        printf("****************************************************************************************\n");

        cin >> choose;
        int a;//临时存放int值 
        char c;//临时存放char值 
        try{
        switch (choose) {
        case 'u':
            printf("建立小大根交替堆:\n");
            printf("请输入关键值:\n");
            getchar();
            flagPrint = false;
            while (1) {
                scanf("%d", &a);
                c = getchar();
                //h.insert(a);
                array[++initSize] = a;//初始化数组
                if (c == '\n') break;
            }
            h.initialize(array, initSize);
            cout << "建堆如下:"<<endl << h << endl;
            //printf("按f回到主菜单\n");
            break;
        case 'i':
            printf("插入:");
            scanf("%d", &a);
            flagPrint = true;
            h.insert(a);
            break;
        case 'b':
            flagPrint = true;
            a = h.popMax();
            printf("删除元素为%d\n", a);
            break;
        case 's':
            flagPrint = true;
            a = h.popMin();
            printf("删除元素为%d\n", a);
            break;
        case 'h':
            h.getMin();
            break;

        case 'g':
            h.getMax();
            break;
        case 'q':
            return 0;
        default:
            printf("输入有误,请重新输入\n");
            break;
        }
        }
        catch (string s) {
            cout << s << endl;
        }
        catch (char* s) {
            cout << s << endl;
        }
    }

return 0;
}

测试截图

这里写图片描述

应用

可以用来实现双端优先队列Double_Ended_Priority_Queue,方便的得到最大最小值。

猜你喜欢

转载自blog.csdn.net/deep_kang/article/details/72085135