領釦網演算法學習筆記 - 215
阿新 • • 發佈:2018-12-23
領釦網演算法學習筆記
本系列的演算法題目來自領釦網
陣列類演算法第六天
題目:陣列中的第K個最大元素
在未排序的陣列中找到第 k 個最大的元素。請注意,你需要找的是陣列排序後的第 k 個最大的元素,而不是第 k 個不同的元素。
示例:
輸入: [3,2,1,5,6,4] 和 k = 2
輸出: 5
輸入: [3,2,3,1,2,4,5,5,6] 和 k = 4
輸出: 4
說明:
- 你可以假設 k 總是有效的,且 1 ≤ k ≤ 陣列的長度。
解題過程:
思路一:
看到這題的第一個思路是:採用快速排序,排序後在取第K個元素。
程式碼如下:
class Solution {
public int findKthLargest(int[] nums, int k) {
// 直接用快速排序,排完再取第 k 個值
QuestSort(nums,0,nums.length-1);
return nums[k-1];
}
public static void QuestSort(int[] nums, int _left, int _right) {
int left = _left;
int right = _right;
if(left >= right){
return;
}
int temp = nums[left];
while(left < right){
if(left < right && nums[right] <= temp){
right--;
}
nums[left] = nums[right];
if(left < right && nums[left] >= temp){
left++;
}
nums[right] = nums[left];
}
nums[right] = temp;
QuestSort(nums,_left,left-1);
QuestSort(nums,right+1,_right);
}
}
// 用時153ms
後續思考:
感覺這個效率好低,然後寫完之後看了一下最佳答案,發現其實不用排序,只要採用快排的思路即可。
思路二:
快排可以使元素分成兩部分,在這裡前面部分是大於指定資料的,後面部分是小於指定資料的,這樣的話,只需要使得前面大於它的數有K個就好了。
程式碼如下:
class Solution {
public int findKthLargest(int[] nums, int k) {
// 套用快速排序,使得前面有k個元素比元資料大
int left = 0;
int right = nums.length - 1;
int loopValue = QuestSortNum(nums,left,right);
k -=1;
while(true){
if(k == loopValue){
return nums[loopValue];
}else if(k < loopValue){
right = loopValue - 1;
}else{
left = loopValue + 1;
}
loopValue = QuestSortNum(nums,left,right);
}
}
public static int QuestSortNum(int[] nums, int left, int right) {
if(left >= right){
return left;
}
int temp = nums[left];
while(left < right){
if(left < right && nums[right] <= temp){
right--;
}
nums[left] = nums[right];
if(left < right && nums[left] >= temp){
left++;
}
nums[right] = nums[left];
}
nums[right] = temp;
return left;
}
}
// 第一次用時54ms,第二次用時63ms,神奇,形同程式碼,速度不一樣......
後續思考:
之前一直想著,利用快速排序的方法,先快排一遍,判斷當前排序後的參考值的位置,然後在使其中一邊等於參考值的位置,導致這個死活的迴圈不出來,後面想通了,如果迴圈到後面,兩個數相等,這個左右的位置就不會再變動,就會一直死迴圈在這個,這樣會有問題,然後就拋棄當前參考值的位置,反正判斷都不是那個位置。
領釦上面該題其他高質量範例:
class Solution {
public int findKthLargest(int[] nums, int k) {
if(nums.length == 1) return nums[0];
int val = qsort(nums, 0, nums.length-1);
int left = 0;
int right = nums.length-1;
k -= 1;//第k大,說明有k-1個元素比他大
if(val == k) return nums[val];
while(left < right){
if(val >= k){
right = val;
val = qsort(nums, left, right);
}
else{
left = val+1;
val = qsort(nums, left, right);
}
}
return nums[k];
}
int qsort(int[] nums,int left,int right){
int i = left;
int j = right;
int center = nums[(i+j) / 2];
do{
while(nums[i] > center) i++;
while(nums[j] < center) j--;
if(i <= j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
i++; j--;
}
}while(i <= j);
if(j < left) return left;//防止j越界,陷入無限迴圈
return j;
}
}
// 用時2ms
後續思考:
第一眼看到這個程式時,總覺得判斷後直接 i++ 或者 j-- ,在某種特定的情況下會產生資料越界,測試後發現,無論什麼情況,都會有一種情況出現,就是數的本身不會小於或者大於它自己,所以不會再這裡不會造成資料越界,同時又需要返回值,不管是left,還是right,最後遍歷都有可能到資料的最右端或者最左端,所以需要判斷返回值是否越界。
至於為什麼比我上面那個速度要快,還沒想清楚在哪,後續在思考思考,想通了再補上、。