算法导论--优先队列实现

仔细看了算法导论第六章,想实现里面的优先队列算法,结果调了好半天的bug才调对,写篇博客记录一下,写的不好之处,跪求各位dalao指点。

算法导论中的优先队列使用数组实现的最大堆,我选择的语言是C++,并且用类封装了这个优先队列,话不多说,我就边贴代码边说明吧_(:3 」∠)_

#pragma once
#include<stdlib.h>
#include<iostream>
using namespace std;

#define MIN 0
#define PARENT(index) (index / 2)
#define LEFT(index) (index * 2)
#define RIGHT(index) (index * 2 + 1)
#define SWAP(a,b,q) \
    NODE temp = q[a];\
    q[a] = q[b]; \
    q[b] = temp;

typedef struct node{
    int dat;     //数据
    int priority;//优先级
}NODE;

class pQueue{
private:
    int heapsize;   //初始队列长度
    int max;        //队列的初始最大长度(如超过可增长)
    NODE *pQ;       //指向结构体数组的指针
    void heapify(NODE* q, int index);//调整堆
public:
    pQueue(int maxsize);
    ~pQueue();
    void bulidPrioQueue(NODE* arr, int initsize);
    int extractMaxPriority(NODE* q);
    void insert(NODE* q, int dat, int prio);
    void increase_prio(NODE* q, int index, int num);
    void decrease_prio(NODE* q, int index, int num);
    int search(NODE *q,int value);
    void showQueue(NODE *q);
};

首先定义了一个结构体NODE来存储优先队列中的元素(内容见代码注释),由于还没有加模板,我这里假设数据是int型。

除了构造和析构函数,对于优先队列的操作,我这里定义了这些:

bulidPrioQueue:建立优先队列(其实是建立大根堆)
extractMaxPriority:提取优先队列中的根元素,并调整堆
insert:往优先队列中插入一个元素
increase_prio:提升队列中某个元素的优先级
decrease_prio:降低队列中某个元素的优先级
search:根据队列中元素的值找到队列元素的下标
showQueue:其实就是一个按照优先级从大到小的堆排序输出

构造函数中我做了一些初始化:

pQueue::pQueue(int maxsize){
    heapsize = 0;
    pQ = NULL;
    max = maxsize;
}

析构函数中我将指向数组的指针释放,这就意味着申请结构体数组必须用动态申请(malloc,calloc等),不过这不是必须的,似乎在工程中(比如写一个服务器)才需要这么写。(不知道说的对不对/(┐「ε:)/

pQueue::~pQueue(){
    if (pQ != NULL){
        free(pQ);
    }
}

下面是一个关键函数:调整堆。

这个函数由于复用的比较多,给我带来了不少bug,一些零碎的就不提了,尤其要注意的是,由于是优先队列,在优先级相同的情况下,应该尽量保证先入队的先出队,所以前面的两个if语句的顺序不能调换.

void pQueue::heapify(NODE* q, int index){
    int largest = index;
    int l = LEFT(index);
    int r = RIGHT(index);

    if (l <= heapsize && q[l].priority > q[index].priority){
        largest = l;
    }
    if (r <= heapsize && q[r].priority > q[largest].priority){
        largest = r;
    }
    //优先级相同的情况下,左儿子的优先级高于右儿子
    if (largest != index){
        SWAP(largest,index,q);
        heapify(q, largest);//递归调整堆,建议改为循环调整
    }
}

初始化优先队列操作,这个函数从倒数第二排从右往左第一个有儿子的节点开始调整堆,从堆底向堆顶构造大根堆,保证了高优先级在上。

void pQueue::bulidPrioQueue(NODE* arr, int initsize){
    pQ = arr;
    heapsize = initsize;
    for (int i = initsize / 2; i >= 1; i--){
        heapify(pQ, i);
    }
}

提取一个堆顶元素。这个函数类似单步的堆排序操作。

int pQueue::extractMaxPriority(NODE* q){
    if (heapsize > 1){
        SWAP(heapsize, 1, q);
        heapsize--;
        heapify(q, 1);
        return q[heapsize + 1].dat;
    }
    return q[heapsize--].dat;
}

往优先队列中插入一个元素。算法导论中插入操作的思想是先将带插入元素的优先级置为最低(这里是0),放在数组末尾(也就是堆的右下角)然后对这个元素执行优先级提升操作,让它提高到应有的位置。

void pQueue::insert(NODE* q, int dat,int prio){
    if (heapsize < max){
        q[++heapsize].dat = dat;
        q[heapsize].priority = MIN;
    }
    else{
        realloc(q,(++heapsize + 1)*sizeof(NODE));
        q[heapsize].dat = dat;
        q[heapsize].priority = MIN;
        max++;
    }
    increase_prio(q, heapsize, prio);
}

优先级提升操作。这个操作很关键,它一定程度上保证了同优先级元素,后入队的后出队。(但也不是一定能保证,比如数组中的元素优先级为:3,2,3。这时插入一个优先级为3的元素,那么由于提升优先级的操作,数组将变成这样:3,3(新插入的),3,2。这时新插入的元素在左儿子,用类似堆排序的方法打印队列的时候,新插入的高优先级元素可能会“插队”。这似乎没什么好的办法解决,dalao们有什么好办法请指教!

void pQueue::increase_prio(NODE* q, int index,int prio){
    int p = PARENT(index);
    if (p == 0){
        //说明是第一个元素,没有父母
        cout << "Alread bigest!" << endl;
        return;
    }
    if (prio <= q[index].priority) {
        //优先级没有提升
        cout << "Priotrity need to be bigger!" << endl;
        return;
    }
    q[index].priority = prio;
    while (p >= 1 && prio > q[p].priority){
        //只有优先级大于(不是大于等于)父节点才往上提,这保证了同级之间的先来后到
        SWAP(p, index, q);
        index = p;
        p = PARENT(index);
    }
    return;
}

有升级就自然有降级,降级操作比较简单,关键还是是调整堆的操作。

void pQueue::decrease_prio(NODE* q, int index,int num){
    if (index > heapsize){
        cout << "Out of heapsize!" << endl;
        return;
    }
    q[index].priority = num;
    heapify(q, index);
}

这个按值寻找元素的操作就是简单的遍历数组。

int pQueue::search(NODE *q, int value){
    for (int i = 1; i <= heapsize; i++){
        if (q[i].dat == value){
            return i;
        }
    }
    return 0;
}

另开辟一片内存,将原数组复制过去(不能改变原队列),进行类似堆排序操作,打印优先队列。

void pQueue::showQueue(NODE *q){
    //使用堆排序显示优先队列
    int hz = heapsize;
    NODE * shower = (NODE*)calloc(heapsize+1, sizeof(NODE));
    if (shower != NULL){
        for (int i = 1; i <= heapsize; i++){
            //复制原队列元素到新开的内存进行堆排序
            shower[i].dat = pQ[i].dat;
            shower[i].priority = pQ[i].priority;
        }
        while (heapsize >= 1){
            cout << extractMaxPriority(shower) << ",";
        }
        heapsize = hz;
        free(shower);
    }
    else{
        cout << "Memory fault!" << endl;
    }


}

测试代码

#pragma once
#include"priorityQueue.h"
#define MAXSIZE 10 + 1 //第0号元素不使用,故优先队列最多MAXSIZE - 1个元素
#define VIP1 1
#define VIP2 2
#define VIP3 3
int main(){
    pQueue prioQ(MAXSIZE);
    NODE* arr = NULL;
    int heapsize = 0;
    int top = 0;
    arr = (NODE *)calloc(MAXSIZE, sizeof(NODE));//自动初始化为0,所以用优先级为0作为队列结束标志
    if (arr != NULL){
        //测试数据
        arr[1].dat = 24;
        arr[1].priority = VIP2;
        arr[2].dat = 14;
        arr[2].priority = VIP1;
        arr[3].dat = 317;
        arr[3].priority = VIP3;
        arr[4].dat = 17;
        arr[4].priority = VIP3;
        for (int i = 1; i < MAXSIZE; i++){
            if (arr[i].priority != 0){
                heapsize++;
            }
            else{
                break;
            }
        }
        prioQ.bulidPrioQueue(arr, heapsize);
        prioQ.showQueue(arr);
        cout << endl;
        prioQ.insert(arr, 100, VIP1);
        prioQ.insert(arr, 99, VIP2);
        prioQ.insert(arr, 101, VIP3);
        prioQ.showQueue(arr);
        cout << endl;
        prioQ.increase_prio(arr, prioQ.search(arr, 99), VIP3);
        prioQ.showQueue(arr);
        cout << endl;
        prioQ.decrease_prio(arr, prioQ.search(arr, 99), VIP1);
        prioQ.showQueue(arr);
        cout << endl;
        cout << top << endl;
    }
    else{
        cout << "Memory fault" << endl;
    }
    system("pause");
    exit(0);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值