https://leetcode.com/problems/k-closest-points-to-origin
when I try to solve this leecode problem, I am curious about how to get the best parameters in my algorithm.
runtime
[1]: https://i.stack.imgur.com/enVe3.png
here is my code:
func kClosest(points [][]int, k int) [][]int {
res := make([][]int,0,k)
max := 0
for i,v := range points {
p := v[0]*v[0]+v[1]*v[1]
if len(res) <k {
if p > max {
max = p
}
res = append(res,v)
if i == k-1 {
sort.Slice(res,func(i,j int) bool {
return res[i][0]*res[i][0]+res[i][1]*res[i][1] < res[j][0]*res[j][0]+res[j][1]*res[j][1]
})
}
continue
}
if p > max {
continue
}
res = append(res,v)
// the 50 is the parameters I want to optimal
if len(res) > 50*k {
sort.Slice(res,func(i,j int) bool {
return res[i][0]*res[i][0]+res[i][1]*res[i][1] < res[j][0]*res[j][0]+res[j][1]*res[j][1]
})
res = res[:k]
max = res[k-1][0]*res[k-1][0]+res[k-1][1]*res[k-1][1]
}
}
sort.Slice(res,func(i,j int) bool {
return res[i][0]*res[i][0]+res[i][1]*res[i][1] < res[j][0]*res[j][0]+res[j][1]*res[j][1]
})
res = res[:k]
return res
}
I think you're using the essentially wrong algorithm -- you're repeatedly sorting the slice and truncating it when it gets too long to try to avoid the O(n log n) runtime. This gives you O(n log k) performance overall (each sort is O(k log k), and you sort approximately n/k times). You can more easily get O(n log k) performance by having a max-heap to which you insert elements one by one (removing the max beforehand when the heap is already at size k and the new element is smaller than the max).
But best is use QuickSelect to select the smallest k elements. It's O(n) time, and the question is obviously geared towards this as a solution because it doesn't require the answer to be in any particular order. It's not in the standard library, but you can easily find implementations online if you can't write it yourself. As a matter of optimization, it's probably better to precompute a slice of the vector lengths coupled with indexes into the original slice and quickselect that rather than the original slice, but it's hard to know without profiling.
Related
Basic approach to delete multiple keys from an array of Maps in Go, would be by using nested loops, that is with a parent loop for iterating over array of Maps and an inner loop of slice of Keys to be deleted. Is there a way to do this without using nested loops. Just trying to figure out a way to get better time complexity that O(n^2).
Removing a slice of keys within a Map with time complexity better than
O(n^2) in Go
What is n? Why is time complexity O(n^2)?
Consider real code and real cases:
package main
func mapsDeleteUniqueKeys(maps []map[string]int, keys []string) {
// iterations = len(keys) × len(maps)
for _, k := range keys {
for _, m := range maps {
delete(m, k)
}
}
}
func mapsDeleteDuplicateKeys(maps []map[string]int, keys []string) {
// iterations = len(keys) + (len(unique) × len(maps))
unique := make(map[string]struct{}, len(keys))
for _, k := range keys {
unique[k] = struct{}{}
}
for k := range unique {
for _, m := range maps {
delete(m, k)
}
}
}
func main() {}
What is the growth function for the number of keys? What is the growth function for the number of maps? What is the growth function for the number of keys times the number of maps?
Is the time spent on each iteration significant?
In your case: What do the keys represent? In your case: What do the maps represent?
What worst-case time complexity do you expect to encounter in the real world?
What Go benchmarks have you run? What were the results?
Time complexity appears to be O(len(keys) × len(maps)), or O(k × m),.
In practice len(maps), the number of maps, is likely small relative to len(keys), the number of keys. The number of maps may also be constant. Therefore, the time complexity is likely near O(len(keys)) or O(n).
I have a question: my solution to the problem of finding common item in two Arrays was this one:
func commonItem2(list1 []string, list2 []string) bool {
list1 = append(list1, list2...)
sort.Strings(list1)
for i := 0; i < len(list1)-1; i++ {
if list1[i] == list1[i+1] {
return true
}
}
return false
} //O(n)
Would that really be O(n)?
Since I'm appending one array to the other and that is as far as I know O(n), don't know the O of the sort.Strings function in Go though.
Any ideas?
Time complexity of the sort.Strings function in Golang is O(n*log(n)), in which n is the length of the array.
As today, internally, sort function makes one call to data.Len to determine n, and O(n*log(n)) calls to data.Less and data.Swap. It uses quick sort for slices with > 12 elements, and uses shell sort for slices with <= 12 elements.
Question Description
What is a good way to calculate the space(stack space) and the time complexity for backtracking algorithms?
Example
We want the output combo to have exactly a length of K, and the sol set must be unique
Input:
arr: [1,2,3,4,5]
K: 4
Output:
[1,2,3,4] //[2,1,3,4] is invalid because it's == [1,2,3,4]
[1,2,3,5]
[1,3,4,5]
[2,3,4,5]
// arr == [1,2,3,4,5]
// K == 4
// backtrack(arr,4,0,{},...other params we don't care)
void backtrack(vector<int> &arr, int K, int start, vector<int> &sol, ...)
{
if(sol.size() == K)
{
//...
}
else
{
for(int i = start; i < arr.size(); i++)
{
sol.push_back(arr[i]);
backtrack(arr, K, i+1,sol,...);
sol.pop_back();
}
}
}
I think
The worst space complexity is O(k), because I think when I recur f1(), f2() to f5() won't be called after the whole subtree of f1() is finished.
[]
f1() f2() f3() f4() f()5
f1.1() f()1.2 ...
The worst time complexity is O(n^k), where n is the length of the array.
Technically time complexity isn't O(n^k) but something like O(sum from i = 1 to k bin(n,i)) because you don't start searching from the beginning of arr but from the element after the last one on the solution and also don't cut states from which you can't finish like [3]. Usually time complexity in such cases is number of states times average time that you need to get from one state to another.
This is a practice question for the understanding of Divide and conquer algorithms.
You are given an array of N sorted integers. All the elements are distinct except one
element is repeated twice. Design an O (log N) algorithm to find that element.
I get that array needs to be divided and see if an equal counterpart is found in the next index, some variant of binary search, I believe. But I can't find any solution or guidance regarding that.
You can not do it in O(log n) time because at any step even if u divide the array in 2 parts, u can not decide which part to consider for further processing and which should be left.
On the other hand if the consecutive numbers are all present in the array then by looking at the index and the value in the index we can decide if the duplicate number is in left side or right side of the array.
D&C should look something like this
int Twice (int a[],int i, int j) {
if (i >= j)
return -1;
int k = (i+j)/2;
if (a[k] == a[k+1])
return k;
if (a[k] == a[k-1])
return k-1;
int m = Twice(a,i,k-1);
int n = Twice(a,k+1,j);
return m != -1 ? m : n;
}
int Twice (int a[], int n) {
return Twice(a,0,n);
}
But it has complexity O(n). As it is said above, it is not possible to find O(lg n) algorithm for this problem.
I have fifo query where I put and eject doubles.
After each update I need max and min values. I don't need position (or index) of these values in query.
How to do that effectively? log(N) or probably even O(1)?
upd: found this Implement a queue in which push_rear(), pop_front() and get_min() are all constant time operations
This is a tricky question. Consider the following:
Say the size of your fifo at any given time is N.
Say that you track the min and max with just a pair of floats.
Say that the size of the fifo remains reasonably constant.
We can therefore assume that one "operation" on the queue logically consists of one push and one pop.
Say you are comparing 2 methods of handling this: one uses a heap pair and one that uses a naive compare and search.
For the heap method:
Each operation, you push to the list and both heaps, then pop from the list and both heaps. The heap operations are always O(log(n)) and the list operation is O(1), so as N is large, the time complexity of one operation is O(log(N)) average case. It's important to note that the heap operations are always this complexity regardless of whether or not the currently popped element is a min or max element. Thus, N operations has a time complexity of O(N*log(N)).
For the naive method:
Each operation, you push and pop the list, and compare the popped item to the stored min and max. If the item is the same as either one, you search the list either for an item of equal value (in which case you break early) or otherwise through the entire rest of the list, until you find the next best element. You then update the min/max with the next best. This method has O(1) typical case and O(N) worst case (min or max needs an update). It's important to note that for some range of N numbers the number of times you will need to update min and max goes to a constant, and the number of times you won't goes to N. Therefore, N operations has a time complexity of O(N). The naive case is actually better than a more advanced solution.
That said, I don't think heaps can efficiently remove elements, so you'd run into lots of trouble that way.
Thus, consider the following pseudocode:
queue fifo;
float min, max;
void push(float f)
{
if (fifo.size == 0)
{
min = f;
max = f;
}
else if (f > max) max = f;
else if (f < min) min = f;
fifo.push(f);
}
float pop()
{
if (fifo.size == 0) return (*((float *)NULL)); // explode
float f = fifo.pop();
if (fifo.size == 0)
{
min = NaN;
max = NaN;
return f;
}
if (f == max) search_max(fifo);
if (f == min) search_min(fifo);
return f;
}
search_max(queue q)
{
float f = min;
for (element in q)
{
if (element == max) return;
if (element > f) f = element;
}
max = f;
}
search_min(queue q)
{
float f = max;
for (element in q)
{
if (element == min) return;
if (element < f) f = element;
}
min = f;
}
How about using heap ( http://en.wikipedia.org/wiki/Heap_%28data_structure%29 ). You can have two heaps. One for extracting min and one for max (as a single heap cannot extract min and max at the same time). it also does not require any space overhead and the Big O is log n.