堆的注意事项

创建一个堆

/**
 * 创建一个堆
 *
 */

public class TestHeap {
    public int[] elem;
    public int usedSize;

    public static final int DEFAULT_SIZE = 10;

    public TestHeap() {
        this.elem = new int[DEFAULT_SIZE];
    }

    public void initElem(int[] array) {
        for (int i = 0; i < array.length; i++) {
            elem[i] = array[i];
            usedSize++;
        }
    }

    public void createHeap() {
        /**
         * 找到所有的根节点,从下往上,对根节点的小树进行调整
         */
        for (int parent = (usedSize-1-1)/2; parent >= 0; parent--) {
            //找到所有的根节点
            //顺序是 自下而上
            shiftDown(parent,usedSize);
            //所有的根节点进行的调整
            //传参usedSize的原因 结束的条件
            //当一个树调整完成后可能会影响后面的树,要对后面的树再进行判断与调整,
            //找个条件让他停下来,就是usedSize,当child >= usedSize时就停下来
        }
    }

    private void shiftDown(int parent, int len) {
        int child = 2*parent+1;
        //堆中,根节点一定有左孩子,因为1:它是根 2:它是完全二叉树
        while(child < len) { 
            /* 
            child指向的一定是最大的------>这个操作实现了复用if(elem[child] > elem[parent])这一部分
            右孩子比较特殊所以先判断右孩子, 如果有右孩子 && 右孩子比作孩子大 那 child++
            如果不满足以上条件,说明没有右孩子或者是右孩子比左孩子小 那child不动
            底下再判断一下 如果child > parent 那就交换 如果不是那就退出循环 
             */
            if(child+1 < len && elem[child] < elem[child+1]) {
                // 如果有右孩子并且右孩子比左孩子大
                child++;
            }
            // 此时Child 指向左右孩子种最大的
            if(elem[child] > elem[parent]) {
                int temp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = temp;
                parent = child;
                child = parent*2+1;
            }else {
                break;
            }
        }
    }

    /**
     * 自写
     * @param parent 根节点
     * @param usedSize 判断的范围
     */
    private void shiftDown2(int parent, int usedSize) {
        int child = parent*2+1;
        while(child < usedSize) {
            if(child+1 < usedSize) { //有右孩子
                child = elem[child] > elem[child+1]? child:child+1;
                if(elem[child] > elem[parent]) {
                    int temp = elem[child];
                    elem[child] = elem[parent];
                    elem[parent] = temp;
                    parent = child;
                    child = parent*2+1;
                }else {
                    break;
                }
            }else { //没有
                if(elem[child] > elem[parent]) {
                    int temp = elem[child];
                    elem[child] = elem[parent];
                    elem[parent] = temp;
                    break;
                }else{
                    break;
                }
            }
        }
    }
}

建堆的时间复杂度分析

在这里插入图片描述

在堆中插入元素

  • 插入前此时的数组已经满足了 大根堆或者是小根堆 。
    -在这里插入图片描述

  • 旁边的节点不用管,因为它一定比它所对应的根要小(小根堆)或大(大根堆)。

/**
     * 在堆中插入元素
     */
    public void offor (int val) {
        if(isFull()) {
            // 扩容
            elem = Arrays.copyOf(elem, elem.length*2);
        }
        elem[usedSize] = val;
        usedSize++;
        int child = usedSize-1;
        // 向上调整
        shiftUp(child);
    }

    // 向上调整
    public void shiftUp(int child) {
        int parent = (child-1)/2;
        while(child > 0) {
            if(elem[child] > elem[parent]) {
                // 交换
                int temp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = temp;
                child = parent;
                parent = (child-1)/2;
            }else {
                break;
            }
        }
    }

    public boolean isFull() {
        return this.usedSize == elem.length;
    }

在堆中删除(弹出)元素

删除前此时的数组已经满足了 大根堆或者是小根堆 。

在这里插入图片描述

/**
     * 在堆中删除(弹出)元素
     */
    public void pop() {
        if(isEmpty()) {
            return;
        }
        // 交换
        int temp = elem[usedSize-1];
        elem[usedSize-1] = elem[0];
        elem[0] = temp;
        usedSize--;
        //数组中我们只操作下标从[0 , usedSize)这部分的数据
        shiftDown3(0, usedSize);
    }

    //向下调整
    public void shiftDown3(int parent, int len) {
        int child = parent*2+1;
        while(child < len) {
            /* 
            child指向的一定是最大的------>这个操作实现了复用if(elem[child] > elem[parent])这一部分
            右孩子比较特殊所以先判断右孩子, 如果有右孩子 && 右孩子比作孩子大 那 child++
            如果不满足以上条件,说明没有右孩子或者是右孩子比左孩子小 那child不动
            底下再判断一下 如果child > parent 那就交换 如果不是那就退出循环 
             */
            if(child+1 < usedSize && elem[child+1] > elem[child]){
                child = child+1;
            }
            
            if(elem[child] > elem[parent]) {
                // 交换
                int temp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = temp;

                parent = child;
                child = parent*2+1;
            }else {
                break;
            }
        }

    }
    public boolean isEmpty() {
        return usedSize == 0;
    }

PriorityQueue的特性

  1. Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线
    程不安全的,PriorityBlockingQueue是线程安全的,本文主要介绍PriorityQueue
  2. PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出
    ClassCastException异常
  3. 不能插入null对象,否则会抛出NullPointerException
  4. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容
  5. 插入和删除元素的时间复杂度为log2 n (最坏情况下向下调整,向上调整 树的高度-1次)
  6. PriorityQueue默认情况下是小堆—即每次获取到的元素都是最小的元素
/**
 * 学生类
 */
class student implements Comparable<student> {
    public String name;
    public int age;

    public student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(student o) {
        return this.age - o.age;  //顺序十分重要,因为底层的原码的判断条件是卡死的
    }

    @Override
    public String toString() {
        return "student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


public class demo {
    
    public static void main(String[] args) {
        TestPriorityQueue();
    }
    
    public static void TestPriorityQueue() {
        PriorityQueue<student> q1 = new PriorityQueue<>();
        
        q1.offer(new student("小白",10));
        q1.offer(new student("小红",20));
        q1.offer(new student("小黑",30));
        
        System.out.println(q1.peek());
        //小根堆
    }
}

在这里插入图片描述

class Comp implements Comparator<Integer> {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2.compareTo(o1);
    }
}

public class demo {
    public static void main(String[] args) {
        TestPriorityQueue();
    }
    public static void TestPriorityQueue() {
        PriorityQueue<Integer> q2 = new PriorityQueue<>(new Comp());
        q2.offer(4);
        q2.offer(3);
        q2.offer(2);
        q2.offer(1);
        System.out.println(q2.peek());
    }
}


在这里插入图片描述

总结:

  1. 当没有传数组容量的时候默认是11
  2. 当没有传比较器的时候,这个对象必须是可比较的,即必须继承comparable< > 方法,并且重写compareTo方法;注意基本数据类型内已经继承comparable< > 方法,并且重写compareTo方法,引用类型需要自己写,而且注意重写compareTo方法的顺序非常重要因为底层条件是卡死的。所以写的顺序决定了到底换不换。
  3. 当传入比较器的时候,会优先使用比较器中的compare方法,同样重写的比较器中的compare方法的顺序非常重要。
  4. 最后注意:基本数据类型默认是小根堆,因为进本数据类型里面继承comparable< > 方法,并且重写compareTo方法,而且compareTo方法的顺序没法给他改变。如果你想将基本数据类型改为是大根堆,你要利用优先使用比较器这特点,传入比较器,然后修改比较器中的compare方法的顺序。

以下三者是等价的

class Comp implements Comparator<Integer> {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2.compareTo(o1);
    }
}
 PriorityQueue<Integer> q2 = new PriorityQueue<>(new Comp());
PriorityQueue<Integer> q3 = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2.compareTo(o1);
            }
    //匿名内部类,这个类实现了comparator这个接口,并重写了他的方法。
        });
PriorityQueue<Integer> priorityQueue2 = new PriorityQueue<>((x,y)->{return x.compareTo(y);});
PriorityQueue<Integer> priorityQueue3 = new PriorityQueue<>((x,y)-> x.compareTo(y));
//lambda表达式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HackerTerry

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值