堆-及其基本操作

实现优先级队列的另一种结构::堆。堆是一种树,,由他实现的优先级队列的插入和删除的时间复杂度都是O(llooggN) 。

注意:这里的堆是一种特殊的二叉树,不要和java以及C++等编程语言里的“堆”混淆,后者指的是程序员用new能得到的计算机内存的可用部分。

堆是有如下特点的二叉树:
—-它是完全二叉树。也就是说,除了最后一层节点不是满的,其他的每一层从左到右都完全是满的。
这里写图片描述
—–它常常用一个数组实现。
——堆中的每一个节点都满足堆的条件,也就是说每一个节点的关键字都大于(或等于)这个节点的子节点的关键字。
堆和它的实现数组:
这里写图片描述

堆中数据元素的移除:
移除堆中关键字最大的节点,这个节点总是根节点,所以移除是比较容易的。根在堆数组中的索引总是0.
问题在于一旦移除了根节点,树就不再是完全了;数组里就有一个空的数据单元,为了将这个数据单元填满,可以把数组中的所有数据项都向前移动一个单元,但是还有快得多的方法,下面就是移除最大节点的步骤:
1.移走根
2.把最后一个节点移动到根的位置
3.一直向下筛选这个节点,直到它在一个大于它的节点之下,小于她的节点之上为止。
最后一个节点(last)是树最底层的最右端的数据项,它对应于数组中最后一个填入的数据单元,把这个节点直接复制到根节点处
heapArray[0] = heapArray[N-1];
这里写图片描述

插入:
插入节点也是很容易的。插入使用向上筛选,而不是向下筛选。节点初始时插入到数组最后一个空着的单元中,数组容量大小增1.
heapArray[N] = newNode
问题在于这样可能会破坏堆的条件。如果新插入的节点大于它的父节点时,就会发生这种情况。因为父节点在堆的底端,它可能很小,所以新节点就显得比较大。因此,需要向上筛选新节点,直到它在一个大于它的节点之下,在一个小于她的节点之上。

这里写图片描述

堆的java代码:
用数组表示一棵树时有一些要点:若数组中节点的索引为x,则:
—-它的父节点的下标为(X - 1) /2
—-它的左子节点的下标为 2 * X + 1
—-它的右子节点的下标为2 * X +2

插入:向上筛选的算法放在它自己的方法中。insert()方法直接调用这个trickleUp()方法。

public class test
{
     public  boolean  insert(int  key)
     {
         if(currentSize == maxSize)
             return  false;
         Node  newNode = new Node(key);
         heapArray[currentSize] = newNode;
         trickleUp(currentSize++);
         return  true;
     }

先检查以确定数组不满,然后用参数传递的关键字创建一个新的节点。把这个节点插入到数组的末端。最后,调用trickleUp方法把这个节点移动到合适的位置


——————————————————————
 public  void  trickleUp(int  index)
     {
         int parent =(index - 1)/ 2;
         Node  buttom = heapArray[index];
         while(index >0 && heapArray[parent].getKey() < buttom.getKey())
         {
             heapArray[index] = heapArray[parent];
             index = parent;
             parent = (parent -1)/2;
         }
         heapArray[index] = buttom;
     }
   trickleUp()方法的参数是新插入的数据项的下标。找到这个位置的父节点,把这个节点保存到名为buttom的变量里。在while循环内部,变量index沿着路径朝根的方向上行,依次指向每一个节点。只要没有达到根(index>0),且index的父节点的关键字(iData)小于这个新节点,循环就一直执行。
    while循环体执行向上筛选过程的一个步骤。首先把父节点复制到index所指向的单元中,即向下移动了父节点,然后头盖骨把父节点的下标传给index,使得index上移了,并且还把父节点的下标给予它的父 节点。
  最后,当退出循环时,临时存储在buttom里要插入的新结点,插入到index所指示的单元中。这就是第一个它大于父节点的位置。

————————
移除:如果在移除的过程中包含了向下筛选的算法,那么移除算法也不复杂,保存根节点,把最后一个节点(下标为currentSize -1) 放到根的位置上,然后调用trickleDown(),把这个节点放到适当的位置
public Node remove()
{
Node root = heapArray[0];
heapArray[0] = heapArray[–currentSize];
trickleDown(0);
return root;
}


trickleDown()稍微复杂,因为它必须判断哪个子节点更大。首先,把下标为index的节点保存到一个临时变量top中。
只要index没有到最底层–这就是说,只要它还有一个子节点,while循环就一直执行。在循环内部,检查是否有右子节点(可能只有一个左子节点),如果有,比较两个子节点的关键字,给largerChild设置合适的值。

然后检查原来节点(现在根位置的节点)的关键字是否大于largerChild,如果是,向下筛选的过程就完成了,并且退出循环。
在退出循环时,只需要把存储在top中的节点恢复到适当的位置上,即index所指的位置。


public  void  trickleDown(int  index)
    {
        int  largerChild;
        Node  top = heapArray[index];
        while(index < currentSize / 2)
        {
            int  leftChild = 2 * index +1;
            int  rightChild = leftChild+1;

            if(rightChild <currentSize  && 
                    heapArray[leftChild].getKey() < heapArray[rightChild].getKey())
            largerChild = rightChild;
            else
                largerChild =  leftChild;
            if(top.getKey()  >=  heapArray[largerChild].getKey())
                break;
            heapArray[index] = heapArray[largerChild];
            index = largerChild;

        }
        heapArray[index]  = top;
    }

关键字更改:
有了trickleUp()和trickleDown()方法之后,很容易实现改变节点的优先级(关键字)算法,先更改节点关键字的值,然后再把节点向上或者向下移动到适当的位置。
程序先检查给定的第一个参数index是否合法,如果是合法的,改变这个下标所指节点的iData字段,赋值为第二个参数 的值。


    public  boolean  change(int  index , int newValue)
    {
        if(index<0    ||   index >= currentSize)
            return  false;
        int  oldValue = heapArray[index].getKey();
        heapArray[index].setKey(newValue);

        if(oldValue < newValue)
            trickleUp(index);
        else
            trickleDown(index);
        return  true;
    }

——————————————————-

堆操作的完整java代码:

class  Node
{
    private  int  iData;
    //..............
    public  Node(int  key)
    {
        iData = key ;
    }
    //.............
    public  int getKey()
    {
        return  iData;
    }

    //................
    public  void  setKey(int  id)
    {
        iData = id;
    }
}

//......................................................
class Heap
{
    private  Node[]   heapArray;
    private  int  maxSize;
    private  int  currentSize;
    //................................
    public  Heap(int  mx)
    {
        maxSize = mx;
        currentSize = 0;
        heapArray = new Node[maxSize];
    }
    //............................
    public  boolean isEmpty()
    {
        return  currentSize  == 0;
    }

    //.....................................

    public  boolean  insert(int  key)
     {
         if(currentSize == maxSize)
             return  false;
         Node  newNode = new Node(key);
         heapArray[currentSize] = newNode;
         trickleUp(currentSize++);
         return  true;
     }
     //........................................
     public  void  trickleUp(int  index)
     {
         int parent =(index - 1) / 2;
         Node  buttom = heapArray[index];
         while(index >0 && heapArray[parent].getKey() < buttom.getKey())
         {
             heapArray[index] = heapArray[parent];
             index = parent;
             parent = (parent -1)/2;
         }
         heapArray[index] = buttom;
     }

     //........................................
     public   Node remove()
     {
         Node   root = heapArray[0];
         heapArray[0] = heapArray[--currentSize];
         trickleDown(0);
         return  root;
     }

     //......................................

    public  void  trickleDown(int  index)
    {
        int  largerChild;
        Node  top = heapArray[index];
        while(index < currentSize / 2)
        {
            int  leftChild = 2 * index +1;
            int  rightChild = leftChild+1;

            if(rightChild <currentSize  && 
                    heapArray[leftChild].getKey() < heapArray[rightChild].getKey())
            largerChild = rightChild;
            else
                largerChild =  leftChild;
            if(top.getKey()  >=  heapArray[largerChild].getKey())
                break;
            heapArray[index] = heapArray[largerChild];
            index = largerChild;

        }
        heapArray[index]  = top;
    }


    public  boolean  change(int  index , int newValue)
    {
        if(index<0    ||   index >= currentSize)
            return  false;
        int  oldValue = heapArray[index].getKey();
        heapArray[index].setKey(newValue);

        if(oldValue < newValue)
            trickleUp(index);
        else
            trickleDown(index);
        return  true;
    }
     //.................................

    public   void  displayHeap()
    {
        System.out.print("heapArray:");
        for (int m = 0; m < currentSize; m++)

            if(heapArray[m]  !=  null)
                System.out.print(heapArray[m].getKey()+" ");
            else 
                System.out.print("--");
            System.out.println();

            int nBlanks = 32;
            int  itemsPerRow =1;
            int column = 0;
            int j=0;
            String  dots = "......................";
            System.out.println(dots+dots);

            while(currentSize > 0)
            {
                if(column == 0)
                    for (int k = 0; k < nBlanks; k++)
                    System.out.print(' ');
                System.out.print(heapArray[j].getKey());
                if(++j  == currentSize)
                    break;
                if(++column  == itemsPerRow)
                {
                    nBlanks  /= 2;
                    itemsPerRow *= 2;
                    column = 0;
                    System.out.println();
                }
                else
                    for (int k = 0; k < nBlanks *2 -2; k++)

                        System.out.print(' ');
            }
            System.out.println("\n"+dots+dots);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值