java数据结构:基于树的堆

本教程的内容基本来自于《Java数据结构与算法》
堆一种是一种特殊的二叉树结构,包含如下特点:

  • 它是完全二叉树,即除了最后一层节点不是满的,其他层节点从左到右都完全是满的;
  • 一般使用数组来存储堆数据结构;
  • 堆中每个节点的关键字都大于等于(小于等于)这个节点的子节点的关键字,节点关键字大于等于子节点的关键字成为最大堆;反之,称为最小堆。

下图显示了完全二叉树与非完全二叉树的区别
java数据结构:基于树的堆_第1张图片
下图显示了一个最大堆的数组存储方式
java数据结构:基于树的堆_第2张图片
图中可以看到,根节点存储在数组的第一个位置,然后从上层到下层,从左到右,将数据存储到数组中。

很容易推得,父节点与子节点在数组中位置角标有如下关系:

设节点的角标为 i i ,则左子节点(如果有左孩子的话)在数组中的角标为 2i+1 2 ∗ i + 1 ,右子节点(如果有右孩子的话)在数组中的角标为 2i+2 2 ∗ i + 2

下面代码实现最大堆的过程。

1. 节点定义
// 定义节点
class Node{
    private int idata;
    public Node(int data){
        idata = data;
    }
    public int getKey(){
        return idata;
    }
}
2. 定义堆
// 定义堆
class Heap{
    private Node[] heapArray;       // 存储节点的数组
    private int maxSize;            // 最大存储大小
    private int currSize;           // 当前大小
    public Heap(int size){
        maxSize = size;
        currSize = 0;
        heapArray = new Node[size];
    }
}
3. 判断堆是否为空
    //判断是否为空
    public boolean isEmpty(){
        return (currSize == 0);
    }
4. 插入元素

插入元素时,总是先插入到数组最后,插入并调整结构的步骤:

  • 将待插入元素放到数组最后;
  • 令当前节点为插入元素所在节点;
  • 当前节点与父节点比较大小,如果父节点的关键字小于当前节点,则与父节点交换;否则,表示调整结构结束,退出;
  • 如果没有退出,对交换后的当前节点重复上一步过程。

下图为插入节点100的过程:
java数据结构:基于树的堆_第3张图片
代码如下

    // 插入元素
    public boolean insert(int key){
        // 存储已满
        if (currSize == maxSize)
            return false;
        Node node = new Node(key);
        // 将新节点放到数组最后
        heapArray[currSize] = node;
        // 调整堆结构
        trickleUp(currSize++);
        return true;
    }
    // 向上比较
    private void trickleUp(int i) {
        // 父节点角标
        int parent = (i - 1) / 2;
        Node temp = heapArray[i];
        // 当前节点关键字大于父节点关键字
        while (i > 0 && heapArray[i].getKey() > heapArray[parent].getKey()){
            heapArray[i] = heapArray[parent];
            i = parent;
            parent = (i - 1) / 2; 
        }
        heapArray[i] = temp;
    }
5. 删除元素

堆中删除元素,总是删除堆顶元素,删除并调整结构的步骤:

  • 删除堆顶元素,并将最后一个元素放到堆顶;
  • 设当前节点为堆顶节点;
  • 找到当前节点的子节点中关键字最大的那个节点,然后将此子节点与当前节点比较大小,如果此节点关键字大于当前节点,则交换两个节点;否则,表示调整结构结束,退出;
  • 如果没有退出,对交换后的当前节点重复上一步过程。

下图为删除节点100的过程:
java数据结构:基于树的堆_第4张图片
代码如下:

// 删除元素
public Node remove(){
    Node root = heapArray[0];
    // 最后一个元素放到堆顶
    heapArray[0] = heapArray[--currSize];
    // 向下比较
    trickleDown(0);
    return root;
}
// 向下比较
private void trickleDown(int i){
    int largeChild;
    Node top = heapArray[i];
    while (i < currSize/2){
        int leftChild = (i-1) / 2;
        int rightChild = leftChild + 1;
        if (rightChild < currSize && 
                heapArray[rightChild].getKey() > heapArray[leftChild].getKey())
            largeChild = rightChild;
        else
            largeChild = leftChild;
        // 如果当前节点关键字大于等于最大子节点关键字,退出
        if (top.getKey() >= heapArray[largeChild].getKey())
            break;
        heapArray[i] = heapArray[largeChild];
        i = largeChild;
    }
    heapArray[i] = top;
}

github完整代码:
https://github.com/gamersover/data_structure_java/blob/master/Heap/HeapApp.java

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