博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

堆实现的优先队列模板

Posted on 2010-12-23 21:25  ¥忘%风  阅读(2070)  评论(0编辑  收藏  举报

下面是自己手写的优先队列模板(默认是大顶堆,可通过重载小于号改变)

用法说明如下:

1)priority_queue部分(用法参照stl)

c-free库函数帮助里提供的priority_queue用法说明如下:

C++优先队列类似队列,但是在这个数据结构中的元素按照一定的断言排列有序。

empty() 如果优先队列为空,则返回真
pop() 删除第一个元素
push() 加入一个元素
size() 返回优先队列中拥有的元素的个数
top() 返回优先队列中有最高优先级的元素

2)堆的基本堆操作部分。

用法如下:

set_size() 设置堆的大小
set_value() 设置堆中第id个元素的值
creat_heap() 创建一个初始堆
heap_sort() 堆排序,默认从小到大排,可通过重载小于号实现按指定规则排序
is_heap() 判断是否构成一个堆

模板如下:

#define MAXN 25500
typedef struct RedType {
	int left, right;
	int weight;
	bool friend operator < (const RedType &a, const RedType &b) {
		return a.weight > b.weight;
	}
}Edge;
	
class priority_queue {
private :
	RedType r[MAXN];
	int length;
	
	void heap_adjust(int s, int m) {
		RedType rc = r[s];
		for (int j = s << 1; j <= m; j <<= 1) {
			if (j < m && r[j] < r[j+1]) j++;
			if (!(rc < r[j])) break;
			r[s] = r[j]; s = j;
		}
		r[s] = rc;
	}
	
public :
	priority_queue() {
		length = 0;
	}
	/*===========下面是堆操作部分,建堆,堆排等=============*/ 
	
	void creat_heap() {
		for (int i = length >> 1; i > 0; i--) {
			heap_adjust(i, length);
		}
	}
	
	void set_size(int length) {
		this->length = length;
	}
	
	void set_value(int id, RedType value) {
		r[id] = value;
	}
	
	void heap_sort() {
		for (int i = length; i > 1; i--) {
			RedType tmp = r[1];
			r[1] = r[i];
			r[i] = tmp;
			heap_adjust(1, i - 1);
		}
	}
	
	bool is_heap() {
		int len = length >> 1 - 1, j;
		if (len < 1) {
			if (length == 1 || (!(r[1] < r[length]) 
			&& !(r[1] < r[length - 1])))
				return true;
			return false;
		}
		for (int i = 1; i <= len; i++) {
			j = i << 1;
			if (r[j] < r[j + 1]) j++;
			if (r[i] < r[j]) return false;
		}
		return true;
	}
	
	/*==下面函数虽无stl强大,但用法类似(用堆实现的priority_queue)==*/	
	void push(RedType rc) {
		++length;
		r[length] = rc;
		int s = length >> 1;
		for (int j = length; j > 1; j >>= 1) {
			if (!(r[s] < r[j])) break;
			RedType tmp = r[s];
			r[s] = r[j];
			r[j] = tmp;
			s >>= 1;
		}
	}

	void pop() {
		RedType tmp = r[1];
		r[1] = r[length];
		r[length] = tmp;
		heap_adjust(1, --length);
	}
	
	RedType top() {
		return r[1];
	} 
	
	int size() {
		return length;
	}
	
	bool empty() {
		if (length <= 0)
			return true;
		return false;
	}
};

基本函数说明及复杂度分析如下:

  heap_adjust()从第一个不满足堆的结点s开始向下做调整,使得维护一个堆。

  --------log(n)

  push()从尾部增加一个叶子节点到堆中,因为原本是满足堆的,所以只需要从下至上不断跟父结点进行交换,直至已满足堆退出

  --------log(n)

  pop()把start跟end做交换,然后[start,end-1]从新做一次调整,使得[start,end-1]维护成一个堆,堆大小length随之-1。

  --------log(n)

  creat_heap() 从第一个非终端结点开始->根节点不断做heap_adjust(),使之构成一个初始堆。

  --------n / 2 * log(n)

  heap_sort()  相当于重复n次pop()操作,但length不改变。

  --------n * log(n)

  is_heap() 判断是否构成堆,堆大小小于4时由外面的if判定,避免for循环内增加多余判断

  --------n / 2.

模板使用举例:

http://acm.hdu.edu.cn/showproblem.php?pid=3371  最小生成树

3358493 2010-12-23 21:34:41 Accepted 3371 500MS 484K 3659 B C++ Slave_wc
#include <stdio.h>
#include <string.h>
#define MAXN 25500
#define MAXN 25500
typedef struct RedType {
	int left, right;
	int weight;
	bool friend operator < (const RedType &a, const RedType &b) {
		return a.weight > b.weight;
	}
}Edge;
	
class priority_queue {
private :
	RedType r[MAXN];
	int length;
	
	void heap_adjust(int s, int m) {
		RedType rc = r[s];
		for (int j = s << 1; j <= m; j <<= 1) {
			if (j < m && r[j] < r[j+1]) j++;
			if (!(rc < r[j])) break;
			r[s] = r[j]; s = j;
		}
		r[s] = rc;
	}
	
public :
	priority_queue() {
		length = 0;
	}
	/*===========下面是堆操作部分,建堆,堆排等=============*/ 
	
	void creat_heap() {
		for (int i = length >> 1; i > 0; i--) {
			heap_adjust(i, length);
		}
	}
	
	void set_size(int length) {
		this->length = length;
	}
	
	void set_value(int id, RedType value) {
		r[id] = value;
	}
	
	void heap_sort() {
		for (int i = length; i > 1; i--) {
			RedType tmp = r[1];
			r[1] = r[i];
			r[i] = r[1];
			heap_adjust(1, i - 1);
		}
	}
	
	bool is_heap() {
		int len = length >> 1 - 1, j;
		if (len < 1) {
			if (length == 1 || (!(r[1] < r[length]) 
			&& !(r[1] < r[length - 1])))
				return true;
			return false;
		}
		for (int i = 1; i <= len; i++) {
			j = i << 1;
			if (r[j] < r[j + 1]) j++;
			if (r[i] < r[j]) return false;
		}
		return true;
	}
	
	/*==下面函数虽无stl强大,但用法类似(用堆实现的priority_queue)==*/	
	void push(RedType rc) {
		++length;
		r[length] = rc;
		int s = length >> 1;
		for (int j = length; j > 1; j >>= 1) {
			if (!(r[s] < r[j])) break;
			RedType tmp = r[s];
			r[s] = r[j];
			r[j] = tmp;
			s >>= 1;
		}
	}

	void pop() {
		RedType tmp = r[1];
		r[1] = r[length];
		r[length] = tmp;
		heap_adjust(1, --length);
	}
	
	RedType top() {
		return r[1];
	} 
	
	int size() {
		return length;
	}
	
	bool empty() {
		if (length <= 0)
			return true;
		return false;
	}
};
priority_queue heap;
int set[MAXN];
bool hash[MAXN];

int find(int x) {
	int r = x;
	while (r != set[r])
		r = set[r];
	int i = x, j;
	while (i != r) {
		j = set[i];
		 set[i] = r;
		 i = j;
	}
	return r;
}

bool merge(int x, int y) {
	int fx = find(x);
	int fy = find(y);
	if (fx != fy) {
		set[fx] = fy;
		return true;
	}
	return false;
}

void Init(int n) {
	for (int i = 0; i <= n; i++) {
		set[i] = i;
		hash[i] = false;
	}
}

void kruscal(int n, int num);
void input() {
	int test, n, m, k;
	Edge cur;
	scanf("%d", &test);
	while (test--) {
		scanf("%d %d %d", &n, &m, &k);
		Init(n);
		heap.set_size(m);
		for (int i = 1; i <= m; i++) {
			scanf("%d %d %d", &cur.left, &cur.right, &cur.weight);
			heap.set_value(i, cur);
		}
		heap.creat_heap();
		int t, a, b, num = 0, nn = 0, nk = 0;
		for (int i = 1; i <= k; i++) {
			scanf("%d %d", &t, &a);
			hash[a] = true;
			for (int j = 2; j <= t; j++) {
				scanf("%d", &b);
				merge(a, b);
				hash[b] = true;
			}
		}
		for (int i = 1; i <= n; i++) if (hash[i]) nn++;
		for (int i = 1; i <= n; i++) {
			if (hash[i] && set[i] == i) nk++;
		}
		num = nn - nk;
		kruscal(n, num);
	}
}

void kruscal(int n, int num) {
	int sum = 0;
	while (!heap.empty() && num < n - 1) {
		Edge cur = heap.top();
		heap.pop();
		if (merge(cur.left, cur.right)) {
			sum += cur.weight;
			num++;
		}
	}
	if (num == n - 1) {
		printf("%d\n", sum);
	} else {
		puts("-1");
	}
}

int main() {
	input();
	return 0;
}

效率分析:

上面代码是通过creat_heap()建堆,如果通过push逐个压入,则由上面复杂度分析可知,建堆过程复杂度提高一倍。

 (n/2*log(n)到n*log(n))

经检测,上面代码改成通过push逐个压入建堆的方式,效率在700+ms。

如果改成用#include <queue>头文件自带的priority_queue, 则同样是通过push完成建堆,效率在800+ms以上。

原创文章如转载请注明:转自¥忘%风 {http://www.cnblogs.com/slave_wc}

本文地址: 

堆实现的优先队列模板 {http://www.cnblogs.com/slave_wc/archive/2010/12/23/1915293.html }