优先队列和索引优先队

用堆二叉树实现,参考《算法》(第四版)。

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<

2.索引优先队列

在优先队列的基础上进行改写,主要是添加一个功能,能够知道某个元素在堆中的位置,这样可以方便对录入的数据进行修改。

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也要进行相应调整。


上面的代码都没有实现容量不够进行扩容的功能。


你可能感兴趣的:(算法,C++)