Skip to content

Commit 43fcc7b

Browse files
authored
Problem/heap (#23)
* Add 215. Kth Largest Element in an Array solution * Add 215. Kth Largest Element in an Array self-implemented heap solution * Add 347. Top K Frequent Elements solution * Add 347. Top K Frequent Elements bucket sort solution * Update 15. 3Sum notes * Update 347. Top K Frequent Elements solution * Add 692. Top K Frequent Words min heap solution * Add 378. Kth Smallest Element in a Sorted Matrix heap solution * Add 378. Kth Smallest Element in a Sorted Matrix binary search solution * Add 973. K Closest Points to Origin heap solution * Add 767. Reorganize String solution * Add quickselect algorithm * Add 451. Sort Characters By Frequency solution * Add 373. Find K Pairs with Smallest Sums solution * Add 295. Find Median from Data Stream solution * Add full implementation of heap * Add array problems summary notes * Update 295. Find Median from Data Stream solution and note * Update 189. Rotate Array solution * Organize the problems and solutions list * Update heap note
1 parent a2e5bed commit 43fcc7b

22 files changed

+1151
-88
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@ The notes here are to contain all basic/fundamental/popular data strcutures and
1414
- [Linked List](./topics/linked-list.md)
1515
- [Stack & Queue](./topics/stack-queue.md)
1616
- [Tree](./topics/tree.md)
17+
- [Heap & Priority Queue](./topics/heap.md)
1718
- [Graph](./topics/graph.md)
1819
- [Shortest Path](./topics/shortest-path.md)
1920
- [Recursion](./topics/recursion.md)
2021
- [Dynamic Programming](./topics/dynamic-programming.md)
2122
- [Greedy](./topics/greedy.md)
2223
- [Sorting](./topics/sorting.md)
23-
- [Heap & Priority Queue](./topics/heap.md)
2424
- [Hash Table](./topics/hash-table.md)
2525
- [Searching](./topics/searching.md)
2626
- (TODO) [Other](./topics/other.md)
27-
- 👉 [Problems & Solutions](./topics/problems-solutions.md)
27+
- 👉 [Problems & Solutions](./problems/problems-solutions.md)
2828

2929
## Complexity
3030
![Sequence Complexity](./media/complexity-sequence.png)

leetcode/15.3sum.md

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
## [15. 3Sum](https://leetcode.com/problems/3sum/)
22

33
### Two Pointers
4+
> The reason why to sort first, the way to avoid duplicate and why two pointers approach helps, you can take a [nice explanation](https://leetcode.cn/problems/3sum/solution/san-shu-zhi-he-by-leetcode-solution/)!!
5+
46
The array `[-1, 0, 1, 2, -1, -4]` can be sorted at first, it will become `[-4, -1, -1, 0, 1, 2]`, then we can fix the `1st` number one by one, and use two points to find the `2nd` + `3rd` number that make the three sum = 0:
57

68
```
@@ -54,13 +56,7 @@ fun threeSum(nums: IntArray): List<List<Int>> {
5456
* **Time Complexity**: `O(n^2)`.
5557
* **Space Complexity**: `O(n^2)`.
5658

57-
#### References
58-
* https://www.geekxh.com/1.0.%E6%95%B0%E7%BB%84%E7%B3%BB%E5%88%97/008.html#_02%E3%80%81%E9%A2%98%E7%9B%AE%E5%88%86%E6%9E%90
59-
* https://www.cnblogs.com/grandyang/p/4481576.html
60-
* Clearer answer: https://leetcode.com/problems/3sum/discuss/143636/Java-with-set
61-
62-
63-
### First Attempt
59+
### First Attempt (WA)
6460
We fix the `1st` number, then we can find the `2nd`, `3rd` numbers from two sum solution. However, it can't avoid the duplicate tuiplets and that breaks the problem.
6561

6662
```kotlin

leetcode/189.rotate-array.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,25 @@ k = 3
1818

1919
```kotlin
2020
fun rotate(nums: IntArray, k: Int): Unit {
21-
reverse(nums)
22-
val rotateIndex = k % nums.size
23-
reserve(nums.slice(0, rotateIndex - 1))
24-
reserve(nums.slice(rotateIndex, nums.size - 1))
21+
val kk = k % nums.size
22+
if (kk == 0) return
23+
reverse(nums, 0, nums.size - 1)
24+
reverse(nums, 0, (kk - 1))
25+
reverse(nums, kk, nums.size - 1)
2526
}
2627

27-
private fun reserve(nums: IntArray) {
28-
var left = 0
29-
var right = nums.size - 1
28+
private fun reverse(A: IntArray, start: Int, end: Int) {
29+
var left = start
30+
var right = end
3031
while (left < right) {
31-
swap(nums, left++, right--)
32+
val temp = A[left]
33+
A[left] = A[right]
34+
A[right] = temp
35+
36+
left++
37+
right--
3238
}
3339
}
34-
35-
private fun swap(nums: IntArray, n1: Int, n2: Int) {
36-
val temp = nums[n1]
37-
nums[n1] = nums[n2]
38-
nums[n2] = temp
39-
}
4040
```
4141

4242
### With Extra Space
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
## [215. Kth Largest Element in an Array](https://leetcode.com/problems/kth-largest-element-in-an-array/)
2+
3+
### Using Build-in Heap
4+
```kotlin
5+
fun findKthLargest(nums: IntArray, k: Int): Int {
6+
// Default is ascending
7+
val heap = PriorityQueue<Int>(Comparator<Int> { n1, n2 ->
8+
// a negative integer, zero, or a positive integer
9+
// as the first argument is less than, equal to, or greater than the second.
10+
// Descending
11+
// when {
12+
// i > j -> -1
13+
// i < j -> 1
14+
// else -> 0
15+
// }
16+
n2 - n1
17+
})
18+
heap.addAll(nums.toList())
19+
var i = 1
20+
while (i < k) {
21+
heap.poll()
22+
i++
23+
}
24+
return heap.peek()
25+
}
26+
```
27+
28+
### IMPORTANT!!
29+
The k-th element from the array `A` after `buildMaxHeap()` **CAN'T** be accessed directed by `A[k- 1]`, it should be accessed by `poll()` (it will re-heapify after this operation).
30+
31+
```js
32+
Original = [3,2,3,1,2,4,5,5,6]
33+
Heap = [6,5,5,3,2,4,3,2,1]
34+
// kth = 4 is not 3!!!
35+
```
36+
37+
### Implement My Heap
38+
* It's important to use `heapSize` for heapify and pop, since you can't change the array.
39+
40+
```kotlin
41+
fun findKthLargest(nums: IntArray, k: Int): Int {
42+
buildMaxHeap(nums)
43+
var i = 0
44+
while (i < k - 1) {
45+
pop(nums, nums.size - i)
46+
i++
47+
}
48+
return nums[0]
49+
}
50+
51+
private fun buildMaxHeap(A: IntArray) {
52+
for (i in A.size / 2 downTo 0) {
53+
heapifyDown(A, i, A.size)
54+
}
55+
}
56+
57+
private fun heapifyDown(A: IntArray, index: Int, heapSize: Int) {
58+
val leftIndex = index * 2 + 1
59+
val rightIndex = index * 2 + 2
60+
var largestIndex = index
61+
62+
if (leftIndex < heapSize && A[leftIndex] > A[largestIndex]) {
63+
largestIndex = leftIndex
64+
}
65+
if (rightIndex < heapSize && A[rightIndex] > A[largestIndex]) {
66+
largestIndex = rightIndex
67+
}
68+
if (largestIndex != index) {
69+
swap(A, largestIndex, index)
70+
heapifyDown(A, largestIndex, heapSize)
71+
}
72+
}
73+
74+
private fun swap(A: IntArray, i: Int, j: Int) {
75+
val temp = A[i]
76+
A[i] = A[j]
77+
A[j] = temp
78+
}
79+
80+
private fun pop(A: IntArray, heapSize: Int): Int {
81+
val peek = A[0]
82+
A[0] = A[heapSize - 1]
83+
heapifyDown(A, 0, heapSize - 1)
84+
return peek
85+
}
86+
```
87+
88+
### Quickselect
89+
Idea from [Quick Sort](../topics/sorting.md#quick-sort)
90+
91+
* Partition: `[ < X ][X][ > X]` returns `q` (the pivot index), once `q == k` then `A[X]` is what we're looking for.
92+
93+
```kotlin
94+
fun findKthLargest(nums: IntArray, k: Int): Int {
95+
// Top k = (size - k)-th small number
96+
// Quick sort is sort ascendingly, we have find the (size - k)-th small number!!
97+
return quickSelect(nums, 0, nums.size - 1, nums.size - k)
98+
}
99+
100+
private fun quickSelect(A: IntArray, p: Int, r: Int, k: Int): Int {
101+
val q = partition(A, p, r)
102+
if (q == k) return A[q]
103+
else {
104+
return if (q < k) quickSelect(A, q + 1, r, k)
105+
else quickSelect(A, p, q - 1, k)
106+
}
107+
}
108+
109+
private fun partition(A: IntArray, p: Int, r: Int): Int {
110+
// We can choose pivot randomly in range p and r to avoid worst case
111+
val pivot = A[p]
112+
var i = p
113+
for (j in p + 1..r) {
114+
if (A[j] <= pivot) {
115+
i++
116+
swap(A, i, j)
117+
}
118+
}
119+
swap(A, i, p)
120+
return i
121+
}
122+
123+
124+
private fun swap(A: IntArray, i: Int, j: Int) {
125+
val temp = A[i]
126+
A[i] = A[j]
127+
A[j] = temp
128+
}
129+
```
130+
131+
* **Time Complexity**: `O(n)` (expected, average) but `O(n^2)` for worst case.
132+
* **Space Complexity**: `O(lg n)` (expected) for recursion.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
## [295. Find Median from Data Stream](https://leetcode.com/problems/find-median-from-data-stream/)
2+
3+
**Idea!** We use two heaps to store the numbers, min heap to store half of numbers that are greater than median and max heap to store half of numbers that are less than median.
4+
5+
* We choose the correct heap to add (comparing with median)
6+
* We have to re-balance the two heaps so that its size difference will be within 1.
7+
8+
![](../media/295.find-median-from-data-stream.png)
9+
10+
```js
11+
Min Heap:
12+
3
13+
/ \
14+
4 5
15+
16+
Max Heap:
17+
2
18+
/ \
19+
1 0
20+
21+
Median = 3
22+
```
23+
24+
```js
25+
Min Heap:
26+
3
27+
/ \
28+
4 5
29+
30+
Max Heap:
31+
2
32+
/
33+
1
34+
35+
Median = (3 + 2) / 2
36+
```
37+
38+
39+
```kotlin
40+
class MedianFinder() {
41+
42+
// We use min heap to store numbers greater than median
43+
private val minHeap = PriorityQueue<Int>() { n1, n2 -> n1 - n2 }
44+
// We use max heap to store numbsers less thant median
45+
private val maxHeap = PriorityQueue<Int>() { n1, n2 -> n2 - n1 }
46+
47+
fun addNum(num: Int) {
48+
if (minHeap.isEmpty() || num >= minHeap.peek()) {
49+
minHeap.add(num)
50+
51+
// Re-balance the two heaps to make sure that the size difference will be <= 1
52+
if (minHeap.size > maxHeap.size + 1) {
53+
maxHeap.add(minHeap.poll())
54+
}
55+
} else {
56+
maxHeap.add(num)
57+
58+
// Re-balance two heaps
59+
if (maxHeap.size > minHeap.size) {
60+
minHeap.add(maxHeap.poll())
61+
}
62+
}
63+
}
64+
65+
fun findMedian(): Double {
66+
// For odd size
67+
return if (minHeap.size > maxHeap.size) minHeap.peek().toDouble()
68+
// For even size
69+
else (minHeap.peek().toDouble() + maxHeap.peek().toDouble()) / 2.0
70+
}
71+
}
72+
73+
/**
74+
* Your MedianFinder object will be instantiated and called as such:
75+
* var obj = MedianFinder()
76+
* obj.addNum(num)
77+
* var param_2 = obj.findMedian()
78+
*/
79+
```
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
## [347. Top K Frequent Elements](https://leetcode.com/problems/top-k-frequent-elements/)
2+
3+
### Heap
4+
* We use min heap, iterate all frequency entry, pop if the heap size > `k`, the final result of heap will be the result (in reversed order).
5+
6+
```kotlin
7+
fun topKFrequent(nums: IntArray, k: Int): IntArray {
8+
if (nums.size == 1) return nums
9+
// Value and it frequency
10+
val frequencyMap = hashMapOf<Int, Int>()
11+
for (i in 0 until nums.size) {
12+
if (frequencyMap.containsKey(nums[i])) {
13+
frequencyMap[nums[i]] = frequencyMap[nums[i]]!! + 1
14+
} else {
15+
frequencyMap[nums[i]] = 1
16+
}
17+
}
18+
19+
val minHeap = PriorityQueue<Map.Entry<Int, Int>>() { e1, e2 ->
20+
e1.value - e2.value
21+
}
22+
frequencyMap.forEach {
23+
minHeap.add(it)
24+
if (minHeap.size > k) {
25+
minHeap.poll()
26+
}
27+
}
28+
val results = IntArray(k)
29+
var index = k - 1
30+
while (index >= 0) {
31+
results[index] = minHeap.poll()!!.key
32+
index--
33+
}
34+
return results
35+
}
36+
```
37+
38+
* **Time Complexity**: `O(n lg k)`, we iterate `n` items, and `add()` / `poll()` takes `O(lg k)` time, total takes `O(n lg k)` time.
39+
* **Space Complexity**: `O(n) + O(k)` for hash table and heap respectively, total takes `O(n)` time.
40+
41+
### Bucket Sort
42+
```kotlin
43+
fun topKFrequent(nums: IntArray, k: Int): IntArray {
44+
val results = mutableListOf<Int>()
45+
// Value and its frequency
46+
// (1, 3)
47+
// (2, 2)
48+
// (3, 3)
49+
// (4, 1)
50+
val frequencyMap = hashMapOf<Int, Int>()
51+
for (i in 0 until nums.size) {
52+
val value = nums[i]
53+
if (frequencyMap.containsKey(value)) {
54+
frequencyMap[value] = frequencyMap[value]!! + 1
55+
} else {
56+
frequencyMap[value] = 1
57+
}
58+
}
59+
60+
// We use frequency as index, and store its values of that frequency
61+
// bucketList[0] = []
62+
// bucketList[1] = [4]
63+
// bucketList[2] = [2]
64+
// bucketList[3] = [1, 3]
65+
// bucketList[4] = []
66+
// Plus 1 for zero frequency
67+
val bucketList = MutableList<MutableList<Int>>(nums.size + 1) { _ -> mutableListOf() }
68+
for (entry in frequencyMap) {
69+
val value = entry.key
70+
val frequency = entry.value
71+
bucketList.get(frequency).add(value)
72+
}
73+
for (i in bucketList.size - 1 downTo 0) {
74+
if (bucketList.getOrNull(i) == null) continue
75+
else {
76+
results.addAll(bucketList.get(i))
77+
if (results.size >= k) break
78+
}
79+
}
80+
return results.toIntArray()
81+
}
82+
```

0 commit comments

Comments
 (0)