优先队列——二叉堆实现

二叉堆是完全二叉树

二叉堆满足堆特性:父节点的键值总是大于或等于(小于或等于)任何一个子节点的键值,且每个节点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。

父节点的键值总是大于或等于任何一个子节点的键值时为最大堆。 当父节点的键值总是小于或等于任何一个子节点的键值时为最小堆

最小堆:  1                 最大堆:  11
       /   \                       /   \
      2     3                     9     10
     / \   / \                   / \    / \
    4   5  6  7                 5   6  7   8
   / \ / \                     / \ / \
  8  9 10 11                  1  2 3  4 

因为完全二叉树的特性, 父节点和子节点的关系是  

        i

    /       \

 2i      2i+1

所以完全二叉树可以直接用数组表示(如图):

优先队列——二叉堆实现_第1张图片

那么二叉堆这种有序队列如何入队呢?

优先队列——二叉堆实现_第2张图片

上图中在最小堆里插入一个键值为2的元素,只需在数组末尾加入这个元素,然后根据父节点的键值总是小于等于子节点的键值这一特性,尽可能把这个元素往父节点移动,直到无法再移动。这里可以看出二叉堆插入操作的时间复杂度为Ο(logn)。

那如何出队呢?如图:

优先队列——二叉堆实现_第3张图片

出队一定是出数组的第一个元素(最小元素),这么来第一个元素的位置就成了空位,我们需要调整元素,从空位的两个子节点中选出一个最小的向上移动,此时原来的那个子节点变成了父节点,而它原来的位置空了出来,这是就变成了二叉堆的子堆移动的问题,所以这样递归一层一层移动,直到空出来的位置被移动的叶子节点。然后把数组最后一个元素插入这个空位。

有些人可能会有疑问,说出队直接把最顶端节点出队,然后直接维护调整顺序不就行了?为什么还要把最后一个元素也取出来?

答案是:为了保证维护后还是一个完全二叉树,根据完全二叉树的性质,如果这个完全二叉树不是满二叉树,但是节点的顺序一定要保证是 i   2*i    2*i+1

所以,如果不把最后一个元素拿出来,最后很可能会导致完全二叉树叶子节节点不是按顺序排列。 

最后出队+维护操作的时间复杂度也是O(1)+Ο(logn)。

代码

typedef struct heap HEAP;
struct heap 
{
     int arr[127];
     int size;
};
void insert(HEAP *h, int n)
{
       int sz = h->size++;
       printf( "%d --- %d\n", sz , n );
       while( sz > 0 )
	   {
            if( n < h->arr[(sz-1)/2] )
			{
                h->arr[sz] = h->arr[(sz-1)/2];
                sz =  (sz-1) / 2;
            }
			else
			{
                h->arr[sz] = n;
                return;
            }
       }
       h->arr[0] = n;
}
void delete( HEAP *h )
{
	   int i;
	   int tmp;
	   int head = h->arr[0];
	   h->size--;
	   h->arr[0] = h->arr[h->size];
	   h->arr[h->size] = 0;
	   i = 1;
	   while( h->arr[i]>0 )
	   {
	           if( h->arr[i] < head )
			   {
	                  h->arr[(i-1)/2] = h->arr[i];
	                  h->arr[i] = head;
	                  i = i*2 + 1;
	           }
			   else if( h->arr[i+1] < head )
			   {
	                  h->arr[(i-1)/2] = h->arr[i+1];
	                  h->arr[i+1] = head;
	                  i = 2*(i+1)+1;
	           }
			   else
			   {
	                  return;
	           }
	   }
}





你可能感兴趣的:(数据结构,算法题目)