题外话
上午学了一些JavaEE初阶知识,下午继续复习数据结构内容
正题
本篇内容把堆的练习题做一下
第一题
1.下列关键字序列为堆的是:( A )
A: 100,60,70,50,32,65
B: 60,70,65,50,32,100
C: 65,100,70,32,50,60
D: 70,65,100,32,50,60
E: 32,50,100,70,65,60
F: 50,100,70,65,60,32
第一题解析
堆分为大根堆小根堆,
而且堆是完全二叉树,
只需要从上到下从左到右建立一个完全二叉树再判断是否是大根堆或者是小根堆即可
A 画出图是一个大根堆
其余画出图既不是大根堆也不是小根堆
第二题
2.已知小根堆为8,15,10,21,34,16,12,删除关键字8之后需重建堆,在此过程中,关键字之间的比较次数是( C )
A: 1 B: 2 C: 3 D: 4
第二题解析
先画图,然后运用堆的删除,将8和最后一个元素12交换位置删除,再调整位置变成小根堆计数关键字比较次数即可
12先和15,10比较,和10交换位置,然后再和16比较调整为小根堆,一共比较三次
第三题
3.最小堆[0,3,2,5,7,4,6,8],在删除堆顶元素0之后,其结果是( C )
A: [3,2,5,7,4,6,8]
B: [2,3,5,7,4,6,8]
C: [2,3,4,5,7,8,6]
D: [2,3,4,5,6,7,8]
第三题解析
和第二题一样,先画图,0与8交换,然后调整为小根堆即可
结果为2,3,4,5,7,8,6
PriorityQueue
Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,
PriorityQueue是线程不安全的
PriorityBlockingQueue是线程安全的
本文主要介绍PriorityQueue。
1.使用时必须导入PriorityQueue所在的包
import java.uitl.PriorityQueue;
2.PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出 ClassCastException异常
3. 不能插入null对象,否则会抛出NullPointerException
4. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容
5.PriorityQueue默认情况下是小堆---即每次获取到的元素都是最小的元素,如果需要大堆需要用户提供比较器
用比较器创建大根堆
先说下Prriority常用构造方法,
PriorityQueue() 创建一个空的优先级队列,默认容量是11
PriorityQueue(int initialCapacity) 创建一个初始容量为initialCapacity的优先级队列,注意: initialCapacity不能小于1,否则会抛IllegalArgumentException异常
PriorityQueue(Collection c) 用一个集合来创建优先级队列
我们可以自己传一个比较器,创建大根堆,代码如下
class IntCmp implements Comparator<Integer>
{
@Override
public int compare(Integer o1, Integer o2)
{ return o2.compareTo(o1)
}
}
这样插入元素的时候都会是以大根堆的方式插入
相关练习题
第一题
设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。
第一题思路
1.创建一个大根堆,把数组前k个元素添加进去
2.因为是大根堆,用数组剩余元素与堆顶元素进行比较,堆顶元素是大根堆中最大的,如果比堆顶元素小就删除堆顶元素,插入当前数组元素,会自动调整为大根堆
3.当数组全部遍历完成,大根堆的k个元素就是最小的k个元素
第一题代码详解
public int[] smallestK(int[] arr, int k) {//创建数组ret,容量为kint[] ret=new int[k];//如果数组为空,或者k小于等于0,说明根本找不到最小的k个元素,不合法if(arr==null||k<=0){//直接返回retreturn ret;}//创建优先级队列PriorityQueue,使用匿名内部类传入比较器PriorityQueue<Integer> p=new PriorityQueue<>(new Comparator<Integer>() { //将比较器设置成满足大根堆的形式@Overridepublic int compare(Integer o1, Integer o2) {return o2.compareTo(o1);}});//添加前k个元素for (int i=0;i<k;i++){p.offer(arr[i]);}//比较k以后元素是否比堆顶元素小for (int i = k; i <arr.length ; i++) {int top=p.peek();//如果比堆顶元素小if (arr[i]<top){//删除堆顶元素p.poll();//添加当前数组元素p.offer(arr[i]);}}//最后将最小的k个元素传入数组ret中即可for (int i = 0; i < k; i++) {ret[i] = p.poll();}//返回retreturn ret;}
第二题
堆排序,从大到小排序
第二题思路
1.我们先考虑,堆排序是建立大根堆还是建立小根堆
2.大根堆我们能保证堆顶元素是整个堆中最大的,小根堆我们能保证堆顶元素是整个堆中最小的
3.我们只需要建立大根堆,然后将堆顶元素和最后一个元素交换,然后再大根堆排序,然后再让堆顶元素和堆尾没有交换过的元素一一交换,再大堆根排序即可
第二题代码详解
//向下调整(上一篇堆的博客写过)
private void siftDown(int parent,int len) {int child=parent*2+1; //child等于len不会进入循环while (child<len){if (child+1<len&&elem[child]<elem[child+1]){child=child+1;}if (elem[child]>elem[parent]){swap(parent,child);parent=child;child=parent*2+1;}else {break;}}}
//堆排序,从小到大排序 public void heapSort() { //让end保存最后一个元素下标int end=usedSize-1; //当end>0的时候就需要排序,等于零说明不需要排序了while(end>0){ //交换堆顶和没有交换过的最后一个元素值swap(0,end); //向下排序,将没交换的排序为大根堆,end下标位置不会进入排序siftDown(0,end); //让末尾位置调整到前一个即可end--;} }
小结
大家有什么意见可以在评论区说出来,我都会改进!!!