Skip to content

Commit 1216299

Browse files
Problem 973 (#23)
* add solution and resources * update readme
1 parent 5445b3e commit 1216299

File tree

2 files changed

+243
-1
lines changed

2 files changed

+243
-1
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ As I work through this list I figure I would make a GitHub repo with my solution
3636
25. [Maximum Subarray #53](https://github.com/curtisbarnard/leetcode-grind75-javascript/blob/main/medium/maximum-subarray-53.md)
3737
26. Insert Interval #57
3838
27. 01 Matrix #542
39-
28. K Closest Points to Origin #973
39+
28. [K Closest Points to Origin #973](https://github.com/curtisbarnard/leetcode-grind75-javascript/blob/main/medium/k-closest-origin-973.md)
4040
29. Longest Substring Without Repeating Characters #3
4141
30. 3Sum #15
4242
31. Binary Tree Level Order Traversal #102
@@ -101,6 +101,7 @@ In order to practice with similar data structures I'll be placing each problem i
101101
- [Maximum Subarray #53](https://github.com/curtisbarnard/leetcode-grind75-javascript/blob/main/medium/maximum-subarray-53.md)
102102
- [Majority Element #169](https://github.com/curtisbarnard/leetcode-grind75-javascript/blob/main/easy/majority-element-169.md)
103103
- [Contains Duplicate #217](https://github.com/curtisbarnard/leetcode-grind75-javascript/blob/main/easy/contains-duplicate-217.md)
104+
- [K Closest Points to Origin #973](https://github.com/curtisbarnard/leetcode-grind75-javascript/blob/main/medium/k-closest-origin-973.md)
104105

105106
### Queue
106107

@@ -145,6 +146,10 @@ In order to practice with similar data structures I'll be placing each problem i
145146
- [Diameter of Binary Tree #543](https://github.com/curtisbarnard/leetcode-grind75-javascript/blob/main/easy/diameter-binary-tree-543.md)
146147
- [Maximum Depth of Binary Tree #104](https://github.com/curtisbarnard/leetcode-grind75-javascript/blob/main/easy/depth-binary-tree-104.md)
147148

149+
### Heap
150+
151+
- [K Closest Points to Origin #973](https://github.com/curtisbarnard/leetcode-grind75-javascript/blob/main/medium/k-closest-origin-973.md)
152+
148153
## Algorithm Patterns
149154

150155
Within the problems above there are several patterns that often occur. I plan to categorize each problem such that if you are having problem with a particular pattern you can look for other similar problems that use the same pattern.

medium/k-closest-origin-973.md

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
# K Closest Points to Origin
2+
3+
Page on leetcode: https://leetcode.com/problems/k-closest-points-to-origin/
4+
5+
## Problem Statement
6+
7+
Given an array of points where points[i] = [xi, yi] represents a point on the X-Y plane and an integer k, return the k closest points to the origin (0, 0). The distance between two points on the X-Y plane is the Euclidean distance (i.e., √(x1 - x2)2 + (y1 - y2)2). You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in).
8+
9+
### Constraints
10+
11+
- 1 <= k <= points.length <= 104
12+
- -104 < xi, yi < 104
13+
14+
### Example
15+
16+
```
17+
Input: points = [[1,3],[-2,2]], k = 1
18+
Output: [[-2,2]]
19+
Explanation:
20+
The distance between (1, 3) and the origin is sqrt(10).
21+
The distance between (-2, 2) and the origin is sqrt(8).
22+
Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin.
23+
We only want the closest k = 1 points from the origin, so the answer is just [[-2,2]].
24+
```
25+
26+
## Solution
27+
28+
Any order so sorting isn't required. This is an optimization problem (minimizing distance to origin) so my mind goes to greedy methods. A heap might be a good data structure. I will need to look at all sets of points so the best conceivable time complexity would be O(n). I'm not sure if that is attainable though.
29+
30+
Edge case would be 1 point provided, with a k of 1. Or k = num of points.
31+
32+
### Pseudocode
33+
34+
1. If points length equals k then return points
35+
2. Create helper function that returns euclidean distance
36+
3. Heapify array to Min-Heap
37+
4. Remove k smallest points from heap to array and return array
38+
39+
### Initial Attempt
40+
41+
```javascript
42+
const kClosest = function (points, k) {
43+
if (points.length === k) {
44+
return points;
45+
}
46+
47+
function dist(point) {
48+
return Math.sqrt(point[0] ** 2 + point[1] ** 2);
49+
}
50+
51+
function heapify(arr) {
52+
for (let i = points.length - 1; i >= 0; i--) {
53+
const right = 2 * i + 2;
54+
const left = 2 * i + 1;
55+
if (arr[right]) {
56+
if (dist(arr[right]) < dist(arr[i])) {
57+
const temp = arr[i];
58+
arr[i] = arr[right];
59+
arr[right] = temp;
60+
}
61+
}
62+
if (arr[left]) {
63+
if (dist(arr[left]) < dist(arr[i])) {
64+
const temp = arr[i];
65+
arr[i] = arr[left];
66+
arr[left] = temp;
67+
}
68+
}
69+
}
70+
return arr;
71+
}
72+
73+
function deleteFromHeap(arr) {
74+
const first = arr.shift();
75+
const last = arr.pop();
76+
arr.unshift(last);
77+
bubbleDown(arr, 0);
78+
return first;
79+
}
80+
81+
function bubbleDown(arr, point) {
82+
if (!arr[point]) {
83+
return;
84+
}
85+
const right = 2 * i + 2;
86+
const left = 2 * i + 1;
87+
if (arr[left] < arr[right] && arr[left] < arr[point]) {
88+
arr[point] = arr[left];
89+
arr[left] = bubbleDown(arr, left);
90+
} else if (arr[right] < arr[left] && arr[right] < arr[point]) {
91+
arr[point] = arr[right];
92+
arr[right] = bubbleDown(arr, right);
93+
}
94+
return;
95+
}
96+
97+
let result = [];
98+
for (let i = 0; i < k; i++) {
99+
result.push(deleteFromHeap(points));
100+
}
101+
return result;
102+
};
103+
```
104+
105+
### Second Attempt
106+
107+
I've used the max heap approach which has a time complexity of O(nlogk) and a space complexity of O(k).
108+
109+
```javascript
110+
const kClosest = function (points, k) {
111+
if (points.length === k) {
112+
return points;
113+
}
114+
115+
const maxHeap = [];
116+
117+
// Add points to the heap
118+
for (let i = 0; i < points.length; i++) {
119+
if (maxHeap.length >= k && dist(points[i]) > dist(maxHeap[0])) {
120+
continue;
121+
}
122+
123+
addToHeap(points[i]);
124+
if (maxHeap.length > k) {
125+
removeFromHeap();
126+
}
127+
}
128+
129+
// Helper Functions
130+
function dist(point) {
131+
return point[0] ** 2 + point[1] ** 2;
132+
}
133+
134+
function addToHeap(point) {
135+
maxHeap.push(point);
136+
heapifyUp(maxHeap, maxHeap.length - 1);
137+
}
138+
139+
function heapifyUp(arr, node) {
140+
const parentNode = Math.floor((node - 1) / 2);
141+
if (parentNode < 0) {
142+
return;
143+
}
144+
if (dist(arr[node]) > dist(arr[parentNode])) {
145+
const temp = arr[parentNode];
146+
arr[parentNode] = arr[node];
147+
arr[node] = temp;
148+
heapifyUp(arr, parentNode);
149+
}
150+
return;
151+
}
152+
153+
function removeFromHeap() {
154+
maxHeap.shift();
155+
maxHeap.unshift(maxHeap.pop());
156+
heapifyDown(maxHeap, 0);
157+
}
158+
159+
function heapifyDown(arr, node) {
160+
const right = 2 * node + 2;
161+
const left = 2 * node + 1;
162+
if (right <= arr.length - 1) {
163+
if (dist(arr[right]) > dist(arr[node])) {
164+
const temp = arr[node];
165+
arr[node] = arr[right];
166+
arr[right] = temp;
167+
heapifyDown(arr, right);
168+
}
169+
}
170+
if (left <= arr.length - 1) {
171+
if (dist(arr[left]) > dist(arr[node])) {
172+
const temp = arr[node];
173+
arr[node] = arr[left];
174+
arr[left] = temp;
175+
heapifyDown(arr, left);
176+
}
177+
}
178+
return;
179+
}
180+
181+
return maxHeap;
182+
};
183+
```
184+
185+
### Optimized Solution
186+
187+
There are a few different ways to solve this problem. Good examples and discussion can be seen here: https://leetcode.com/problems/k-closest-points-to-origin/discuss/762781/javascript-sort-minHeap-and-maxHeap-solutions
188+
189+
I would say the min heap approach is the most ideal.
190+
191+
```javascript
192+
var kClosest = function (points, k) {
193+
// we can build the heap in place
194+
let p = Math.floor((points.length - 2) / 2); // last parent
195+
for (let i = p; i >= 0; i--) {
196+
heapifyDown(points, i, distance);
197+
}
198+
199+
// now we need to remove the smallest (points[0]) k times
200+
let solution = [];
201+
for (let i = 0; i < k; i++) {
202+
solution.push(remove(points, distance));
203+
}
204+
205+
return solution;
206+
207+
// read 0, replace 0 with last position, heapifyDown
208+
function remove(heap, weightFunction) {
209+
let val = heap[0];
210+
heap[0] = heap.pop();
211+
heapifyDown(heap, 0, weightFunction);
212+
return val;
213+
}
214+
215+
// compare with children, swap with smallest, repeat
216+
function heapifyDown(heap, idx, weightFunction) {
217+
let left = 2 * idx + 1;
218+
let right = 2 * idx + 2;
219+
let smallest = left;
220+
221+
if (left >= heap.length) return;
222+
223+
if (right < heap.length && weightFunction(heap[left]) > weightFunction(heap[right])) {
224+
smallest = right;
225+
}
226+
227+
if (weightFunction(heap[idx]) > weightFunction(heap[smallest])) {
228+
[heap[idx], heap[smallest]] = [heap[smallest], heap[idx]];
229+
heapifyDown(heap, smallest, weightFunction);
230+
}
231+
}
232+
233+
function distance(point) {
234+
return point[0] * point[0] + point[1] * point[1];
235+
}
236+
};
237+
```

0 commit comments

Comments
 (0)