数据结构与算法之堆

声明:博客主要知识点内容来自参考书《数据结构、算法与应用》,本篇博客的主要目的是记录学习过程,方便日后复习。

堆的定义:一棵大根树(小根树)是这样一棵树,其中每个节点的值都大于(小于)或等于其子节点(若有子节点的话)的值。一个大根堆(小根堆)既是大根树(小根树),也是完全二叉树。

堆的常见操作:

  • 大根堆的插入操作:插入过程基本是这样的,将新的元素插入新的节点,然后沿着新的节点到根节点的路径,执行一趟起泡操作(基本就是两两比较的操作,若比父节点大就执行交换操作),将新元素与父节点的元素进行比较交换,直到后者大于或者等于前者为止。在插入函数push的代码中,首先判断数组heap是否有空间容纳新的元素,如果没有的话需要扩容。然后令变量currentNode指向新的叶子节点的位置。接下来,沿着叶子节点向根节点的路径进行遍历。实际上,是将新元素theElement沿着这条路径起泡上浮,直至到达合适的位置。在每一次确定上移位置currentNode的值时,我们需要判断,是否到达根节点(也即currentNode == 1),或者是否在currentNode位置插入新的元素之后符合大根树的特性(也即theElement <=heap[currentNode/2]).。如果满足一个条件,就将新元素theElement插入的位置currentNode。否则,进入while循环体,将位置currentNode/2位置的元素下移至currentNode,然后令,currentNode = currentNode/2。
  • 大根堆的插入C++示例代码:
template<class T>
void maxHeap<T>::push(const T& theElement)
{
   //增加数组长度
  if(heapSize == arrayLength -1)
  {
    changeLength1D(heap,arrayLength,2*arrayLength);
	arrayLength = 2*arrayLength;
  }
  //为新的元素theElement找到插入位置
  int currentNode = ++heapSize;
  while(currentNode !=1 && heap[currentNode/2] <theElement)
  {
    //父节点下移
	heap[currentNode] = heap[currentNode/2];
	currentNode = currentNode/2;      //currentNode移向双亲
  }
  heap[currentNode] = theElement;
}
  • 大根堆的删除操作:在大根堆中删除一个元素,就是删除根节点的元素。删除根节点之后,需要进行大根堆的重构。以下是C++操作示例代码:pop操作要删除的是堆中的最大元素,它存在根heap[1]中。删除的过程是,把堆的最后一个元素heap[heapSize]保存到变量lastElement中,然后堆的元素个数heapSize减一。在while循环中,我们把heap数组重建为大根堆。重建堆的过程是为存储在lastElement中元素寻找合适的插入位置,寻找过程从根开始沿堆向下进行。
  • template<class T>
    void maxHeap<T>::pop()
    {
      if(heapSize == 0)     //堆为空
        throw queueEmpty();
      
      //堆不为空的话,删除第一个元素
      heap[1].~T();
      
      //删除最后一个元素,然后重建堆
      T lastElement = heap[heapSize--];
      
      //从根节点开始,为最后一个元素寻找位置
      int currentNode = 1;
      int  child = 2;            //currentNode的孩子
      while(child<heapSize)
      {
        //heap[child]应该的currentNode的更大的孩子
    	if(child<heapSize && heap[child]<heap[child+1])
    	  child++;
    	  
    	//如果lastElement比heap[child]还大,就可以将lastElement放在currentNode位置上
    	if(lastElement >heap[child])
    	  break;
    	
        //否则的话,将孩子向上移动,currentNode向下一层寻找位置
        heap[currentNode] = heap[child];
        currentNode = child;
        child = child*2;        	
      }  
      heap[currentNode] = lastElement;
    	
    }

     

初始化一个非空大根堆:方法initialize在数组heap中建堆。函数一开始,令heap指向数组theHeap,heapSize等于数组中元素个数theSize。在for循环中,我们从最后一个具有孩子的节点到根节点进行扫描,并用root表示正在处理的节点。对于每一个root值,嵌入的while循环把以root为根的子树调整为大根堆。

template<class T>
void maxHeap<T>::initialize(T *theHeap,int theSize)
{
 //在数组theHeap[1:theSize]中建大根堆
 delete [] heap;
 heap = theHeap;
 heapSize = theSize;
 
 //堆化
 for(int root = heapSize/2;root>=1;root--)
 {
	T rootElement = heap[root];
    //为元素rootElement寻找位置
    int child = 2*root;
    while(child < heapSize)
	{
		//heap[child]应该是兄弟中的较大者
		if(child<heapSize && heap[child]< heap[child+1])
			child++;
		//可以将元素rootElement放在heap[child/2]嘛?
		if(rootElement >= heap[child])
			break;  //可以
		//不可以
		heap[child/2] = heap[child];   //把孩子向上移
		child *=2;		
	}		
	heap[child/2] = rootElement;
 }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值