堆 ----> 优先级队列

目录

概念

如何创建一个堆

1.向下调整

2 . 向上调整

堆的删除和插入

优先级队列

1.概念

2. 关于PriorityQueue的使用注意 : 

对象的比较

 在二叉树中经常使用链式存储,而堆也是二叉树只不过它是顺序存储的.

在java中提供了关于堆的集合 PriorityQueue 优先级队列.

在了解优先级队列之前,先来了解一下.

概念

如果有一个 关键码的集合 K = {k0 k1 k2 kn-1} ,把它的所有元素 按完全二叉树的顺序存储方式存储 在一 个一维数组中 ,并满足: Ki <= K2i+1 Ki<= K2i+2 (Ki >= K2i+1 Ki >= K2i+2) i = 0 1, 2… ,则 称为 小堆 ( 或大堆) 。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的性质:
   1.堆中某个节点的值总是不大于或不小于其父节点的值;
   2.堆总是一棵完全二叉树。

如何创建一个堆

建堆的方式有两种 : 一种是使用向下调整进行建堆,一种是向上调整建堆.

1.向下调整

向下调整 : 时间复杂度 : O(logn)   空间复杂度 : O(1)

通过向下调整建堆的时间复杂度 : O(n)  空间复杂度 : O(1)

//建堆(大根堆)
    public void createHeap(int[] array) {
        //array.length - 1 是数组有效数字的最后一个下标
        //(arrat.length - 1 - 1) / 2  这个公式得到的就是父亲下标
        for (int parent = (array.length - 1 - 1) / 2; parent >= 0; parent--) {
            shiftDown(array,parent,array.length - 1);
        }
    }
    //向下调整
    public void shiftDown(int[] array, int parent, int end) {
        //得到父亲节点的左孩子的位置
        int child = parent * 2 + 1;
        //判断孩子下标有没有越界
        while (child <= end) {
            //判断有没有右孩子 并且  右孩子的值有没有左孩子大
            if (child + 1 <= end && array[child + 1] > array[child]) {
                child++;
            }
            //如果父亲下标的值小于孩子下标的值就进行交换   否则就退出循环
            //并且让parent = child 进入下一轮循环
            if (array[parent] < array[child]) {
                swap(array,parent,child);
                parent = child;
                child = parent * 2 + 1;
            } else {
                break;
            }
        }

    }
    //交换
    public void swap(int[] array, int x, int y) {
        int temp = array[x];
        array[x] = array[y];
        array[y] = temp;
    }

2 . 向上调整

//使用向上调整建堆
    public void createHeap1(int[] array) {
        for (int child = 1; child < array.length; child++) {
            shiftUp(array,child);
        }
    }
    //向上调整
    public void shiftUp(int[] array, int child) {
        int parent = (child - 1) / 2;
        while (child > 0) {
            if (array[child] > array[parent]) {
                swap(array,child,parent);
                child = parent;
                parent = (child - 1) / 2;
            } else {
                break;
            }
        }
    }
    //交换
    public void swap(int[] array, int x, int y) {
        int temp = array[x];
        array[x] = array[y];
        array[y] = temp;
    }

向上调整的时间复杂度 : O(logn)

通过向上调整建堆的时间复杂度 : O(n*logn)

堆的删除和插入

插入 : 直接让插入进来的数也就是最后一个下标向上调整.

public void insertNum(int[] array, int num) {
        int length = array.length;
        //在这里假设数组没满 就把Num放到length位置
        array[length] = num;
        shiftUp(array,length);
    }

删除 : 思路 : 让堆顶元素(0下标) 与 数组的最后一个有效元素的下标进行交换.

         交换完成后最后一个数就不再列入有效数字中

          再让0下标进行向下调整

//堆的删除
    public void poll(int[] array) {
        //这里要注意  假设array.length - 1就是数组的有效数字的最后一个
        swap(array,0,array.length - 1);
        shiftDown(array,0,array.length - 2);
    }

优先级队列

1.概念

前面介绍过队列, 队列是一种先进先出 (FIFO) 的数据结构, 但有些情况下, 操作的数据可能带有优先级,一般出队 列时,可能需要优先级高的元素先出队列.
比如 : 有一组数我每次都取0下标的值并且要确保每次取出来的数是 最大值.
在这种情况下, 数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象 。这种数据结构就是优先级队列 (Priority Queue)

2. 关于PriorityQueue的使用注意 : 

1.使用时必须导入PriorityQueue所在的包 : 

import java.util.PriorityQueue;
2. 不能插入null对象,否则会抛出NullPointerException.

3. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容.

4.插入和删除元素的时间复杂度为O(logn)

5.PriorityQueue底层使用了堆数据结构 .

6.PriorityQueue默认情况下是小堆 ---- >即每次获取到的元素都是最小的元素

7. PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出
ClassCastException异常.
注意:默认情况下,PriorityQueue队列是小堆,如果需要大堆需要用户提供比较器.
对于第七点我们要细说一下 : 
因为它使用了堆数据结构,那么它就要去比较来保持当前堆是最大堆或者最小堆,因此它就需要通过比较来判断两个数的大小.
class Student {
    public String name;
    public int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
public class Test {
    public static void main(String[] args) {
        Queue<Student> priorityQueue = new PriorityQueue<>();
        Student stu1 = new Student("张三",12);
        Student stu2 = new Student("李四",22);
        priorityQueue.offer(stu1);
        priorityQueue.offer(stu2);
    }
}

在PriorityQueue中如果没有传比较器,在要被比较的类型实现Comparable接口的前提下,那么它就会自动通过compareTo()方法来进行比较.

对象的比较

1.  判断两个对象相不相同  -------->equals

    public static void main(String[] args) {
        Student stu1 = new Student("张三",12);
        Student stu2 = new Student("张三",12);
        System.out.println(stu1 == stu2);
    }

结果为 false 因为 "==" 比较的是两个引用数据类型的地址,一般的我们要去比较两个引用相不相同的话, 就要通过equals方法. 如果引用数据类型没有实现equals方法那么就需要我们自己去重写equals方法.

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 boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}


    public static void main(String[] args) {
        Student stu1 = new Student("张三",12);
        Student stu2 = new Student("张三",12);
        System.out.println(stu1.equals(stu2));
    }

2.  判断两个对象谁大谁小 ----------> 要么实现comparable<>接口  要么使用comparator<>构造器

对于引用类型直接去使用大于小于号比较也是不行的.

需要通过实现comparable<>接口 或者使用comparator<>构造器

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;
    }
}

public static void main(String[] args) {
        Student stu1 = new Student("张三",22);
        Student stu2 = new Student("李四",12);
        System.out.println(stu1.compareTo(stu2));

    }

 

 但这种比较方法它对类的侵入性太强了!

相比之下,comparator()比较器就会更好一点!

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;
    }
}
//用来比较年龄的比较器
class CompareAge implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    }
}
public static void main(String[] args) {
        Student stu1 = new Student("张三",22);
        Student stu2 = new Student("李四",12);
        CompareAge compareAge = new CompareAge();
        System.out.println(compareAge.compare(stu1, stu2));

    }

这就是对象的比较.

然后再回到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;
    }
}
//用来比较年龄的比较器
class CompareAge implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    }
}
public class Test {
    public static void main(String[] args) {
        CompareAge compareAge = new CompareAge();
        //传进入一个比较器
        Queue<Student> priorityQueue = new PriorityQueue<>(compareAge);
        Student stu1 = new Student("张三",22);
        Student stu2 = new Student("李四",12);
        priorityQueue.offer(stu1);
        priorityQueue.offer(stu2);
        System.out.println(priorityQueue.poll().name);
    }
}

对于PriorityQueue 它里面是一个小根堆 如何变成大根堆呢?

 

 或者也可以使用匿名内部类来实现构造器!

Queue<Student> pri = new PriorityQueue<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o2.age - o1.age;
            }
        });

希望可以帮到大家~~~~~🎉🎉🎉

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值