LeetCode 題解之 215. Kth Largest Element in an Array
阿新 • • 發佈:2019-01-22
215. Kth Largest Element in an Array
題目描述和難度
- 題目描述:
在未排序的陣列中找到第 k 個最大的元素。請注意,你需要找的是陣列排序後的第 k 個最大的元素,而不是第 k 個不同的元素。
示例 1:
輸入: [3,2,1,5,6,4] 和
k = 2
輸出: 5
示例 2:
輸入: [3,2,3,1,2,4,5,5,6] 和
k = 4
輸出: 4
說明:
你可以假設 k 總是有效的,且 1 ≤ k ≤ 陣列的長度。
思路分析
求解關鍵:這是一個常規問題,使用借用快速排序的 partition 的思想完成。關鍵在於理解 partition 的返回值,返回值是拉通了整個陣列的索引值,這一點是非常重要的,不要把問題想得複雜了。
- partition 這個函式返回的是整個陣列的第 k 個最小元素(從 0 開始計算)。
- 如果找第 k 個最小元素,即第 n - k 個最大元素。
例如:給定陣列為:[2,5,6,1,4,7] ,一共 6 個元素 找 k = 2,如果返回 4 ,就可以返回了。
給定陣列為:[2,5,6,1,4,7] ,一共 6 個元素 找 k = 2,如果返回 2 ,左邊的區間就可以不用看了。
參考解答
參考解答1:使用快速排序的 partition 的思想完成。
public class Solution2 {
private static Random random = new Random(System.currentTimeMillis());
public int findKthLargest(int[] nums, int k) {
int len = nums.length;
if (len == 0 || k > len) {
throw new IllegalArgumentException("引數錯誤");
}
// 轉換一下,這樣比較好操作
// 第 k 大元素的索引是 len - k
int target = len - k;
int l = 0;
int r = len - 1;
while (true) {
int i = partition(nums, l, r);
if (i < target) {
l = i + 1;
} else if (i > target) {
r = i - 1;
} else {
return nums[i];
}
}
}
// 在區間 [left, right] 這個區間執行 partition 操作
private int partition(int[] nums, int left, int right) {
// 在區間隨機選擇一個元素作為標定點(以下這兩行程式碼非必需)
// 這一步優化非必需
if (right > left) {
int randomIndex = left + 1 + random.nextInt(right - left);
swap(nums, left, randomIndex);
}
int pivot = nums[left];
int l = left;
for (int i = left + 1; i <= right; i++) {
if (nums[i] < pivot) {
l++;
swap(nums, l, i);
}
}
swap(nums, left, l);
return l;
}
private void swap(int[] nums, int index1, int index2) {
if (index1 == index2) {
return;
}
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
}
參考解答2:使用最小堆,這個寫法是我最開始的寫法,有點死板。
public class Solution3 {
public int findKthLargest(int[] nums, int k) {
int len = nums.length;
if (len == 0 || k > len) {
throw new IllegalArgumentException("引數錯誤");
}
// 使用一個含有 k 個元素的最小堆
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(k, (a, b) -> a - b);
for (int i = 0; i < k; i++) {
priorityQueue.add(nums[i]);
}
for (int i = k; i < len; i++) {
// 看一眼
Integer topEle = priorityQueue.peek();
// 只要當前遍歷的元素比堆頂元素大,堆頂出棧,遍歷的元素進去
if (nums[i] > topEle) {
priorityQueue.poll();
priorityQueue.add(nums[i]);
}
}
return priorityQueue.peek();
}
}
參考解答3:最小堆更簡單的寫法。
public class Solution3 {
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(k + 1, (a, b) -> (a - b));
for (int num : nums) {
priorityQueue.add(num);
if(priorityQueue.size()==k+1){
priorityQueue.poll();
}
}
return priorityQueue.peek();
}
}