• 数据结构之堆(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}
    
  • 相关阅读:
    设计模式的概念与作用
    动画回调函数未执行
    使用PathfindingProject Pro 4.0.10实现2D自动寻路
    一些概念和路径记录
    android 数据库添加字符串 添加失败 解决方案
    view的focusable属性改变设置是否可获取光标
    view的clickable属性和点击background颜色改变
    java中一对多 关联建表
    Android 自定义View
    Android 之常用布局
  • 原文地址:https://www.cnblogs.com/fanglongxiang/p/13168668.html
Copyright © 2020-2023  润新知