前言
给定一个数组以及一个数字n,输出数组中第n大的数,例如数组
array = {1,4,6,5,4,7,8,9,10}
n = 5
那么输出的数字是 6。
本文会提供几种不同的写法,从效率上我们一起来看看
正文
第一种
首先我们想到对数组进行排序,利用Arrays.sort()方法,然后从后往前数出第n大数,代码如下
/**
* 给定一个数组和n,输出第n大数
* 不存在则返回0
*
* @param arr 数组
* @return 第n大的数
*/
public static int findNMaxNumberVersion1(int[] arr, int n) {
if (arr == null || arr.length == 0 || n > arr.length){
return 0;
}
Arrays.sort(arr);
return arr[arr.length - n];
}
那么我们来思考,这种方法是最优的吗?
肯定不是最优的,我们只是想找出第n大数,上述解法将所有数都排序了一遍,必然多出来很多无效的操作,但是这种写法是最简洁的。
第二种
/**
* 给定一个数组和n,输出第n大数
* 不存在则返回0
*
* @param arr 数组
* @return 第n大的数
*/
public static int findNMaxNumberVersion2(int[] arr, int n) {
if (arr == null || arr.length == 0 || n > arr.length){
return 0;
}
int count = 0;
for (int i = 0;i < arr.length;i++){
int temp = arr[0];
int m = 0;
int j = 1;
while (j < arr.length){
if (arr[j] > temp){
temp = arr[j];
m = j;
}
j++;
}
arr[m] = 0;
count++;
if (count == n){
return temp;
}
}
return 0;
}
这种写法比第一种效率高出不少,他不会将所有的数都排序一遍,而只是将最大的n - 1个数重置为0,返回第n个数。对剩下的数,不会进行重复无效的计算。那么我们来思考,这种解法有没有优化的空间呢?显然是可以的。我们发现上述的代码,将最大的数都重置为0,但是下次遍历的时候,还会遍历到这些重置的数据,假如整个数组有1万个元素,我们要找到第5千个大数,那么遍历数组的大小不会随着次数而递减,意思是每次遍历数组的大小是相同的,每次都要遍历1万次,这部分的遍历工作也是无效的,既然分析出弊端,我们接着往下看。
第三种
/**
* 给定一个数组和n,输出第n大数
* 不存在则返回0
*
* @param arr 数组
* @return 第n大的数
*/
public static int findNMaxNumberVersion3(int[] arr, int n) {
if (arr == null || arr.length == 0 || n > arr.length){
return 0;
}
for (int i = 0; i < n ;i++){
int j = i + 1;
while (j < arr.length){
if (arr[j] > arr[i]){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
j++;
}
}
return arr[n -1];
}
这些写法只会遍历前n个数,而且解决了第二种方法每次遍历的数组大小相同的弊端,第0 个数我遍历arr.length次,第1个数,遍历的次数 arr.length -1 依次类推,如果有1万个元素,那么提升的效率就很明显了。
延伸
这道算法题还可以延伸成找到前n大个元素,就不是第n大元素,是前n大个元素,其实解法是相同的,只是会保存每次遍历的结果即可。