用堆二叉树实现,参考《算法》(第四版)。
1.优先队列
//二叉堆,从小到大排序
class heapBinaryTree
{
public:
heapBinaryTree(int capacity);//指定容量
heapBinaryTree(const int num[], int len);//用数组初始化堆
~heapBinaryTree();
inline int getCapacity(){return cap;}
inline int getLen(){return len;}
bool insert(int e);
int getMin();//取得最小值
int deleteMin();//删除并返回最小值
void showHeap();
private:
void sink(int pos);//将pos位置的元素下沉到合适的位置
void swim(int pos);
inline void swap(int num[], int pos1, int pos2);
private:
int len;//堆长度
int cap;//可扩展的最大容量
int *elem;//元素数组
};
实现:
heapBinaryTree::heapBinaryTree(int capacity)
{//申请空间,但不初始化数据
len=0;
cap=capacity;
elem=new int[capacity];
}
heapBinaryTree::heapBinaryTree(const int num[], int len)
{//使用已存在的数组数据进行初始化,并调整好堆
cap=len*2;
elem=new int[cap];
this->len=0;
for(int i=0;ielem[childPos+1]) childPos++;//找到子节点中较小的
if(elem[pos]<=elem[childPos]) break;//如果比两个子节点都小,则不需要再下沉
swap(elem, pos, childPos);
pos=childPos;
childPos=pos*2;
}
if(childPos==len && elem[pos]>elem[childPos]) swap(elem, pos, childPos);//只有一个子节点的情况
}
void heapBinaryTree::swim(int pos)//将pos位置的元素上升到合适的位置
{
while(pos/2>0)//如果存在父节点
{
if(elem[pos]>=elem[pos/2]) break;//如果比父节点大,不需要再上浮
swap(elem, pos, pos/2);
pos=pos/2;
}
}
bool heapBinaryTree::insert(int e)//插入元素到堆的结尾,并上升到合适的位置
{
if(len>=cap) return false;
len++;
elem[len]=e;
swim(len);
}
int heapBinaryTree::getMin()//取得堆顶的元素,即最小值
{
if(len<1) return -1;
return elem[1];
}
int heapBinaryTree::deleteMin()//删除并返回最小值
{
if(len<1) return -1;
int min=elem[1];
swap(elem, 1, len);
len--;
sink(1);
return min;
}
void heapBinaryTree::showHeap()
{
for(int i=1; i<=len;i++)
std::cout<
在优先队列的基础上进行改写,主要是添加一个功能,能够知道某个元素在堆中的位置,这样可以方便对录入的数据进行修改。
class priorityQueueWithIndex
{
public:
priorityQueueWithIndex(int capacity);
priorityQueueWithIndex(const int num[], int len);
~priorityQueueWithIndex();
inline int getCapacity(){return cap;}
inline int getLen(){return heapLen;}
bool insert(int e);
int getMin();//取得最小值
int deleteMin();//删除并返回最小值
void showHeap();
bool change(int index, int e);//修改第index个元素的值为e,并调整队列
int insertWithCheck(int e);//如果已添加的元素队列中存在空位(标记为-1),则添加到空位,否则添加到队列结尾,返回插入位置
private:
void sink(int pos);//将pos位置的元素下沉到合适的位置
void swim(int pos);
inline void swap(int num[], int pos1, int pos2);
private:
int heapLen;//堆的大小
int elemNum;//已加元素的大小,包括被删除的,因为元素被删除后并未被真的删除,只是被标记为-1
int cap;
//下面3个数组用来保存数据,elem[elemIndex[1]]表示最大(小)元素的值
//heapIndex[1]表示第一个插入的元素在堆中的位置
//elemIndex[heapInde[i]]=i
int *elem;//元素数组,按插入顺序保存
int *elemIndex;//用来保存元素数组的下标(元素的插入顺序),堆结构,元素小(大)的优先
int *heapIndex;//用来保存堆的下标(元素在堆里的位置),按元素的插入顺序保存
};
实现如下:
priorityQueueWithIndex::priorityQueueWithIndex(int capacity)
{
heapLen=0;
elemNum=0;
cap=capacity;
elem=new int[capacity];
elemIndex=new int[capacity];
heapIndex=new int[capacity];
}
priorityQueueWithIndex::priorityQueueWithIndex(const int num[], int len)
{//用数组来初始化
cap=len*2;
elemNum=0;
heapLen=0;
elem=new int[cap];
elemIndex=new int[cap];
heapIndex=new int[cap];
for(int i=0;ielem[elemIndex[childPos+1]]) childPos++;
if(elem[elemIndex[pos]]<=elem[elemIndex[childPos]]) break;
swap(elemIndex, pos, childPos);
//保证elemIndex[heapInde[i]]=i,对heapIndex作对应调整
swap(heapIndex,elemIndex[pos], elemIndex[childPos]);
pos=childPos;
childPos=pos*2;
}
if(childPos==heapLen && elem[elemIndex[pos]]>elem[elemIndex[childPos]])
{
swap(elemIndex, pos, childPos);//只有一个子节点的情况
swap(heapIndex,elemIndex[pos], elemIndex[childPos]);
}
}
void priorityQueueWithIndex::swim(int pos)
{
while(pos/2>0)
{
if(elem[elemIndex[pos]]>=elem[elemIndex[pos/2]]) break;
swap(elemIndex, pos, pos/2);
swap(heapIndex,elemIndex[pos], elemIndex[pos/2]);
pos=pos/2;
}
}
bool priorityQueueWithIndex::insert(int e)
{
if(elemNum>=cap) return false;
heapLen++;
elemNum++;
elem[elemNum]=e;//直接在elem末尾添加新元素
elemIndex[heapLen]=elemNum;//在堆的结尾添加新元素在elem数组中的位置
heapIndex[elemNum]=heapLen;//在heapIndex的结尾添加新元素在堆中的位置
swim(heapLen);
}
int priorityQueueWithIndex::getMin()
{
if(heapLen<1) return -1;
return elem[elemIndex[1]];//elemIndex[1]是最小元素的下标
}
int priorityQueueWithIndex::deleteMin()
{
if(heapLen<1) return -1;
swap(elemIndex, 1, heapLen);//将堆顶放到堆的最后
swap(heapIndex, elemIndex[1], elemIndex[heapLen]);
int min=elem[elemIndex[heapLen]];
elem[elemIndex[heapLen]]=-1;//将元素从elem数组中标记为-1,表示删除
heapIndex[elemIndex[heapLen]]=-1;//将heapIndex中对应位置标记为-1
heapLen--;
sink(1);
return min;
}
bool priorityQueueWithIndex::change(int index, int e)//修改第index个已存在元素的值为e,并调整队列
{
if(index>elemNum) return false;
if(heapIndex[index]==-1) return false;
elem[index]=e;
sink(heapIndex[index]);
swim(heapIndex[index]);
}
int priorityQueueWithIndex::insertWithCheck(int e)//如果已添加的元素队列中存在空位(标记为-1),则添加到空位,否则添加到队列结尾
{
if(heapLen==elemNum) //如果堆长度与元素数组的长度一致,则说明元素数组中不存在空缺,直接插入结尾
{
insert(e);
return elemNum;
}
int i=1;
while(i<=elemNum)//查找元素数组的空缺位置
{
if(heapIndex[i]==-1) break;
i++;
}
//插入新元素
elem[i]=e;
elemIndex[++heapLen]=i;
heapIndex[i]=heapLen;
//调整堆
sink(heapIndex[i]);
swim(heapIndex[i]);
return i;
}
void priorityQueueWithIndex::showHeap()
{
std::cout<<"elems:"<
注意点:
1.代码实现用到3个数组,具体存的数据见注释,为方便理解,这里用成绩录入作为例子,那么:
elem[]数组用来按照座位号存成绩;
elemIndex[]用来存座位号,按照成绩的大小整理为堆结构;
heapIndex[]用来按照座位号存该座位号在堆中的位置,因此与上面的数组存的数据刚好逆序,即elemIndex[heapInde[i]]=i。
2.从队列中删除元素
对于elemIndex[]会删除对应座位同时调整堆;
对于elem[]和heapIndex[]并不会真的删除对应元素,而是将对应位置标记为-1;
因此elemIndex[]的有效长度<小于等于elem[]和heapIndex[]的有效长度。
3.由于上面的原因,两个数组中有空缺的位置,所以编写了insertWithCheck()函数,如果有空缺则会优先将添加的元素加到空缺中。
4.假如某个同学成绩录入错误,可以通过change()函数对其进行修改,并重新调整堆结构。
5.sink()和swim()函数
相比优先队列,这里主要有2点不一样,一是用elem数组进行比较而用elemIndex进行交换;二是heapIndex也要进行相应调整。
上面的代码都没有实现容量不够进行扩容的功能。