一. heap操作
1、make_heap:使序列变成堆
函数原型:
template <class RandomAccessIterator>
void make_heap ( RandomAccessIterator first, RandomAccessIterator last);
template <class RandomAccessIterator,class Compare>
void make_heap ( RandomAccessIterator first, RandomAccessIterator last,
Compare comp );//cmp为函数指针,默认是小于
自己编写的实现机理:从first到last按照顺序执行sift_up操作即可,如果前n-1个元素已经完成sift_up操作,那么这n-1个元素构成的必然满足堆的性质,此时只需要对n元素完成sift_up操作即可。(在我自己实现时,有一次试图从last执行到first,结果可想而知,会出现问题的,但是也能实现,不过除了使用sift_up函数外,还需要执行sift_down操作。建议不要这样来实现。)
STL实现机理:在STL源码中,make_heap并不是和我自己认为的实现机理一致,它是先对最后一个节点位于的子二叉树执行下溯操作,_adjust_heap便是真正执行下溯操作的函数,它这个下溯和我的想法仍然不同,详见pop_heap中分析。这样经过一个下溯操作后,这棵子二叉树调整完毕,接下来在调整其他节点的二叉树。采用一种自底向上的调整过程,这样调整的过程中不仅要sift_up,还需要sift_down.
2、push_heap:压栈(入栈)
函数原型:
template <class RandomAccessIterator>
void push_heap ( RandomAccessIterator first, RandomAccessIterator last);
template <class RandomAccessIterator,class Compare>
void push_heap ( RandomAccessIterator first, RandomAccessIterator last,
Compare comp );
实现机理:一种方法是对于所有要建立堆的数据执行一次make_heap即可,但这样明显没有利用好再make_heap的实现机理中说的一点,那就是要充分利用好前n-1个元素已经构成堆,此时只需要对于最后一个元素执行sift_up即可!!这便是第二种实现方法。在STL中是采用后者的,因此前者影响效率。
3、pop_heap:弹栈(出栈)
函数原型:
template <class RandomAccessIterator>
void pop_heap ( RandomAccessIterator first, RandomAccessIterator last );
template <class RandomAccessIterator,class Compare>
void pop_heap ( RandomAccessIterator first, RandomAccessIterator last,
Compare comp );
实现机理:pop_heap()并不是将堆顶元素弹出并释放空间,而是将堆顶元素与最后一个叶子节点交换位置,并且将除掉最后一个叶子节点外的其他节点仍然维持成一个符合条件的堆,这在背后实际只需要借助sift_down()便可实现。
一个由于sift_down实现方法不同而导致的问题:在STL的heap源码中,sift_down是由_adjust_heap完成的,它的下溯过程是:将空间节点和其较大子节点“对换”,并持续下放,直到叶节点为止,由于在持续下放过程中可能会出现子节点均比空间节点小的情况,因此在经过这一步后,还需要对这个叶子节点进行一次上溯操作!!这些是从《STL源码分析》一书的源码中看到的,所以我一直认为这种思路是不可取的!因此,我的做法是在下溯过程中将空间节点和比自己大的子节点(如果都大,则选最大的节点)进行"对换",直到不能对换或者到达叶节点为止,这样便不需要最后一个上溯操作!!!而STL中的make_heap也是借助于下溯操作_adjust_heap来完成的,所以这两个地方和自己的实现方法有差别,初步来看,我个人还没看出STL的这种做法的高明之处。
4、sort_heap:对堆排序
函数原型:template <class RandomAccessIterator>
void sort_heap ( RandomAccessIterator first, RandomAccessIterator last);
template <class RandomAccessIterator,class Compare>
void sort_heap ( RandomAccessIterator first, RandomAccessIterator last,
Compare comp );
实现机理:借助pop_heap函数实现,每次找出序列中最大(最小)的元素。
二、priority_queue操作(建立在heap之上的另一个STL常用的容器)
在普通的queue或者deque中,队列具有的特点均是“先进先出”,恰好与stack相反,
在queue或者deque的底层实现上较为简单,可以采用链式存储结构或者顺序存储结构,而stack也可以借助上述二者实现,因此queue和stack的实现均可以相互转化(有一道题目就是利用队列实现栈,或者用栈实现队列)。而priority_queue不同于queue和deque,它是优先级队列,每次出队列的操作均是选择队列中优先级最高的元素,由于频繁的pop()操作,这使得在priority_queue中如何确定优先级最高的元素是很重要的一件事情!!!借助上述
heap的实现,我们知道在heap中得到最大(最小)值的复杂度为O(1),因此实现priority_queue
的思路就不难想到了,那就是借用heap!
empty()
size()
top()
push()
pop()
priority_queue的构造函数三个参数:
template <class T, class Container =vector<T>,
class Compare = less<typename Container::value_type> > classpriority_queue;
T代表元素类型,container则是容器类型,通常默认为vector,compare则是元素之间比较的方法。
因此,priority_queue可以在构造函数中就直接调用make_heap
front函数则是借助于pop_heap来实现
push与heap中的push_heap相关联
pop则是先调用pop_heap,再调用vector中的pop_back
在使用STL实现的priority_queue时,可以将它理解为一个会自动根据优先级排序的队列,front()即为队列中优先级别高的则在队头,利用push_back可以实现将一个元素放入队列,并且队列会根据这个元素的优先级放入恰当的位置,而要弹出队头元素,则用pop()。
以下为自己封装的heap类代码:
class heap
{
public:
intn;//元素个数
vector<int>h;
int numSort;
heap(vector<int>hh):h(hh){n=hh.size();numSort=0;};
void sift_up(int end);
void sift_down(int start,int end);
void make_heap();
void pop_heap();
void sort_heap();
void print();
};
void heap::print()
{
for(inti=0;i<n;i++)
cout<<h[i]<<""<<endl;
}
inline int index(int j)//计算父母节点的下标
{
if(j&1)//为奇数
return(j-1)/2;
returnj/2-1;
}
void heap::sift_up(int end)
{
intj=end;
while(index(j)>=0&& h[index(j)]<h[j])
{
inttemp=h[j];
h[j]=h[index(j)];
h[index(j)]=temp;
j=index(j);
}
return;
}
void heap::sift_down(int start,int end)
{
intj=2*start+1;
intk=2*start+2;
while((j<end && h[j]>h[start]) || (k<end &&h[k]>h[start]))
{
if(j<end&& h[j]>h[start] && !(k<end && h[k]>h[start]))
{
swap(h[j],h[start]);
start=j;
}
elseif( !(j<end && h[j]>h[start]) && (k<end &&h[k]>h[start]))
{
swap(h[k],h[start]);
start=k;
}
elseif( j<end && k<end&& h[j]>h[start]&&h[k]>h[start])
{
if(h[j]>h[k])
{
swap(h[j],h[start]);
start=j;
}
else
{
swap(h[k],h[start]);
start=k;
}
}
j=2*start+1;
k=2*start+2;
}
return;
}
void heap::sort_heap()
{
while(numSort<n)
{
pop_heap();
};
return;
}
void heap::make_heap()
{
/*for(inti=n-1;i>=0;i--)
{
sift_down(sift_up(i),n);
}*///逆序来建立堆,该思路不好!
for(inti=0;i<n;i++)
{
sift_up(i);
}
return;
}
void heap::pop_heap()
{
inttemp=h[0];
h[0]=h[n-1-numSort];
h[n-1-numSort]=temp;
numSort++;
sift_down(0,n-numSort);
}