优先级队列(堆),比较器(Comparator,Comparable)

优先级队列(PriorityQueue),比较普通的队列可以提供了一个基本操作,返回最高优先级元素。PriorityQueue的底层使用了堆这种数据结构,而堆则是在完全二叉树的基础上进行调整。

堆的特点:

堆的父节点一定是大于(大堆)或小于(小堆)子节点。

堆的逻辑结构是二叉数,但存储结构是一维数组。

堆一定是完全二叉树。

普通二叉树不使用一维数组存储的原因是二叉树的节点可能为空,要还原二叉树就要将空节点null也存入数组,使空间利用率较低。

堆的创建

堆的创建采用的是向下调整,以小根堆为例,向下调整是先在头节点不满足小根堆,而左右子树都已经满足小根堆,设左子树为child,父节点为parent,通过先比较child和child+1,找出最大的数,与parent比较,如果小于则将两者交换,再让parent = child,child = chlid+1,直到child>size或child和parent已经满足小根堆了,结束循环。

如果知道子节点child,求父节点parent = (child-1)/2。

知道父节点parent,左chlid = 2*parent+1,右child=2*parent+2。

向下调整:

 public  void shiftdown1 (int index,int[] arr){
        int parent = index;
        int child = 2*parent+1;
        while(child<arr.length){
            while(child<arr.length&&arr[child]<arr[child+1]){
                child+=1;
            }
            if(arr[parent]>arr[child]){
                sweap(arr[parent],arr[child]);
                parent = child;
                child = 2*parent+1;
            }else{
                break;
            }
        }
    }
    public void sweap(int a,int b){
        int tem = a;
        a = b;
        b = tem;
    }

如果给一组不规则的数组,建堆则要从最后一个非叶子节点开始,依次向下调整。

public void creatheap(int [] arr){
        for (int i = (arr.length-1-1)/2; i >=0 ; i--) {
            shiftdown1(i,arr);
        }
    }
    public  void shiftdown1 (int index,int[] arr){
        int parent = index;
        int child = 2*parent+1;
        while(child<arr.length){
            while(child<arr.length&&arr[child]<arr[child+1]){
                child+=1;
            }
            if(arr[parent]>arr[child]){
                sweap(arr[parent],arr[child]);
                parent = child;
                child = 2*parent+1;
            }else{
                break;
            }
        }
    }
    public void sweap(int a,int b){
        int tem = a;
        a = b;
        b = tem;
    }

建堆的时间复杂度是O(N)

堆的删除和添加

堆的删除是先将堆顶元素和堆的最后一个叶子节点交换,然后对堆顶元素进行一次向下调整,将元素个数减一。

public void pollHeap(int arr[]) {
        int tem = arr[0];
        arr[0] = arr[usedSize-1];
        usedSize--;
        shiftDown(0,arr);
    }
 private void shiftDown(int index,int []arr) {
        int parent = index;
        int child = 2*parent +1;
        while(child<usedSize){
            if(child+1<usedSize&&arr[child]>=arr[child+1]){
                child++;
            }
            if(arr[parent]>arr[child]){
                sweap(arr[parent],arr[child]);
            }
            parent = child;
            child = 2*parent+1;
        }
    }
    public void sweap(int a,int b){
        int tem = a;
        a = b;
        b = tem;
    }

堆添加元素,要使用向上调整,新添的元素直接放到堆尾,找到它的父节点,比较大小,如果大于父就break;小于就交换值,并且让child = parent+1,parent = (child-1)/2。

 public void push(int val,int [] arr) {
         arr[usedSize]= val;
        usedSize++;
         shiftUp(usedSize-1,arr);

    }
    //在添加新元素的时候,向上调整
    private void shiftUp(int index,int[]arr) {
        int child = index;
        int parent = (child-1)/2;
        while(child>0){
            if(arr[parent]>arr[child]){
             sweap(arr[parent],arr[child]);

             child= parent ;
             parent = (child-1)/2;
            }else{
                break;
            }
        }

    }

堆模拟实现优先级队列。

public class priorityqueue {
    int[] arr = new int  [100];
    int size = 0;
    public void offer(int k){
        arr[size] = k;
        size++;
        shiftUp(size-1);

    }
    public int poll(){
          int s = arr[0];
          sweap(arr[0],arr[size]);
          size--;
          shiftDown(0);
          return s;
    }
   
    }
PritorityQueue类特点

PritorityQueue(优先级队列)是JAVA集合框架中提供的一种优先级队列,PritorityQueuez中放置的元素必须要能够比较大小,不然会抛出ClassCastException异常。

不能放入null对象,否则会抛出NullPointerException异常。

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

PritorityQueue默认是小堆(每次获取元素为最小),要转大堆则需要比较器。

import java.util.Comparator;
import java.util.PriorityQueue;

//设置比较器Comparater
        class Incom implements Comparator<Integer>{
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
}
//比较器Comparable
        class Incomw implements Comparable<Integer>{
    @Override
    public int compareTo(Integer o) {
        return 0;
    }

 PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Incom());
}
top-k问题

第一种做法:直接将所有元素入PritorityQueue,返回前K个元素。

public class Heap {
    public int[] smallestK1(int[] arr, int k) {
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Incom());
        for (int i = 0; i < arr.length; i++) {
            priorityQueue.add(arr[i]);
        }
        int [] n = new int[k];
        for (int i = 0; i < k; i++) {
            n[i] = priorityQueue.poll();
        }
        return n;
    }

第二种做法:先将前K个元素建堆(大堆),取堆顶元素,与第k个元素开始依次比较,最后返回前K个元素。

public int[] smallestK(int[] arr, int k){
        int[] ret =new int[k];
        if(k<0||k>=arr.length){
            return ret;
        }
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
        for (int i = 0; i < k; i++) {
            priorityQueue.offer(arr[i]);
        }
        for (int i = k; i < arr.length; i++) {
            int cur = priorityQueue.poll();
            if(cur>arr[k]){
                priorityQueue.poll();
                priorityQueue.add(arr[i]);
            }
        }
        return ret;
    }

这里的建堆是入PritorityQueue。

比较器(Comparator,Comparable)

JAVA中涉及到两个对象比较,要用Comparator和Comparable接口。

类要实现comparator需要重写里面的compare方法。

 class Incom implements Comparator<Integer>{
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
}
import java.util.Comparator;
class Card {
public int rank; // 数值
public String suit; // 花⾊
public Card(int rank, String suit) {
this.rank = rank;
this.suit = suit;
}
}
class CardComparator implements Comparator<Card> {
// 根据数值⽐较,不管花⾊
// 这⾥我们认为 null 是最⼩的
@Override
public int compare(Card o1, Card o2) {
if (o1 == o2) {
return 0;
}
if (o1 == null) {
return -1;
}
if (o2 == null) {
return 1;
}
return o1.rank - o2.rank;
}
public static void main(String[] args){
Card p = new Card(1, "♠");
Card q = new Card(2, "♠");
Card o = new Card(1, "♠");
// 定义⽐较器对象
CardComparator cmptor = new CardComparator();
// 使⽤⽐较器对象进⾏⽐较
System.out.println(cmptor.compare(p, o)); // == 0,表⽰牌
相等
System.out.println(cmptor.compare(p, q)); // < 0,表⽰ p ⽐较⼩
System.out.println(cmptor.compare(q, p)); // > 0,表⽰ q ⽐较⼤
}
}

要实现comparable要实现compareTo方法。

 class Incomw implements Comparable<Integer>{
    @Override
    public int compareTo(Integer o) {
        return 0;
    }
}
public class Card implements Comparable<Card> {
public int rank; // 数值
public String suit; // 花⾊
public Card(int rank, String suit) {
this.rank = rank;
this.suit = suit;
}
// 根据数值⽐较,不管花⾊
// 这⾥我们认为 null 是最⼩的
@Override
public int compareTo(Card o) {
if (o == null) {
return 1;
}
return rank - o.rank;
}
public static void main(String[] args){
Card p = new Card(1, "♠");
Card q = new Card(2, "♠");
Card o = new Card(1, "♠");
System.out.println(p.compareTo(o)); // == 0,表⽰牌相等
System.out.println(p.compareTo(q)); // < 0,表⽰ p ⽐较⼩
System.out.println(q.compareTo(p)); // > 0,表⽰ q ⽐较⼤
}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值