LeetCode 215 - Kth Largest Element in an Array - 我的博客 - ITeye技术网站
http://www.jiuzhang.com/solutions/kth-largest-element/
The quick selection algorithm is very similar to quick sort. Instead of sorting, it only needs the partition step, where it finds the pivot and compare the pivot with k.
Since it only uses the partition algorithm, the average time complexity is O(n). However, the worst case complexity is O(n^2).
In which case the quick selection has time complexity of O(n^2)?
Let's say you have a sorted array, 1 2 3 4 5, and k = 1, means we select the largest element which is 5. For each element in the array, we need to run the partition algorithm and discard the pivot only, which has the time complexity of O(n). For the n elements in the array, the overall time complexity is O(n^2). Therefore, for either quick sort and quick select algorithm, we usually need to well suffle the array for the reason each time the pivot is chosen in the half.
https://leetcode.com/discuss/36966/solution-explained
https://leetcode.com/discuss/36991/java-quick-select
http://www.cnblogs.com/yrbbest/p/4982861.html
public class Solution {
时间 O(NlogK) 空间 O(K)
思路
遍历数组时将数字加入优先队列(堆),一旦堆的大小大于k就将堆顶元素去除,确保堆的大小为k。遍历完后堆顶就是返回值。
X.
A min-heap solution:
-- Step 1: put the first k elements into the heap. Time complexity is O(k).
-- Step 2: Start from elements k + 1 to n, compare each one with heap.peek(). If greater than the peek, replace with the element. The time complexity is O((n - k) logk).
-- Step 3: After we iterate the array, the heap stores the top k elements, and the kth largest element is the minimum element of the heap, which is peek.
The space complexity is O(k).
时间 O(NlogN) 空间 O(1)
X. Random partition
http://analgorithmaday.blogspot.com/2011/05/random-partition-its-use-cases.html
Find the kth largest element in an unsorted array.
For example,
Given
Time complexity: http://stackoverflow.com/questions/5945193/average-runtime-of-quickselect
Given
[3,2,1,5,6,4]
and k = 2, return 5.Time complexity: http://stackoverflow.com/questions/5945193/average-runtime-of-quickselect
First Step: T(n) = cn + T(n/2)
c(n + n/2 + n/4 + ... + 2 + 1) = c(2n) //sum of a GPHence, it's O(n)
- public int findKthLargest(int[] nums, int k) {
- return findKthLargest(nums, 0, nums.length-1, k-1);
- }
- private int findKthLargest(int[] nums, int l, int h, int k) {
- if(k<l || k>h) return -1;
- int p = partition(nums, l, h);
- if(p == k) {
- return nums[p];
- } else if(p > k) {
- return findKthLargest(nums, l, p-1, k);
- } else {
- return findKthLargest(nums, p+1, h, k);
- }
- }
- private int partition(int[] nums, int l, int h) {
- int pivot = nums[h];
- int p = l;
- for(int i=l; i<h; i++) {
- if(nums[i]>pivot) {
- swap(nums, i, p++);
- }
- }
- swap(nums, p, h);
- return p;
- }
- private void swap(int[] nums, int i, int j) {
- int tmp = nums[i];
- nums[i] = nums[j];
- nums[j] = tmp;
- }
http://www.jiuzhang.com/solutions/kth-largest-element/
public int kthLargestElement(int k, int[] nums) { if (nums == null || nums.length == 0) { return 0; } if (k <= 0) { return 0; } return helper(nums, 0, nums.length - 1, nums.length - k + 1); } public int helper(int[] nums, int l, int r, int k) { if (l == r) { return nums[l]; } int position = partition(nums, l, r); if (position + 1 == k) { return nums[position]; } else if (position + 1 < k) { return helper(nums, position + 1, r, k); } else { return helper(nums, l, position - 1, k); } } public int partition(int[] nums, int l, int r) { // 初始化左右指针和pivot int left = l, right = r; int pivot = nums[left]; // 进行partition while (left < right) { while (left < right && nums[right] >= pivot) { right--; } nums[left] = nums[right]; while (left < right && nums[left] <= pivot) { left++; } nums[right] = nums[left]; } // 返还pivot点到数组里面 nums[left] = pivot; return left;//return where is the pivot; its position }https://segmentfault.com/a/1190000003704825
public int findKthLargest(int[] nums, int k) {
return quickSelect(nums, k - 1, 0, nums.length - 1);
}
private int quickSelect(int[] arr, int k, int left, int right){
int pivot = arr[(left + right) / 2];
int orgL = left, orgR = right;
while(left <= right){
// 从右向左找到第一个小于枢纽值的数
while(arr[left] > pivot){
left ++;
}
// 从左向右找到第一个大于枢纽值的数
while(arr[right] < pivot){
right --;
}
// 将两个数互换
if(left <= right){
swap(arr, left, right);
left ++;
right --;
}
}
// 最后退出的情况应该是右指针在左指针左边一格
// 这时如果右指针还大于等于k,说明kth在左半边
if(orgL < right && k <= right) return quickSelect(arr, k, orgL, right);
// 这时如果左指针还小于等于k,说明kth在右半边
if(left < orgR && k >= left) return quickSelect(arr, k, left, orgR);
return arr[k];
}
A quick-selection algorithm:The quick selection algorithm is very similar to quick sort. Instead of sorting, it only needs the partition step, where it finds the pivot and compare the pivot with k.
Since it only uses the partition algorithm, the average time complexity is O(n). However, the worst case complexity is O(n^2).
In which case the quick selection has time complexity of O(n^2)?
Let's say you have a sorted array, 1 2 3 4 5, and k = 1, means we select the largest element which is 5. For each element in the array, we need to run the partition algorithm and discard the pivot only, which has the time complexity of O(n). For the n elements in the array, the overall time complexity is O(n^2). Therefore, for either quick sort and quick select algorithm, we usually need to well suffle the array for the reason each time the pivot is chosen in the half.
public
int
findKthLargest(
int
[] nums,
int
k) {
if
(nums ==
null
|| nums.length ==
0
) {
return
0
;
}
return
findKthLargestHelper(nums,
0
, nums.length -
1
, nums.length - k);
}
private
int
findKthLargestHelper(
int
[] nums,
int
lo,
int
hi,
int
k) {
if
(lo >= hi) {
return
nums[lo];
}
int
pivot = partition(nums, lo, hi);
if
(pivot == k) {
return
nums[k];
}
if
(pivot > k) {
return
findKthLargestHelper(nums, lo, pivot -
1
, k);
}
else
{
return
findKthLargestHelper(nums, pivot +
1
, hi, k);
}
}
private
int
partition(
int
[] nums,
int
lo,
int
hi) {
int
pivot = nums[lo];
int
i = lo +
1
;
int
j = hi;
while
(i <= j) {
while
(i <= j && nums[i] < pivot) {
i++;
}
while
(i <= j && nums[j] >= pivot) {
j--;
}
if
(i <= j) {
swap(nums, i, j);
}
}
swap(nums, lo, j);
return
j;
}
private
void
swap(
int
[] nums,
int
i,
int
j) {
int
temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
O(N) guaranteed running time + O(1) space
So how can we improve the above solution and make it O(N) guaranteed? The answer is quite simple, we can randomize the input, so that even when the worst case input would be provided the algorithm wouldn't be affected. So all what it is needed to be done is to shuffle the input.
public int findKthLargest(int[] nums, int k) {
shuffle(nums);
k = nums.length - k;
int lo = 0;
int hi = nums.length - 1;
while (lo < hi) {
final int j = partition(nums, lo, hi);
if(j < k) {
lo = j + 1;
} else if (j > k) {
hi = j - 1;
} else {
break;
}
}
return nums[k];
}
private void shuffle(int a[]) {
final Random random = new Random();
for(int ind = 1; ind < a.length; ind++) {
final int r = random.nextInt(ind + 1);
exch(a, ind, r);
}
}
X. Iterative Versionhttps://leetcode.com/discuss/36991/java-quick-select
http://www.cnblogs.com/yrbbest/p/4982861.html
Discard half each time: n+(n/2)+(n/4)..1 = n + (n-1) = O(2n-1) = O(n), because n/2+n/4+n/8+..1=n-1.
public int findKthLargest(int[] nums, int k) {
int start = 0, end = nums.length - 1, index = nums.length - k;
while (start < end) {
int pivot = partion(nums, start, end);
if (pivot < index) start = pivot + 1;
else if (pivot > index) end = pivot - 1;
else return nums[pivot];
}
return nums[start];
}
private int partion(int[] nums, int start, int end) {
int pivot = start, temp;
while (start <= end) {
while (start <= end && nums[start] <= nums[pivot]) start++;
while (start <= end && nums[end] > nums[pivot]) end--;
if (start > end) break;
temp = nums[start];
nums[start] = nums[end];
nums[end] = temp;
}
temp = nums[end];
nums[end] = nums[pivot];
nums[pivot] = temp;
return end;
}public class Solution {
private void swap(int[] nums, int i, int j) { int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; return; } private int partition( int[] nums, int low, int high, int pivotIdx ) { int pivotVal = nums[pivotIdx]; swap(nums, pivotIdx, high); int storeIdx = low; for (int i = low; i < high; i++) { if (nums[i] > pivotVal) { swap(nums, storeIdx, i); storeIdx++; } } swap(nums, storeIdx, high); return storeIdx; } private int findKthLargest( int[] nums, int low, int high, int k ) { if (low == high) { return nums[low]; } while (true) { int offset = new java.util.Random().nextInt( high - low + 1 ); int pivotIdx = low + offset; pivotIdx = partition(nums, low, high, pivotIdx); if (pivotIdx == k) { return nums[k]; } if (pivotIdx > k) { high = pivotIdx - 1; } else { low = pivotIdx + 1; } } } public int findKthLargest(int[] nums, int k) { return findKthLargest(nums, 0, nums.length - 1, k - 1); }X. Shuffle
改进了一下quick-select, 在查找前按照塞神的建议先进行了O(n)的shuffle,速度果然快了不少, 从47ms到达了7ms
https://discuss.leetcode.com/topic/14597/solution-explainedpublic int findKthLargest(int[] nums, int k) {
shuffle(nums);
k = nums.length - k;
int lo = 0;
int hi = nums.length - 1;
while (lo < hi) {
final int j = partition(nums, lo, hi);
if(j < k) {
lo = j + 1;
} else if (j > k) {
hi = j - 1;
} else {
break;
}
}
return nums[k];
}
private void shuffle(int a[]) {
final Random random = new Random();
for(int ind = 1; ind < a.length; ind++) {
final int r = random.nextInt(ind + 1);
exch(a, ind, r);
}
}
https://segmentfault.com/a/1190000003704825时间 O(NlogK) 空间 O(K)
思路
遍历数组时将数字加入优先队列(堆),一旦堆的大小大于k就将堆顶元素去除,确保堆的大小为k。遍历完后堆顶就是返回值。
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> p = new PriorityQueue<Integer>();
for(int i = 0 ; i < nums.length; i++){
p.add(nums[i]);
if(p.size()>k) p.poll();
}
return p.poll();
}
A max-heap solution:
First of all, put all elements into the heap. Then poll the top k elements from the max heap then we get the result. The time complexity for max heap is O(n) + O(k * logn). The space complexity is O(n).
First of all, put all elements into the heap. Then poll the top k elements from the max heap then we get the result. The time complexity for max heap is O(n) + O(k * logn). The space complexity is O(n).
public
int
findKthLargest(
int
[] nums,
int
k) {
if
(nums ==
null
|| nums.length ==
0
) {
return
0
;
}
PriorityQueue<Integer> maxHeap =
new
PriorityQueue<>(nums.length, Collections.reverseOrder());
for
(
int
num : nums) {
maxHeap.offer(num);
}
int
result =
0
;
for
(
int
i =
0
; i < k; i++) {
result = maxHeap.poll();
}
return
result;
}
X.
- O(N lg K) running time + O(K) memory
Other possibility is to use a min oriented priority queue that will store the K-th largest values. The algorithm iterates over the whole input and maintains the size of priority queue.
public int findKthLargest(int[] nums, int k) {
final PriorityQueue<Integer> pq = new PriorityQueue<>();
for(int val : nums) {
pq.offer(val);
if(pq.size() > k) {
pq.poll();
}
}
return pq.peek();
}
A min-heap solution:
-- Step 1: put the first k elements into the heap. Time complexity is O(k).
-- Step 2: Start from elements k + 1 to n, compare each one with heap.peek(). If greater than the peek, replace with the element. The time complexity is O((n - k) logk).
-- Step 3: After we iterate the array, the heap stores the top k elements, and the kth largest element is the minimum element of the heap, which is peek.
The space complexity is O(k).
public
int
findKthLargest(
int
[] nums,
int
k) {
if
(nums ==
null
|| nums.length ==
0
) {
return
0
;
}
PriorityQueue<Integer> maxHeap =
new
PriorityQueue<>(k);
for
(
int
num : nums) {
if
(maxHeap.size() < k) {
maxHeap.offer(num);
}
else
{
if
(num > maxHeap.peek()) {
maxHeap.poll();
maxHeap.offer(num);
}
}
}
return
maxHeap.peek();
}
public
int
findKthLargest(
int
[] nums,
int
k) {
if
(nums ==
null
|| nums.length ==
0
) {
return
0
;
}
PriorityQueue<Integer> maxHeap =
new
PriorityQueue<>(nums.length, Collections.reverseOrder());
for
(
int
num : nums) {
maxHeap.offer(num);
}
int
result =
0
;
for
(
int
i =
0
; i < k; i++) {
result = maxHeap.poll();
}
return
result;
}
public int findKthLargest(int[] nums, int k) {
Arrays.sort(nums);
return nums[nums.length - k];
}
Note that it is the kth largest element in the sorted order, not the kth distinct element.
http://analgorithmaday.blogspot.com/2011/05/random-partition-its-use-cases.html
int random_partition(int* arr, int size)
{
srand(time(NULL));
int pivotIdx = rand() % size;
int pivot = arr[pivotIdx];
int i=-1;
int j=0;
swap(arr[pivotIdx], arr[size-1]); // move pivot element to the end
pivotIdx = size-1;
while(j < size)
{
if(arr[j] <= pivot)
{
i = i+1;
swap(arr[i], arr[j]);
}
j++;
}
swap(arr[i+1], arr[pivotIdx]);
return i+1;
}http://www.sanfoundry.com/java-program-implement-quick-sort-randomization/
public static void QuickSort(int left, int right)
{
if (right - left <= 0)
return;
else
{
Random rand = new Random();
int pivotIndex = left + rand.nextInt(right - left + 1);
swap(pivotIndex, right);
int pivot = sequence[right];
int partition = partitionIt(left, right, pivot);
QuickSort(left, partition - 1);
QuickSort(partition + 1, right);
}
}
public static int partitionIt(int left, int right, long pivot)
{
int leftPtr = left - 1;
int rightPtr = right;
while (true)
{
while (sequence[++leftPtr] < pivot)
;
while (rightPtr > 0 && sequence[--rightPtr] > pivot)
;
if (leftPtr >= rightPtr)
break;
else
swap(leftPtr, rightPtr);
}
swap(leftPtr, right);
return leftPtr;
}