leetcode题目
leetcode215题 : 数组中的第K个最大元素
题目:给定整数数组 nums 和整数 k,请返回数组中第 k 大的元素。
解析:可以调API,但调API方法就会导致有多余的操作。我们这里可以简化api方法,并不需要把整个数组完全排序后再返回对应位置的值。
正确的代码如下:
public int findKthLargest(int[] nums, int k) {
//快排——每次确定一个元素的位置
k = nums.length-k;
int l = 0 , r = nums.length-1;
int index = 0;
while(l < r){
index = quicksort(nums,l,r);
if(index == k){
break;
}else if(index > k){
r = index-1;
}else{
l = index+1;
}
}
return nums[k];
}
private int quicksort(int[] nums ,int l , int r){
int i = l , j = r;
int index = new Random().nextInt(r-l+1)+l;
swap(nums,l,index);
int temp = nums[l];
while(i < j){
while(i < j && nums[j] > temp){
j--;
}
while(i < j && nums[i] <= temp){
i++;
}
if(i < j){
swap(nums,i,j);
}
}
swap(nums,l,i);
return i;
}
private void swap(int[] nums , int i , int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
出现问题
由于该题是我第二次做,很快就得出思路并写出代码。但校对了很久,仍无法得出正确答案。最终在不断地尝试后发现:如果在一次快速排序的过程中,先从左边找到第一个大于基准值的值的位置,再从右往左找到第一个小于或等于基准值的位置是无法得出正确答案的。
快排的顺序问题
为什么一定要先从右边开始找,再到左边呢?
原因:因为我们的基准值是放在数组的最左边,等找到 i 与 j 相遇的位置后,是要把该位置上的值与基准值进行交换,而我们又知道当基准值确定位置后,位于基准值左边的值一定是小于或等于基准值,位于基准值右边的值一定是大于基准值的。所以,我们找的位置上的元素一定是要小于或等于基准值,才成立。也就是说,我们要先找到比基准值小的数,再去找比基准值大的数,这样才可以保证当 i 与 j 相遇的时候,永远是在比基准值小的位置上。举个反例: 3 2 1 5 6 4
假设第一次我选取的基准值是 4 。 与数组第一个位置的值交换,得:
4 2 1 5 6 3
设 i = 0 , j = 5 , 先从左往右, i 找到第一个比4大的值, i = 3 , j 找到第一个比4小的值 , j = 5 ,两个位置的值进行交换,得到的数组如下:
4 2 1 3 6 5
再从左往右,i 找到了 6 ,i = 4 , 此时 j 再去找,由于要满足 i > j 所以j = 4 .跳出循环,进行队首的值与i 或 j 位置的值交换,得到数组为:
6 2 1 3 4 5
6 不应该在 4 的左边, 所以结果错误。
得出结论: 当我们把基准值放在一边时,我们应该从另外一边先开始找值。
等号(=)问题
结论: 基准值在哪边,等号就加在从那边到另外一边的找值过程上。
原因: 因为,我们在寻找基准值的位置时,是先从另外一边开始遍历的,如果将另外一边向基准值一边遍历的过程中加上=号,这样会出现一个问题,当随机的基准值取到整个数组的最值时,就会出现数组越界的问题,所以,不可以。
例子 :3 2 1 5 6 4
假设我第一次选取的基准值是6 , 如果等号是加在另一边往基准值这边的找值过程上,就是while( i < j && nums[j] >= temp) , 6是这个数组的最大值,此时 j 要找到第一个比 6 小的数,一直找,直到 j = -1 。发生越界或者产生不如人意的答案。