第一章 引论
1.1 本书讨论的内容
确定N个数中第k大的数
设有一组N个数而要确定其中第k个最大者,我们称之为选择问题。书中介绍了两种解法:
一是将这N个数读进一个数组中,再通过某种简单的算法,比如冒泡排序法,以递减顺序将数组排序,然后返回位置k上的元素;
二是可以把前k个元素读入数组并(以递减的顺序)对其排序。接着,将剩下的元素再逐个读入。当新元素被读到时,如果它小于数组中的第k个元素则忽略之,否则就将其放到数组中正确的位置上,同时将数组中的一个元素挤出数组。当算法终止时,位于第k个位置上的元素作为答案返回。
第一种解法很容易就写出来了,但是第二种解法着实使我耗费了很久的时间,而且搞得我晕头转向的。
第二种解法我按照错误的思路写了很久,我认为取出数组的前k个数后,用剩余的N-k个数和k数组(初始数组的前k个数组成的数组)里的数从大到小、逐个地进行比较,只要在k数组里找到比该数小的数,就即刻替换更新k数组。这种思路的错误在于,被替换的数,本身可能是一个很大的数,实际在N数组(初始数组)中是排的上前k位的,却仅仅因为比剩余数里的一个数小,就被踢出了排位,而原先在k排位中比它小的数却侥幸留存了下来,这就是错误的根源。如:原数组[1, 2, 3, 4, 5, 6, 7, 8, 9],确定第4大的数,先取出前4个元素并从大到小排序组成k数组[4, 3, 2, 1],剩余的数构成数组[5, 6, 7, 8, 9]。读取剩余数组中的数更新k数组,更新的过程为:
最后k数组为[9, 3, 2, 1],第4大的数为1.错误!
正确的做法应该是在得到剩余数组里的数在k数组的正确排序后,把k数组里数往后移动。正确的更新过程应该是:
最后k数组应为[9, 8, 7, 6],第4大的数为6.
结论:不能直接替换k数组里的数,要先向右复制更新k数组后再替换。
/* 问题:一个有N个数的数组,确定其中第K个最大值
* 1。生成一个整数数组,存放随机数
* 2.写两个函数,分别为冒泡排序和读入前K个数
*/
public class SelectionProblem {
public static void main(String[] args) {
int n = 100000;
int[] arr = new int[n];
for(int i = 0; i < n; i++){
int number = (int)(Math.random()*100);
arr[i] = number;
}
//复制数组,用于另一种方法
int[] arr2 = new int[n];
for(int i = 0; i < n; i++){
arr2[i] = arr[i];
}
long start;
long end;
start = new Date().getTime();
System.out.println(bubble(arr, 5000));
end = new Date().getTime();
System.out.printf("冒泡法花了%d秒\n", (end - start)/1000);
System.out.println(firstK(arr2, 5000));
start = new Date().getTime();
System.out.printf("读取前k个数法花了%d秒", (start - end)/1000);
}
public static int bubble(int[] arr, int k){
for(int i = 0; i < arr.length; i++){
for(int j = 0; j < arr.length - i -1; j++){
if(arr[j] < arr[j + 1]){
int temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
}
}
}
return arr[k - 1];
}
public static int firstK(int[] arr, int k){
int[] collection = new int[k];
int[] rest = new int[arr.length-k];
System.arraycopy(<