The task is to return the K most frequent elements.
What I did is to calculate the frequencies and put it in a min-heap (as we know there's no max-heap in Python), then I need to heappop k times.
from collections import defaultdict
import heapq
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
counts = defaultdict(int)
for i in range(len(nums)):
counts[nums[i]] -= 1
counts = list(counts.items())
heapq.heapify(counts)
top_k = [heapq.heappop(counts)[0] for i in range(k)]
return top_k
Why does my code fails on topKFrequent([4,1,-1,2,-1,2,3], 2)?
You asked why it fails. It's failing because the heappop is returning the smallest item from counts. Counts is a tuple, so it is the smallest by the first element of the tuple, which are the nums. So you are getting back the nums, but only the unique ones because the dictionary is essentially acting like set.
Related
How can i know the time complexity of this code??, the point of the program is given a list of integers and a single sum value, return the first two values (parse from the left) in order of appearance that add up to form the sum.
If there are two or more pairs with the required sum, the pair whose second element has the smallest index is the solution. more info here: https://www.codewars.com/kata/54d81488b981293527000c8f/train/python/6330a6a6c68f64676db5d0b7
def sum_pairs(arr, suma): ##possible stack solution
pairs = {n:suma-n for n in set(arr)}
unsolved=set()
solved=set()
for i, n in enumerate(arr):
if pairs[n] in unsolved:
solved.add((pairs[n], n, i))
if n not in unsolved:
unsolved.add(n)
if solved == set():
return None
else:
solution = min(solved, key=lambda x:x[2])[:2]
return list(solution)
I have an algorithm written which gives result.
Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
You can return the answer in any order.
But it is too slow,
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
res=[]
for i in range(len(nums)):
first_val=nums[i]
second_val=target - nums[i]
for j in range(len(nums)):
if i!=j:
if nums[j]==second_val:
res.append(i)
res.append(j)
return res
return res
Could anyone assist me in rewriting this algorithm in Follow-up: Can you come up with an algorithm that is less than O(n2) time complexity?
In O(n) time complexity it can be done as below code.
logic is as, since there is solution pair in the given input, ie sum of these value make up to target, ie val1 + val2 = target, so we need to iterate through the list and search for val2 = target - val1, if we found it then return result.
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
info = {}
for i, v in enumerate(nums):
if target-v not in info:
info[v] = i
else:
return [info[target-v], i]
You can do it in O(n) with a collections.Counter:
from collections import Counter
def two_sum(nums: list[int], target: int) -> tuple[int, int]:
c = Counter(nums)
for a in c:
b = target - a
if c[b] >= 1 + (a == b):
return a, b
Building the Counter is O(n), iterating over it is also O(n), and checking for elements inside the iteration is O(1).
I have been following Neetcode and working on several Leetcode problems. Here on 347 he advises that his solution is O(n), but I am having a hard time really breaking out the solution to determine why that is. I feel like it is because the nested for loop only runs until len(answers) == k.
I started off by getting the time complexity of each individual line and the first several are O(n) or O(1), which makes sense. Once I got to the nested for loop I was thrown off because it would make sense that the inner loop would run for each outer loop iteration and result in O(n*m), or something of that nature. This is where I think the limiting factor of the return condition comes in to act as the ceiling for the loop iterations since we will always return once the answers array length equals k (also, in the problem we are guaranteed a unique answer). What is throwing me off the most is that I default to thinking that any nested for loop is going to be O(n^2), which doesn't seem to be uncommon, but seems to not be the case every time.
Could someone please advise if I am on the right track with my suggestion? How would you break this down?
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
countDict = {}
frequency = [[] for i in range(len(nums)+1)]
for j in nums:
countDict[j] = 1 + countDict.get(j, 0)
for c, v in countDict.items():
frequency[v].append(c)
answer = []
for n in range(len(frequency)-1, 0, -1):
for q in frequency[n]:
print(frequency[n])
answer.append(q)
if len(answer) == k:
return answer
frequency is mapping between frequency-of-elements and the values in the original list. The number of total elements inside of frequency is always equal or less than the number of original items in in nums (because it is mapping to the unique values in nums).
Even though there is a nested loop, it is still only ever iterating at some O(C*n) total values, where C <= 1 which is equal to O(n).
Note, you could clean up this answer fairly easily with some basic helpers. Counter can get you the original mapping for countDict. You can use a defaultdict to construct frequency. Then you can flatten the frequency dict values and slice the final result.
from collections import Counter, defaultdict
class Solution:
def top_k_frequent(self, nums: list[int], k: int) -> list[int]:
counter = Counter(nums)
frequency = defaultdict(list)
for num, freq in counter.items():
frequency[freq].append(num)
sorted_nums = []
for freq in sorted(frequency, reverse=True):
sorted_nums += frequency[freq]
return sorted_nums[:k]
A fun way to do this in a one liner!
class Solution:
def top_k_frequent(self, nums: list[int], k: int) -> list[int]:
return sorted(set(nums), key=Counter(nums).get, reverse=True)[:k]
The code I have written that aims to solve the Two Sum problem:
def twoSum(self, nums: List[int], target: int) -> List[int]:
dict = {}
for i in range(len(nums)):
complement = target - nums[i]
if complement in dict:
return [dict[complement], i]
dict[complement] = i
I have just started practicing on LeetCode and I am experiencing issues with solving the Two Sum problem.
The problem statement:
Given an array of integers nums and an integer target, return indices
of the two numbers such that they add up to target.
You may assume that each input would have exactly one solution, and
you may not use the same element twice.
You can return the answer in any order.
My reasoning is to create a dictionary and iterate through the numbers, and for each number generate a complement number that I then look for in my dictionary, if it in fact is there, then I return the index that generates that complement and the current index i. Otherwise, I insert the key with the complement.
Somehow my function does not output anything, just two empty brackets. Below there is a sample input and correct output.
Input: nums = [3,2,4], target = 6
Output: [1,2]
The last line is wrong. It should read dict[nums[i]] = i, because you are storing indeces for their values. Here is the entire function with a better variable name that doesn't shadow the built-in type:
def twoSum(self, nums, target):
dct = {}
for i in range(len(nums)):
complement = target - nums[i]
if complement in dct:
return [dct[complement], i]
dct[nums[i]] = i
Or more concise using enumerate and storing indeces for their complement values:
def twoSum(self, nums, target):
dct = {}
for i, num in enumerate(nums):
if num in dct:
return [dct[num], i]
dct[target - num] = i
You may notice that you had a mixture of the two approaches. You looked for the complement in dct, and also wanted to store it for the current index. One of the two needs to be the current value.
I have the following code for the Leetcode's top k Frequent question.
The time limit complexity allowed is smaller than o(nlogn), where n is the array size
Isn't my big O complexity of o(n)?
If so why am I still exceeding the time limit ?
def topKFrequent(self, nums, k):
output = {}
outlist = []
for item in nums:
output[item] = nums.count(item)
max_count = sorted(output.values(),reverse= True)[:k]
for key,val in output.items():
if val in max_count:
outlist.append(key)
return (outlist)
testinput: array [1,1,1,2,2,3,1,1,1,2,2,3] k = 2
testoutput: [1,2]
Question link: https://leetcode.com/problems/top-k-frequent-elements/
Your solution is O(n^2), because of this:
for item in nums:
output[item] = nums.count(item)
For each item in your array, you're looking through the whole array to count the number of elements which are the same.
Instead of doing this, you can get the counts in O(n) by iterating nums and adding 1 to the counter of each item you find as you go.
The O(n log n) in the end will come from sorted(output.values(), reverse=True) because every generic sorting algorithm (including Timsort) will be O(n log n).
As another answer mentions, your counting is O(n^2) time complexity, which is causing your time limit exceeded. Fortunately, python comes with a Counter object in the collections module, which will do exactly what the other answer describes, but in well-optimized C code. This will reduce your time complexity to O(nlogn).
Furthermore, you can reduce your time complexity to O(nlogk) by replacing the sort call with a min-heap trick. Keep a min-heap of size k, and add the other elements and pop the min one by one, until all elements have been inserted (at some point or another). The k that remain in the heap are your maximum k values.
from collections import Counter
from heapq import heappushpop, heapify
def get_most_frequent(nums, k):
counts = Counter(nums)
counts = [(v, k) for k, v in counts.items()]
heap = counts[:k]
heapify(heap)
for count in counts[k:]:
heappushpop(heap, count)
return [k for v, k in heap]
If you must return the elements in any particular order, you can sort the k elements in O(klogk) time, which still results in the same O(nlogk) time complexity overall.