leetcode:347. Top K Frequent Elements
问题描述:给定一个非空数组,返回前K个出现频率最高的元素。
解法:字典存放元素和频率,创建优先队列保存前K个(频率,元素)自动最小堆排序。
# 排序算法
# 时间复杂度:O(),其中 n 表示数组的长度。
# 空间复杂度:O(n),最极端的情况下(每个元素都不同),用于存储元素及其频率的 Map 需要存储 n 个键值对
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
# 创建字典哈希表,无序 key,value 元素,频率
dict1 = {}
for i in nums:
if i not in dict1:
dict1[i] = 1
else:
dict1[i] += 1
# 按照频率进行排序
freq_dict = sorted(dict1.items(), key=lambda x: x[1], reverse=True)
res = []
for i in range(k):
res.append(freq_dict[i][0])
return res
# 最小堆
# 时间复杂度:O(nlogk),其中 n 表示数组的长度。首先,统计元素的频率,时间复杂度是 O(n) 的;接着,遍历用于存储元素频率的 map,如果元素的频率大于最小堆中顶部的元素,则将顶部的元素删除并将该元素加入堆中,时间复杂度是 O(nlogk) 的;最后,弹出堆中的元素所需的时间复杂度是 O(klogk) 的。因此,总的时间复杂度是 O(nlogk)
# 空间复杂度:O(n),最坏情况下(每个元素都不同),map 需要存储 n 个键值对,优先队列需要存储 k 个元素
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
# 创建字典哈希表,无序 key,value 元素,频率
dict1 = {}
for i in nums:
# 字典(Dictionary) get() 函数返回指定键的值,如果值不在字典中返回默认值
dict1[i] = dict1.get(i, 0) + 1
# 创建优先队列 维护一个大小为k的最小堆,使得堆中的元素即为前k个高频元素
# heapq.heappush(heap,item) # heap为定义堆,item 增加的元素
# heapq.heappop(heap) # 删除最小的值
# heapq.heapreplace(heap, item) #删除最小元素值,添加新的元素值
pq = []
for key, value in dict1.items():
if len(pq) < k:
heapq.heappush(pq, (value, key))
elif value > pq[0][0]:
heapq.heapreplace(pq, (value, key))
res = []
while pq:
res.append(heapq.heappop(pq)[1])
return res
# 桶排序
# 时间复杂度:O(n),其中 n表示数组的长度。
# 空间复杂度:O(n)
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
# 创建字典哈希表,无序 key,value 元素,频率
#生成字典映射
dict1 = {}
for i in nums:
dict1[i] = dict1.get(i,0) + 1
max_value = max(dict1.values())
# 用数组表示每个桶,从0到最大出现次数,一共是(max_value+1)个桶
tong = [[] for i in range(max_value+1)]
for key, value in dict1.items():
# 将元素key放入桶中
tong[value].append(key)
res = []
# 按桶索引排序
for i in range(max_value,-1,-1):
# extend() 函数用于在列表末尾一次性追加另一个序列中的多个值
if tong[i]:
res.extend(tong[i])
if len(res) == k:
break
return res
剑指offer:用两个栈来实现队列
问题描述:用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
解法:一个栈处理节点的放入,一个栈处理输出节点,初始化为空。入队时,直接压入;出队时,判断pop_stack是否为空,如不为空,直接弹出栈顶元素,如为空,将push_stack的元素逐个压入pop_stack,然后弹出栈顶元素。
时间复杂度:O(1)
# -*- coding:utf-8 -*-
class Solution:
def __init__(self):
self.push_stack = []
self.pop_stack = []
def push(self, node):
self.push_stack.append(node)
def pop(self):
if len(self.pop_stack) == 0:
# while 而不是if 需要循环遍历!!!
while len(self.push_stack) > 0:
self.pop_stack.append(self.push_stack.pop())
head = self.pop_stack.pop()
return head
面试问题:用两个队列来实现栈
解法:一个队列放入,一个队列输出。因为栈是后入先出,所以把q1的元素依次删除并插入q2,再删除最后一个元素。然后q1赋值为q2,q2初始化为空,这样才能不断删除。
# 用两个队列实现一个栈
class Stack:
def __init__(self):
self.q1 = []
self.q2 = []
def append(self, nums):
self.q1.append(nums)
return self.q1
def pop(self):
while len(self.q1) != 1:
self.q2.append(self.q1.pop(0))
stack_top = self.q1.pop(0)
self.q1 = self.q2
self.q2 = []
return stack_top
a = Stack()
a.append(1)
a.append(2)
a.append(3)
a.append(4)
print(a.append(5))
print(a.pop())
print(a.pop())
print(a.append(6))
剑指offer:包含min函数的栈
问题描述:定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))
解法:数据栈data_stack、辅助栈min_stack,元素入栈时,辅助栈进入最小值(与栈顶元素比较)
时间复杂度:O(1)
# -*- coding:utf-8 -*-
class Solution:
def __init__(self):
self.data_stack = []
self.min_stack = []
def push(self, node):
self.data_stack.append(node)
if len(self.min_stack) == 0 or self.min_stack[-1] > node:
self.min_stack.append(node)
else:
self.min_stack.append(self.min_stack[-1])
def pop(self):
self.data_stack.pop()
self.min_stack.pop()
def top(self):
return self.data_stack[-1]
def min(self):
return self.min_stack[-1]
剑指offer:栈的压入、弹出序列
问题描述:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。压入序列1,2,3,4,5,弹出序列4,5,3,2,1,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
解法:建立一个辅助栈,如果栈顶数字是弹出数字,则弹出;如果栈顶数字不是弹出数字,则压入栈。直到所有数字入栈且找不到下一个弹出的数字,则不是弹出序列。
时间复杂度:O(1)
# -*- coding:utf-8 -*-
class Solution:
def IsPopOrder(self, pushV, popV):
stack = []
j = 0
# 把压入序列的元素放入栈
for i in range(len(pushV)):
# 如果压入元素==弹出元素,则不需要压入
if pushV[i] == popV[j]:
j += 1
else:
stack.append(pushV[i])
# 栈里的元素依次弹出,与弹出元素比较
for i in range(len(stack)):
if stack[-1] == popV[j]:
j += 1
stack.pop()
else:
return False
return True
leetcode:94. 二叉树的中序遍历
问题描述:给定一个二叉树,返回它的中序 遍历。
解法:不断压入根节点的所有左节点直到当前节点为空,然后弹出节点,并且压入节点的右节点。如果有节点有左子树,则继续压入。
时间复杂度:O(n)
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
# cur当做指针
cur = root
res = []
stack = []
while cur or stack:
# 把左子树压入栈中
while cur:
stack.append(cur)
cur = cur.left
# 输出栈顶元素
cur = stack.pop()
res.append(cur.val)
# 检查右子树
cur = cur.right
return res
leetcode:144. 二叉树的前序遍历
问题描述:给定一个二叉树,返回它的 前序 遍历。
解法:从根节点开始,每次迭代弹出当前栈顶元素,并将其孩子节点压入栈中,先压右孩子再压左孩子。
(与前序遍历的唯一区别是先右后左)
时间复杂度:O(n)
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
if root is None:
return []
res = []
stack = [root, ]
while stack:
cur = stack.pop()
res.append(cur.val)
if cur.right:
stack.append(cur.right)
if cur.left:
stack.append(cur.left)
return res
leetcode:145. 二叉树的后序遍历
问题描述:给定一个二叉树,返回它的 后序 遍历。
解法:从根节点开始依次迭代,弹出栈顶元素输出到输出列表中,然后依次压入它的所有孩子节点,按照从上到下、从左至右的顺序依次压入栈中,最后结果返回回文序列。后序遍历:左右根,压入根-弹出根-压入左-压入右-弹出右-弹出左,得到根-右-左,回文则是左-右-根。(与前序遍历的唯一区别是先左后右,再回文)
时间复杂度:O(n)
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
if root is None:
return []
res = []
stack = [root,]
while stack:
cur = stack.pop()
res.append(cur.val)
if cur.left:
stack.append(cur.left)
if cur.right:
stack.append(cur.right)
return res[::-1]
剑指offer:最小的K个数
问题描述:输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
解法:放入最大堆中k个元素,如果当前元素小于最大值,则替换
时间复杂度:O(n) or O(n * logk)
# -*- coding:utf-8 -*-
# 方法一:直接通过堆返回前k个最小数
import heapq
# 堆:集合中找出最大或最小的N个元素
# print(heapq.nlargest(3,nums)) heapq.nsmallest(3,nums)
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
if k > len(tinput):
return []
res = heapq.nsmallest(k, tinput)
res.sort()
return res
# -*- coding:utf-8 -*-
# 方法二:创建长度为k的最大堆,因为python实现的是最小堆,这里用列表排序替代
import heapq
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
if k <= 0 or k > len(tinput):
return []
res = []
for i in range(len(tinput)):
if len(res) < k:
res.append(tinput[i])
else:
res.sort()
if tinput[i] < res[-1]:
res[-1] = tinput[i]
res.sort()
return res
剑指offer:
问题描述:
解法:
时间复杂度:O(n) or O(n * logk)