数据结构之堆(Heap)(一)

堆的概述

什么是堆

堆是一种特殊的基于树的数据结构。树是完全二叉树,实现是数组。

堆的性质

这里的堆,一般指二叉堆,符合下面两种性质的。

  • 堆总是一颗完全二叉树。
  • 堆总是有序的。即堆的父结点总是大于等于子结点,或者堆的父结点总是小于等于子结点。
    所以堆分为两种:最大堆(或大根堆,即堆的父结点总是大于等于子结点) 和 最小堆(或小根堆,即堆的父结点总是小于等于子结点)。

堆的表示

max_min_heap
如上图,示例了一个最大堆和一个最小堆。下面以最大堆来说明,堆如何表示的。

如下图,就是最大堆的数组存储的示意。
heap_max_arr
那么如何能知道结点的关系呢?哪个是父结点、子结点呢?
这就与完全二叉树的特点有关了。在数据结构之树(Tree)(一)_树的基础中也介绍了二叉树和完全二叉树的特点(注意树中介绍的特点是从1开始编号的,这里从0开始。),不了解可以先查看参考下。
所以有(Arr[]表示存储的数组,如上图):

//根结点即第一个元素
root = Arr[0];
//结点i的 父结点
parent = Arr[(i-1)/2];
//结点i的 左孩子结点
leftChild = Arr[(2*i)+1];
//结点i的 右孩子结点
rightChild = Arr[(2*i)+2];

注:结点i的 父结点(i-1)/2,因为int/int是取整,其实相当于Math.floor((i-1)/2)。

堆的操作

这里还是以最大堆为例说明
堆的主要操作:

  • getMax():即获取最大堆的根结点,也就是数组第一个元素。时间复杂度O(1)。
  • insert():插入一个新元素。插入过程和操作也很好理解,在二叉树的末尾(即最后一层最后一个结点右边位置)插入,插入完成后主要是调整结点使所有结点满足最大堆的属性。插入结点与父结点比较,如果比父结点大,则与父结点互换位置,依次类推,直到父结点比它大或者已交换至根结点。这个时间复杂度为O(logn)。
  • delete():删除某个元素。这个过程是:删除某个结点元素,然后用最后一个叶子结点替换到删除位置,这样树仍是完整的 但不符合最大堆属性。所以删除替换后,对堆进行maxHeapify()操作,调整以删除结点为根结点的子树,使所有结点符合最大堆要求即可。这个时间复杂度为O(logn)。

过程不复杂也很容易理解,就不用示意图显示了。
下面是编写的一个最大堆的操作demo,大家可以参考了解下过程。

public class MaxHeap {
    private int[] heapArr;
    private int heapSize;
    private int maxsize;

    public MaxHeap(int maxsize) {
        this.maxsize = maxsize;
        this.heapSize = 0;
        heapArr = new int[this.maxsize];
   }

    //父结点位置 数组下标
    private int parent(int pos) {
        return (pos - 1) / 2;
    }

    //左孩子结点位置 数组下标
    private int leftChild(int pos) {
        return (2 * pos) + 1;
    }
	
    //右孩子结点位置 数组下标
    private int rightChild(int pos) {
        return (2 * pos) + 2;
    }
	
    //交换位置
    private void swap(int fpos, int spos) {
        int tmp = heapArr[fpos];
        heapArr[fpos] = heapArr[spos];
        heapArr[spos] = tmp;
    }
	
    //插入:末尾插入,然后调整结点使符合最大堆属性
    public void insert(int element) {
        int current = heapSize;
		
        heapArr[heapSize++] = element;
		
        while (heapArr[current] > heapArr[parent(current)]) {
            swap(current, parent(current));
            current = parent(current);
        }
    }
	
    //删除元素
    public int delete(int pos) {
        int tmp = heapArr[pos];
        heapArr[pos] = heapArr[heapSize-1];
        heapSize--;
        maxHeapify(pos);
        return tmp;
    }

	//删除堆的最大元素,即最大堆的根结点
//	public int extractMax() {
//		int root = heapArr[0];
//		heapArr[0] = heapArr[heapSize--];
//		maxHeapify(0);
//		return root;
//	}

    // Returns true of given node is leaf
    private boolean isLeaf(int pos) {
        if (pos > (heapSize/2 - 1) && pos <= (heapSize-1)) {
            return true;
        }
        return false;
    }

    //递归调整某结点为根结点的子树,是符合堆的属性。
    private void maxHeapify(int pos) {
        if (isLeaf(pos))
            return;

        if (heapArr[pos] < heapArr[leftChild(pos)] || heapArr[pos] < heapArr[rightChild(pos)]) {
            if (heapArr[leftChild(pos)] > heapArr[rightChild(pos)]) {
                swap(pos, leftChild(pos));
                maxHeapify(leftChild(pos));
            } else {
                swap(pos, rightChild(pos));
                maxHeapify(rightChild(pos));
            }
        }
    }
	
    private void printHeap() {
        System.out.print("size=" + heapSize + ";Heap={");
        for (int i = 0; i < heapSize; i++) {
            System.out.print(heapArr[i]);
            if (i < heapSize - 1) {
                System.out.print(",");
            }
        }
        System.out.print("}");
        System.out.println();
    }

    public static void main(String[] arg) {
        //创建堆并插入数据,形成了开始示意图中最大堆的样子。
        MaxHeap maxHeap = new MaxHeap(15); maxHeap.printHeap();
        maxHeap.insert(8); 
        maxHeap.insert(9); maxHeap.printHeap();
        maxHeap.insert(6); 
        maxHeap.insert(5); maxHeap.printHeap();
        maxHeap.insert(3); 
        maxHeap.insert(1); maxHeap.printHeap();
        //删除堆顶
        maxHeap.delete(0); maxHeap.printHeap();
        //删除了编号为1的位置
        maxHeap.delete(1); maxHeap.printHeap();
    }
}

很容易理解。下面是打印的信息,与预期一致:

size=0;Heap={}
size=2;Heap={9,8}
size=4;Heap={9,8,6,5}
size=6;Heap={9,8,6,5,3,1}
size=5;Heap={8,5,6,1,3}
size=4;Heap={8,3,6,1}

猜你喜欢

转载自www.cnblogs.com/fanglongxiang/p/13168668.html