zoukankan      html  css  js  c++  java
  • 2.7 二叉堆及优先队列

    一.二叉树

    1.首先介绍一下二叉树

    (1)二叉树:每个节点最多有两个子树的结构

    (2)完全二叉树:叶节点只能出现在最下层和次下层,并且最下一层的节点集中在该层最左边的若干位置的二叉树

    (3)满二叉树:叶节点只能出现在最下层

    二.二叉堆(堆)

    1.堆有序:当一棵二叉树的每个结点都不小于它的两个子结点时,称为堆有序

    2.二叉堆:一组能够用堆有序的完全二叉树排序的元素,并且能在数组中按照层级存储。

    3.说明:

    (1)二叉堆可以用数组来表示,从1-N,根节点位置在a[1]

    (2)二叉堆可以不使用指针而是简单的使用数学关系,来表示结构

    (3)位置k的结点的父节点是(int)k/2,两个子结点的位置是2k和2k+1

    (4)一颗大小为N的完全二叉树的高度是|_lgN_|

    4.堆的维护

    (1)上浮。当某个结点比父结点更大时,需要交换它和父结点的位置,并继续判断,直到比父结点小。

        private void swim(int k) {
            while(k>1 && less(k/2,k)) {
                 exch(k,k/2);
                 k=k/2;
            }
        }
    View Code

    (2)下沉。当某个结点比它的两个子结点或其中一个子结点小,需要将父结点与子结点中较大的一个进行交换,重复该过程,直到比子结点都大。

    private void sink(int k) {
            while(2*k<=N) {
                int j=2*k;
                //考虑到只有左结点的情况
                //如果有2个结点,则取最大的结点
                if(j<N && less(j,j+1)) j++;
                //比较子结点和k的大小
                if(less(j,k)) break;
                exch(j,k);
                k=j;
                
            }
        }
    View Code

    三.优先级队列

    1.能够进行删除最大元素和插入元素的操作的数据结构

    2.方法1:将元素进行排序,然后就可以用普通队列的方式,从队尾插元素,从队首取元素

       方法2:使用无序数组的方式,在a[N]出插入元素,删除最大元素时,需要遍历数组

       方法3:利用堆来实现优先队列

    3.性能比较

    4.使用无序数组实现优先队列

    package com.cx.sort;
    
    //可变数组构建的优先级队列
    public class UnorderedMaxPQ<T extends Comparable<T>> {
    
        private T[] pq;
        private int N;
        
        public UnorderedMaxPQ(int capacity) {
            pq=(T[])new Comparable[capacity];
        }
        public boolean isEmpty() {
            return N==0;
        }
        public int size() {
            return pq.length;
        }
        public void insert(T x) {
            //如果pq容量不够,将pq容量变为2倍
            if(N==pq.length) resize(2*N);
            pq[N++]=x;
        }
        public T delMax() {
            //初级优先队列,是遍历数组找到最大的元素,然后删除
            int max=0;
            for(int i=1;i<N;i++) {
                if(less(max,i)) max=i;
            }
            exch(max,N-1);
            T delVal=pq[--N];
            //避免对象游离
            pq[N]=null;
            //删除完后,如果数组长度过大(1/4满),将数组长度减半
            if(N>0 && N<pq.length/4) resize(pq.length/2);
            return delVal;
        }
        
        private boolean less(int i,int j) {
            return pq[i].compareTo(pq[j])<0;
        }
        private void exch(int i,int j) {
            T temp=pq[i];
            pq[i]=pq[j];
            pq[j]=temp;
        }
        //可变数组
        private void resize(int capacity) {
            T[] temp=(T[])new Comparable[capacity];
            for(int i=0;i<N;i++)
                temp[i]=pq[i];
            pq=temp;
        }
    
    }
    View Code

    5.使用堆实现优先队列:充分利用上一段指出的堆的维护的特点

    (1)当插入元素时,将元素放于数组的末尾,然后利用swim,即可继续维持堆的结构

    (2)当删除元素时,优先级最高的元素时根的元素即a[1]。将a[1]与末尾元素交换,对刚变为根节点的元素使用sink,即可继续维持堆的结构。并且成功找到并删除了优先级最高的结点

    (3)由于堆是从数组的1到N的,所以在调整数组大小的时候,有很多需要注意的地方,具体实现如下:

    public class MaxPQ <T extends Comparable<T>>{
        private T[] pq;
        //元素存在pq[1..N]
        private int N=0;
        
        public MaxPQ(int capacity) {
            pq=(T[])new Comparable[capacity+1];
        }
        public boolean isEmpty() {
            return N==0;
        }
        //pq中有效元素的个数
        public int size() {
            return N;
        }
        //pq的真实容量
        public int length() {
            return pq.length;
        }
        public void insert(T v) {
            //如果容量不够,变为2倍
            if(N==pq.length-1) resize(2*N);
            pq[++N]=v;
            swim(N);
        }
        public T delMax() {
            T max=pq[1];
            exch(1,N);
            pq[N--]=null;
            sink(1);
            //如果容量过大,变为一半
            if(N>0 && N<pq.length/4) resize(pq.length/2); 
            return max;
        }
        //将对象上浮
        private void swim(int k) {
            while(k>1 && less(k/2,k)) {
                 exch(k,k/2);
                 k=k/2;
            }
        }
        //将对象下沉
        private void sink(int k) {
            while(2*k<=N) {
                int j=2*k;
                //考虑到只有左结点的情况
                //如果有2个结点,则取最大的结点
                if(j<N && less(j,j+1)) j++;
                //比较子结点和k的大小
                if(less(j,k)) break;
                exch(j,k);
                k=j;
                
            }
        }
        private boolean less(int i,int j) {
            return pq[i].compareTo(pq[j])<0;
        }
        private void exch(int i,int j) {
            T temp=pq[i]; pq[i]=pq[j]; pq[j]=temp;
        }
        //可变数组
        private void resize(int capacity) {
            T[] temp=(T[])new Comparable[capacity+1];
            //这一步注意是从1..N,容易把pq[N]变为空了
            for(int i=0;i<=N;i++)
                temp[i]=pq[i];
            pq=temp;
        }
        public void show() {
            for(T t:pq) {
                System.out.print(t+" ");
            }
            System.out.println();
        }
    }
    View Code

    6.测试代码如下:

    package com.cx.sort;
    
    public class TestPQ {
    
        public static void main(String[] args) {
    //        UnorderedMaxPQ<String> pq1=new UnorderedMaxPQ<>(5);
            MaxPQ<String> pq1=new MaxPQ<>(2);
            pq1.insert("c");
            System.out.println(pq1.size()+" "+pq1.length());
            pq1.insert("a");
            System.out.println(pq1.size()+" "+pq1.length());
            pq1.insert("d");
            System.out.println(pq1.size()+" "+pq1.length());
            pq1.insert("m");
            System.out.println(pq1.size()+" "+pq1.length());
            pq1.insert("r");
            System.out.println(pq1.size()+" "+pq1.length());
            pq1.show();
            pq1.insert("z");
            System.out.println(pq1.size()+" "+pq1.length());
            pq1.show();
            pq1.insert("f");
            System.out.println(pq1.size()+" "+pq1.length());
            pq1.show();
            while(pq1.size()>0) {
                System.out.println(pq1.size()+" "+pq1.length());
                pq1.delMax();
                pq1.show();
            }
        }
    
    }
    View Code

    7.说明:

    对于一个含有N个元素的基于堆的优先队列,插入元素操作只需要不超过(lgN+1)次比较,删除最大元素的操作需要不超过2lgN次比较

  • 相关阅读:
    myBatsi调用存储过程
    Db2与Oracle的区别
    动态SQL与模糊查询
    一对多联表查询
    一对一联表查询
    025 资源的隔离与配置
    024 Yarn核心生态系统
    sift算法特征点如何匹配?
    Camera 图像处理原理分析
    基于灰度世界、完美反射、动态阈值等图像自动白平衡算法的原理、实现及效果
  • 原文地址:https://www.cnblogs.com/sunnyCx/p/8182887.html
Copyright © 2011-2022 走看看