大-小顶混合堆的实现与应用(a min-max heap)

一般情况下我们使用的堆都是大顶堆或者小顶堆,其能实现在常数时间内获得数组的最大值或者最小值,同时满足在对数时间内删除和插入元素。但是如果要同时实现即能在常数时间内获得最大值和最小值,又能在对数时间内删除和插入元素,通常情况下的堆就不能满足上述要求了。为此介绍一种新的数据结构min-max heap

min-max heap 是一颗完全二叉树,但是二叉树的奇数层存的是max元素,偶数层存的是min元素,也即在以偶数层的某节点为根节的子树中,根节点最大,若在以奇数层为根节点的子树中,根节点最小。根据上述的思想构造出相应的min-max heap。

算法实现:

#include "min_max_heap.h"
#include <iostream>
#include<vector>
using namespace std;
bool min_max_heap::is_min_level(int index)
{
	int res = 0;
	index = index+1;
	while(index>1)
	{
		res = res + 1;
		index = index>>1;
	}
	if(res % 2 == 0)
		return true;
	return false;
}
bool min_max_heap::has_child(int index) const
{
	int size = data.size();
	if(2*index<size-1)
		return true;
	return false;
}
int min_max_heap::min_child(int index) const
{
	int size = data.size();
	int res=index*2+1;
	if(res<size-1 && data[res]>data[res+1])
		res++;
	return res;
}
int min_max_heap::max_child(int index) const
{
	int size = data.size();
	int res = 2*index +1;
	if(res<size-1 && data[res]<data[res+1])
		res++;
	return res;
}
bool min_max_heap::has_grandchild(int index) const
{
	int size = data.size();
	int k=2*index+1;
	if(2*k<size-1)
		return true;
	return false;
}
int min_max_heap::min_grandchild(int index) const
{
	int size = data.size();
	int res = 2*index+1;
	int left_res = 2*res+1;
	if(left_res < size-1 && data[left_res]>data[left_res + 1])
		left_res++;
	int right_res=-1;
	if(has_child(res+1))
		right_res = 2*(res+1)+1;
	if(right_res == -1)
		res = left_res;
	else
	{
		if(right_res < size-1 && data[right_res]>data[right_res + 1])
			right_res++;
		if(data[left_res] > data[right_res])
			res = right_res;
		else
			res = left_res;
	}
	return res;

}
int min_max_heap::max_grandchild(int index) const
{
	int size = data.size();
	int res = 2*index+1;
	int left_res = 2*res+1;
	if(left_res<size-1 && data[left_res] < data[left_res+1])
		left_res++;
	int right_res = -1;
	if(has_child(res+1))
		right_res = 2*(res+1)+1;
	if(right_res == -1)
		res = left_res;
	else
	{
		if(right_res<size-1 && data[right_res]<data[right_res+1])
			right_res++;
		if(data[left_res] > data[right_res])
			res = left_res;
		else
			res = right_res;
	}
	return res;
}
bool min_max_heap::has_grandfather(int index) const
{
	if(has_parent(index))
	{
		int res = parent(index);
		if(has_parent(res))
			return true;
	}
	return false;
}
int min_max_heap::grandfather(int index) const
{
	int p = parent(index);
	return parent(p);
}
bool min_max_heap::has_parent(int index) const
{
	if(index == 0)
		return false;
	int res = (index-1)/2;
	if(res >=0)
		return true;
	return false;
}
int min_max_heap::parent(int index) const
{
	int res = (index-1)/2;
	return res;
}
min_max_heap::min_max_heap(const int* array, const int n)
{
	for(int i=0; i<n; i++)
		data.push_back(array[i]);
	for(int i=(n-1)/2; i>=0; i--)
	{
		if(is_min_level(i))
			min_shift_down(i);
		else
			max_shift_down(i);
	}
}
min_max_heap::~min_max_heap(){}
void min_max_heap::swap(int i, int j)
{
	int temp = data[i];
	data[i] = data[j];
	data[j] = temp;
}
void min_max_heap::min_shift_up(int index)
{
	if(!has_parent(index))
		return;
	else if(!has_grandfather(index))
	{
		int p = parent(index);
		if(data[p] < data[index])
			swap(p,index);
		return;
	}
	else
	{
		int grand = grandfather(index);
		if(data[grand] > data[index])
		{
			swap(index,grand);
			min_shift_up(grand);
		}
		else
		{
			int p = parent(index);
			if(data[p] > data[index])
				return;
			else
			{
				swap(p,index);
				max_shift_up(p);
			}
		}
	}
}
void min_max_heap::max_shift_up(int index)
{
	if(!has_parent(index))
		return;
	else if(!has_grandfather(index))
	{
		int p = parent(index);
		if(data[p] > data[index])
			swap(p,index);
		return;
	}
	else
	{
		int grand = grandfather(index);
		if(data[grand] < data[index])
		{
			swap(grand,index);
			max_shift_up(grand);
		}
		else
		{
			int p = parent(index);
			if(data[p] < data[index])
				return;
			else
			{
				swap(index,p);
				min_shift_up(p);
			}
		}
	}
}
void min_max_heap::min_shift_down(int index)
{
	if(!has_child(index))
		return;
	else if(!has_grandchild(index))
	{
		int c = min_child(index);
		if(data[c] <data[index])
			swap(c,index);
		return;
	}
	else
	{
		int c = min_child(index);
		if(data[c] < data[index])
		{
			swap(index,c);
			max_shift_down(c);
		}
		int grand = min_grandchild(index);
		if(data[grand] > data[index])
			return;
		else
		{
			swap(grand,index);
			index = grand;
			int p = parent(index);
			if(data[p] < data[index])
				swap(p,index);
			min_shift_down(index);
		}
	}
}
void min_max_heap::max_shift_down(int index)
{
	if(!has_child(index))
		return;
	else if(!has_grandchild(index))
	{
		int c = max_child(index);
		if(data[c] > data[index])
			swap(c,index);
		return;
	}
	else
	{
		int c = max_child(index);
		if(data[c] > data[index])
		{
			swap(c,index);
			min_shift_down(c);
		}
		int grand = max_grandchild(index);
		if(data[grand] < data[index])
			return;
		else
		{
			swap(grand,index);
			index = grand;
			int p = parent(index);
			if(data[p] > data[index])
				swap(p,index);
			max_shift_down(index);
		}
	}
}
void min_max_heap::insert(int item)
{
	data.push_back(item);
	int index = data.size()-1;
	if(is_min_level(index))
		min_shift_up(index);
	else
		max_shift_up(index);
}
int min_max_heap::delmin()
{
	int res = -1;
	int n = data.size();
	if(n == 0)
		return -1;
	res = data[0];
	swap(0,n-1);
	data.pop_back();
	min_shift_down(0);

	return res;

}
int min_max_heap::delmax()
{
	int n = data.size();
	int res = -1;
	if(n == 0)
		return res;
	if(n==1)
	{
		res = data[0];
		data.pop_back();
	}
	else
	{
		int c = max_child(0);
		res = data[c];
		swap(c,n-1);
		data.pop_back();
		max_shift_down(c);
	}
	return res;
}
int min_max_heap::min()
{
	if(data.size()==0)
		return -1;
	return data[0];
}
int min_max_heap::max()
{
	int n = data.size();
	if(n==0)
		return -1;
	if(n==1)
		return data[0];
	return data[max_child(0)];
}
ostream& operator<<(ostream& os, const min_max_heap& hp)
{
	for(unsigned i=0; i<hp.data.size(); i++)
		os<<hp.data[i]<<" ";
	return os;
}

由于存在奇数层和偶数层之分,也即max层和min层之分,因此在堆的“上浮”和“下沉”的过程中要依据节点所在的层次选择不同的“上浮”和“下层”方法

测试代码:

#include <iostream>
#include "min_max_heap.h"
#include <time.h>
#include <stdlib.h>
using namespace std;
int* create_array(const int n);
void main()
{
	int n;
	cin>>n;
	while(n>0)
	{
		int* a = create_array(n);
		cout<<"The array: ";
		for(int i=0; i<n; i++)
			cout<<a[i]<<" ";
		cout<<endl;
		min_max_heap hp(a,n);
		cout<<"The min-max heap: "<<hp<<endl;
		cout<<"delmax(): ";
		for(int i=0; i<n; i++)
			cout<<hp.delmax()<<" ";
		cout<<endl;
		for(int i=0; i<n; i++)
			hp.insert(a[i]);
		cout<<"The min-max heap: "<<hp<<endl;
		cout<<"delmin(): ";
		for(int i=0; i<n; i++)
			cout<<hp.delmin()<<" ";
		cout<<endl;
		cin>>n;
	}
}
int* create_array(const int n)
{
	int* res = new int[n];
	for(int i=0; i<n; i++)
		res[i] = 0;
	for(int i=0; i<n; i++)
	{
		srand((unsigned)time(0));
		while(1)
		{
			int m=rand()%n;
			if(res[m] ==0)
			{
				res[m] = i;
				break;
			}
		}
	}
	return res;
}

上述代码只是我在学习min-max heap 时使用的测试代码,如有什么不明白的地方欢迎讨论。

时间: 2024-09-29 23:49:40

大-小顶混合堆的实现与应用(a min-max heap)的相关文章

heap c++ 操作 大顶堆、小顶堆

在C++中,虽然堆不像 vector, set 之类的有已经实现的数据结构,但是在 algorithm.h 中实现了一些相关的模板函数.下面是一些示例应用 http://www.cplusplus.com/reference/algorithm/pop_heap/ #include <iostream> #include <algorithm> // make_heap(), pop_heap(), push_heap() #include <vector> using

python 大顶堆 小顶堆

http://www.coder4.com/archives/3844 需1求:给出N长的序列,求出TopK大的元素,使用小顶堆,heapq模块实现. import heapq import random class TopkHeap(object): def __init__(self, k): self.k = k self.data = [] def Push(self, elem): if len(self.data) < self.k: heapq.heappush(self.data

wikioi 2573 大顶堆与小顶堆并用

题目描述 Description 我们使用黑匣子的一个简单模型.它能存放一个整数序列和一个特别的变量i.在初始时刻,黑匣子为空且i等于0.这个黑匣子能执行一系列的命令.有两类命令: ADD(x):把元素x放入黑匣子:GET:把i加1的同时,输出黑匣子内所有整数中第i小的数.牢记第i小的数是当黑匣子中的元素已非降序排序后位于第i位的元素. 下面的表6_4是一个11个命令的例子: 表6_4 编号 命令 i 黑匣子内容 输出 1 ADD(3) 0 3 2 GET 1 3 3 3 ADD(1) 1 1,

剑指offer:数据流中的中位数(小顶堆+大顶堆)

1. 题目描述 /** 如何得到一个数据流中的中位数? 如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值. 如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值. 我们使用 Insert()方法读取数据流,使用 GetMedian()方法获取当前读取数据的中位数. */ 2. 思路 /** 最大堆和最小堆 * 每次插入小顶堆的是当前大顶堆中最大的数 * 每次插入大顶堆的是当前小顶堆中最小的数 * 这样保证小顶堆中的数永远大于等于大顶堆中的数(值

wikioi 1245 小顶堆

题目描述 Description 有两个长度为 N 的序列 A 和 B,在 A 和 B 中各任取一个数可以得到 N^2 个和,求这N^2 个和中最小的 N个. 输入描述 Input Description 第一行输入一个正整数N:第二行N个整数Ai 且Ai≤10^9:第三行N个整数Bi, 且Bi≤10^9 输出描述 Output Description 输出仅一行,包含 n 个整数,从小到大输出这 N个最小的和,相邻数字之间用 空格隔开. 样例输入 Sample Input 5 1 3 2 4

POJ3253 Fence Repair 小顶堆+贪心

给了你N个木棒,求把他们组装成一根需要的最小花费,每次只能选两根组装在一起,需要的花费为两个木棒之和, 以前遇到过把一整根切开的,那个是DP,这个则有些类似,可是大胆的猜测了一下,直接每次选取所有木棒中最短的两根,这样就可以了,那么贪心是适用的,但是数量很多,而且两根最短的组装好了得插回去,这样不可能每次都排序吧, 这题首先优先队列肯定是可以做的, 最小堆也是可以的,每次都选出堆里的最小的两个求和再放回去即可 队列本身也就是堆吧,所以差别不大,但是没用过最小堆最大堆的 所以用一次把 #inclu

BZoj 1293 生日礼物(小顶堆)

传送门:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=27400 Anayse:小顶堆: 1 1 5     - >    1 3 5     ->    3 5 7    ->     5 7 8 pop(1)    p(1)      p(3)       最优解. 每次剔除堆顶,然后换push与堆顶相同种类的珠子,由于单调,,距离会被缩短,于是找出最优解.详见上式. push()  log(n) so,复杂度

优先级队列(小顶堆)的dijkstra算法

php实现迪杰斯特拉算法,并由小顶堆优化 1 <?php 2 3 class DEdge 4 { 5 public $nextIndex, $length; 6 7 public function __construct($nextIndex, $length) 8 { 9 $this->nextIndex = $nextIndex; 10 $this->length = $length; 11 } 12 } 13 14 class DNode 15 { 16 public $index

7-31 笛卡尔树 (25分)--判断二叉搜索树,小顶堆

先初步判断是否满足二叉搜索树和小顶堆(针对每一颗最小的子树),如果都满足,进一步判断整棵树是否满足. 1 #include <iostream> 2 #include <string> 3 #include <cstring> 4 using namespace std; 5 typedef struct node 6 { 7 int K1; 8 int K2; 9 int L; 10 int R; 11 }node_arr[1001]; 12 node_arr s;