前K个高频元素
解法1:哈希表+排序
一遍遍历统计各个元素的出现次数,再根据其出现次数对元素进行排序,输出排序后的Top-K元素。
代码略。
该方法的时间复杂度为 O(nlogn),其中n为nums的长度。
(进阶)
解法2:堆
一次遍历统计各个元素的出现次数,得到元素出现次数字典。但后续我们不用排序,而是利用堆的思想,构建一个小顶堆,大小为k。遍历元素出现次数字典元素,处理规则如下:
- 若堆中元素小于k,则直接加入堆中。
- 如果堆的元素个数等于 k,则检查堆顶与当前出现次数的大小。如果堆顶更大,说明至少有 k 个数字的出现次数比当前值大,故舍弃当前值;否则,就弹出堆顶,并将当前值插入堆中
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
num_dict = {}
for num in nums:
if num not in num_dict:
num_dict[num] = 1
else:
num_dict[num] += 1
heap = []
l = k
def build_heap(i, l):
left, right = 2*i+1, 2*i+2
min_index = i
if left < l and heap[i][1] > heap[left][1]:
min_index = left
if right < l and heap[left][1] > heap[right][1] and heap[i][1] > heap[right][1]:
min_index = right
if min_index != i:
heap[i], heap[min_index] = heap[min_index], heap[i]
build_heap(min_index, l)
# print(num_dict)
for num in num_dict:
if len(heap) < k:
# <k,则直接加入。
heap.append((num,num_dict[num]))
if len(heap) == k:
# 遍历其非叶子节点,构造小顶堆。
for i in range(k//2-1, -1, -1):
build_heap(i, l)
else:
if num_dict[num] > heap[0][1]:
# print(num, heap)
# 舍弃堆顶元素,并将新值插入堆中,重新调整小顶堆
heap[0] = (num, num_dict[num])
build_heap(0, l)
# print(heap)
return [temp[0] for temp in heap]
该方法时间复杂度O(nlogk)其中k为堆大小。
关于堆相关原理可参考Python实现堆排序及原理详解 + TopK面试题(多图解释),但其代码在build_heap的找寻当前节点和左右子节点中的最小值有些问题。
字符的最短距离
解法:两次遍历+双指针
步骤如下:
- 第一次遍历得到 c 在 s 中出现位置列表。
- 维护两个指针left和right,分别指向c的当前位置和下一个位置。
- 遍历s中元素,若等于c且索引大于right,则更新left和right指针至下一个位置。结果列表中+0
- 若不等于c,则增加其与两个指针之间距离的最小值。
class Solution:
def shortestToChar(self, s: str, c: str) -> List[int]:
pos = []
result = []
for i, char in enumerate(s):
if char == c:
pos.append(i)
i = 0
# min(i+1, len(pos)-1)是解决s中只有一个c的情况
left, right = pos[i], pos[min(i+1, len(pos)-1)]
# print(pos)
# print(left, right)
for j, char in enumerate(s):
if char == c:
if j >= right and i < len(pos) - 2:
i += 1
left, right = pos[i], pos[i+1]
# print(left, right)
result.append(0)
continue
result.append(min(abs(j-left), abs(j-right)))
return result