347. Top K Frequent Elements
Given a non-empty array of integers, return the k most frequent elements.
Example 1:
Input: nums = [1,1,1,2,2,3], k = 2
Output: [1,2]
Example 2:
Input: nums = [1], k = 1
Output: [1]
Note:
You may assume k is always valid, 1 ≤ k ≤ number of unique elements.
Your algorithm’s time complexity must be better than O(n log n), where n is the array’s size.
It’s guaranteed that the answer is unique, in other words the set of the top k frequent elements is unique.
You can return the answer in any order.
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/top-k-frequent-elements
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
Think and method
No matter what method is used, there is one step that should not be skipped. Since we are considering the first K high frequency elements in the array, we might as well follow the idea of counting out the occurrence frequency of each number first and considering whether to arrange them.
1、simple quick sort
Direct sorting is the most straightforward and easy to think of. In order to improve the speed, we choose fast sorting, which is similar to a problem I did a few days ago. Simply speaking, we divide the array into two parts with a number q, and then we consider it with k。
If k ≤q − i, then the large pre-k value of the array [l… r] is equal to the large pre-k value of the subarray arr[i… q-1].
Otherwise, the large pre-k value in the array arr[l… r] equals all the elements in the left subarray, plus the large pre-k-(q-i) value in the right subarray arr[q +1… j].
Although it is easy to think of, the actual implementation of the algorithm is a bit complicated, and I refer to the official documentation of the method.
Time complexity: O(n)
The average time complexity of the quicksort algorithm is O(nlogn).Because we only need to recurse one branch at a time, so the average time complexity of the algorithm is O(n).
Space complexity: O(n)
2、similar to 1 and a little change
Create an array, using the frequency of occurrence as the index of the array, to get the required minimum frequency of k most frequent occurrences and output
Time complexity: O(n)
Space complexity: O(n)
3、leap
Instead of sorting, I used the heap, which was the first time I used the heap in the Go exercise, and learned a lot.
Create a small top heap and then traverse the Occurrence array:
1)If the number of elements in the heap is less than kk, it can be inserted directly into the heap.
2)If the number of elements in the heap is equal to kk, check the size of the top of the heap with the current number of occurrences.If the top of the heap is larger, it means that at least kk Numbers appear more frequently than the current value, so the current value is abandoned.Otherwise, the top of the heap pops out and the current value is inserted into the heap.
Time complexity: O(nlogk),
traversing the original array, and using a hash table to record the occurrence of times, a total of Order N time.Traversing the occurrence array, since the heap size is at most K, each heap operation requires O(logk) time, and a total of O(nlogk) time.
Space complexity: O(N)
Code:
1、
func topKFrequent(nums []int, k int) []int {
occurrences := map[int]int{}
for _, num := range nums {
occurrences[num]++
}
values := [][]int{}
for key, value := range occurrences {
values = append(values, []int{key, value})
}
result := make([]int, k)
qsort(values, 0, len(values) - 1, result, 0, k)
return result
}
func qsort(values [][]int, start, end int, result []int, retIndex, k int) {
//get a random k between intervals
rand.Seed(time.Now().UnixNano())
q := rand.Int() % (end - start + 1) + start;
values[q], values[start] = values[start], values[q]
pivot := values[start][1]
index := start
//make sure any nums in arr[i...q-1] < arr[q]
for i := start + 1; i <= end; i++ {
if values[i][1] >= pivot {
values[index + 1], values[i] = values[i], values[index + 1]
index++
}
}
values[start], values[index] = values[index], values[start]
//k <= q-i
if k <= index - start {
qsort(values, start, index - 1, result, retIndex, k)
} else {
for i := start; i <= index; i++ {
result[retIndex] = values[i][0]
retIndex++
}
if k > index - start + 1 {
qsort(values, index + 1, end, result, retIndex, k - (index - start + 1))
}
}
}
2、
func topKFrequent(nums []int, k int) []int {
result := make([]int, 0, k)
// //traversal and get the appearing times of each numbers
appearnums := make(map[int]int, len(nums))
for _, n := range nums {
appearnums[n]++
}
// sort
counts := make([]int, 0, len(appearnums))
for _, i := range appearnums {
counts = append(counts, i)
}
sort.Ints(counts)
min_times := counts[len(counts)-k]
// get result according to the min_times
for i, j := range appearnums {
if j >= min_times {
result = append(result, i)
}
}
return result
}
3、
func topKFrequent(nums []int, k int) []int {
//traversal and get the appearing times of each numbers
appearnums := map[int]int{}
for _, num := range nums {
appearnums[num]++
}
h := &IHeap{}
heap.Init(h)
//set a heap and traversal the "appearnums"
for key, value := range appearnums {
//if the number of elements < k insert the heap
heap.Push(h, [2]int{key, value})
//if the number of elements > k, Take an element from the end and return
if h.Len() > k {
heap.Pop(h)
}
}
//tramsfer into []int
ret := make([]int, k)
for i := 0; i < k; i++ {
ret[k - i - 1] = heap.Pop(h).([2]int)[0]
}
return ret
}
//Methods to implement interface definitions
type IHeap [][2]int
func (h IHeap) Len() int { return len(h) }
func (h IHeap) Less(i, j int) bool { return h[i][1] < h[j][1] }
func (h IHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *IHeap) Push(x interface{}) {
*h = append(*h, x.([2]int))
}
func (h *IHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
Test:
Example 1:
Input: nums = [1,1,1,2,2,3], k = 2
Example 2:
Input: nums = [1], k = 1
In practical tests, the second improved sorting method is the fastest.