Question:
查找数组arr中第k大的奇数,如果不存在则返回0. (arr[i] > 0 (i>=0))
计算出时间复杂度(注意代码注释,不要使⽤库函数或脚本中已经实现好的排序算法和⼯具, 需要⾃⼰实现数据结构和所需要的算法)
当我阅读完毕题目时,第一个思路就是先进行排序 然后进行遍历查找第k的奇数
代码如下:
public static void BubbleSort(int []arr){
int length=arr.length;
for (int i = 0; i <length-1 ; i++) {
for (int j = 0; j <length-1-i ; j++) {
if(arr[j]>arr[j+1]){
int temp= arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
public static int findKth(int[] arr, int k){
//冒泡排序(从小到大)
BubbleSort(arr);
//寻找第k大的奇数(要倒序遍历)
int size =1;
for (int i = arr.length-1; i >=0 ; i--) {
if (arr[i]%2==1){ //奇数
if (size==k){ //第k大
return arr[i];
}
size++;
}
}
return 0;
}
由于题目要求不能使用库函数 ,so要自己编写排序算法,由此可以推断改算法的时间复杂度为O(N^2).
显然,这个时间复杂度有点大,能不能进一步优化一下呢?
经过查阅文档和java源码
想到了队列,我们知道队列是先进先出的,也不符合我们这个题的要求设计啊,但是队列有一个实现类 PriorityQueue,它可以根据优先级 优先处理优先级高的对象。
PriorityQueue是基于优先堆的一个无界队列,这个优先队列中的元素可以默认自然排序或者通过提供的Comparator(比较器)在队列实例化的时排序。
优先队列不允许空值,而且不支持non-comparable(不可比较)的对象,比如用户自定义的类。优先队列要求使用Java Comparable和Comparator接口给对象排序,并且在排序时会按照优先级处理其中的元素。
优先队列的头是基于自然排序或者Comparator排序的最小元素。如果有多个对象拥有同样的排序,那么就可能随机地取其中任意一个。当我们获取队列时,返回队列的头对象。
优先队列的大小是不受限制的,但在创建时可以指定初始大小。当我们向优先队列增加元素的时候,队列大小会自动增加!
PriorityQueue通过二叉小顶堆实现,可以用一棵完全二叉树表示(任意一个非叶子节点的权值,都不大于其左右子节点的权值),也就意味着可以通过数组来作为PriorityQueue的底层实现。
由上图可以发现规律:
leftNo = parentNo*2+1
rightNo = parentNo*2+2
parentNo = (nodeNo-1)/2
通过上述三个公式,可以轻易计算出某个节点的父节点以及子节点的下标。这也就是为什么可以直接用数组来存储堆的原因。
有限队列具体的源代码后续文章会进行专题分析。
!!!优先队列的头是基于自然排序或者Comparator排序的最小元素。而且队列的大小会自动增加!!!
我们可以创建一容量大小为k的自然排序的priority队列来存放奇数,插入元素时。当队列满了时,poll()出最小的元素,再进行插入,如果未满,则继续插入,最后当队列满时我们peek()或者poll()的元素就是队列的头元素,也就是 我们想要的第k大的奇数,如果队列不满,即不存在第k大的 奇数。
代码如下:
public static int findKth2(int[] arr, int k) {
PriorityQueue<Integer> q = new PriorityQueue<Integer>(k);
for (int i = 0; i <arr.length ; i++) {
if (arr[i]%2==1){ //奇数
q.add(arr[i]);
}
if (q.size()>k){ //如果队列容量超过k了 就弹出最小元素
q.poll();
}
}
//判断队列长度是否为空或者根本不存在第k大的奇数
if (q.size()!=k||q.size()==0){
return 0;
}else {
return q.peek();
}
}
此时程序的时间复杂度为O(n)。
参考文章:
https://blog.csdn.net/huangwei18351/article/details/82183052