Skip to content

Commit

Permalink
[zh-hans en] update kth_largest_element.md: add iterative implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
JuneYuan committed Aug 28, 2018
1 parent 76b4447 commit 3b2f146
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 3 deletions.
2 changes: 1 addition & 1 deletion en/basics_sorting/quick_sort.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ The output:
Having analyzed three implementations of quick sort, we may grasp one key difference between *quick sort* and *merge sort* :

1. Merge sort divides the original array into two sub-arrays, and merges the sorted sub-arrays to form a totally ordered one. In this case, recursion happens before processing(merging) the whole array.
2. Quick sort divides the original array into two sub-arrays, and then sort them. The whole array is sorted as soon as the sub-arrays get sorted. In this case, recursion happens after processing(partition) the whole array.
2. Quick sort divides the original array into two sub-arrays, and then sort them. The whole array is ordered as soon as the sub-arrays get sorted. In this case, recursion happens after processing(partition) the whole array.

Robert Sedgewick's presentation on [quick sort](http://algs4.cs.princeton.edu/23quicksort/) is strongly recommended.

Expand Down
58 changes: 57 additions & 1 deletion en/integer_array/kth_largest_element.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Trail and error: Comparison-based sorting algorithms don't work because they inc

By quick sorting, we get the final index of a pivot. And by comparing that index with `K`, we decide which side (the greater or the smaller) of the pivot to recurse on.

### Java
### Java - Recursion

```java
public class Solution {
Expand Down Expand Up @@ -79,6 +79,62 @@ b. final index of pivot equals K.

Since 'Kth **largest**' is wanted, numbers greater than pivot are placed to the left and numbers smaller to the right, which is a little different with typical quick sort code.

### Java - Iteration

Recursive code is easier to read than to write, and it demands some experience and skill. Here is an iterative implementation.

```
class Solution {
public int findKthLargest(int[] A, int k) {
if (A == null || A.length == 0 || k < 0 || k > A.length) {
return -1;
}
int lo = 0, hi = A.length - 1;
while (lo <= hi) {
int idx = partition(A, lo, hi);
if (idx == k - 1) {
return A[idx];
} else if (idx < k - 1) {
lo = idx + 1;
} else {
hi = idx - 1;
}
}
return -1;
}
private int partition(int[] A, int lo, int hi) {
int pivot = A[lo], i = lo + 1, j = hi;
while (i <= j) {
while (i <= j && A[i] > pivot) {
i++;
}
while (i <= j && A[j] <= pivot) {
j--;
}
if (i < j) {
swap(A, i, j);
}
}
swap(A, lo, j);
return j;
}
private void swap(int[] A, int i, int j) {
int tmp = A[i];
A[i] = A[j];
A[j] = tmp;
}
}
```

### Src Code Analysis

The `while` loop in `findKthLargest` is very much like that in `binary search`. And `partition` method is just the same as quick sort partition.

### Complexity

Time Complexity. Worse case (when the array is sorted): ***n + n - 1 + ... + 1 = O(n^2)*** . Amortized complexity: ***n + n/2 + n/4 + ... + 1 = O(2n)=O(n)*** .
Expand Down
58 changes: 57 additions & 1 deletion zh-hans/integer_array/kth_largest_element.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ adding this problem and creating all test cases.

找第 K 大数,基于比较的排序的方法时间复杂度为 $$O(n)$$, 数组元素无区间限定,故无法使用线性排序。由于只是需要找第 K 大数,这种类型的题通常需要使用快排的思想解决。[Quick Sort](http://algorithm.yuanbin.me/zh-hans/basics_sorting/quick_sort.html) 总结了一些经典模板。这里比较基准值最后的位置的索引值和 K 的大小关系即可递归求解。

### Java
### Java - 递归求解

```java
public class Solution {
Expand Down Expand Up @@ -76,6 +76,62 @@ public class Solution {
递归的终止条件有两个,一个是左边界的值等于右边界(实际中其实不会有 l > u), 另一个则是索引值 `m + 1 == k`.
这里找的是第 K 大数,故为降序排列,for 循环中使用`nums[i] > nums[left]` 而不是小于号。

### Java - 迭代求解

递归代码看上去顺理成章,实际上构造递归方法的参数、返回值是需要经验技巧的,自己写起来就会发现机关重重,一次性做到 bug-free 并不容易。下面是一个迭代版的实现。

```
class Solution {
public int findKthLargest(int[] A, int k) {
if (A == null || A.length == 0 || k < 0 || k > A.length) {
return -1;
}
int lo = 0, hi = A.length - 1;
while (lo <= hi) {
int idx = partition(A, lo, hi);
if (idx == k - 1) {
return A[idx];
} else if (idx < k - 1) {
lo = idx + 1;
} else {
hi = idx - 1;
}
}
return -1;
}
private int partition(int[] A, int lo, int hi) {
int pivot = A[lo], i = lo + 1, j = hi;
while (i <= j) {
while (i <= j && A[i] > pivot) {
i++;
}
while (i <= j && A[j] <= pivot) {
j--;
}
if (i < j) {
swap(A, i, j);
}
}
swap(A, lo, j);
return j;
}
private void swap(int[] A, int i, int j) {
int tmp = A[i];
A[i] = A[j];
A[j] = tmp;
}
}
```

### 源码分析

`findKthLargest` 里的 `while` 循环体有种二分搜索的既视感,`partition` 就是标典型的快排分区写法。

### 复杂度分析

最坏情况下需要遍历 $$ n + n - 1 + ... + 1 = O(n^2)$$, 平均情况下 $$n + n/2 + n/4 + ... + 1 = O(2n)=O(n)$$. 故平均情况时间复杂度为 $$O(n)$$. 交换数组的值时使用了额外空间,空间复杂度 $$O(1)$$.

0 comments on commit 3b2f146

Please sign in to comment.