2670. 找出不同元素数目差数组
题目描述
给你一个下标从 0 开始的数组 nums ,数组长度为 n 。nums 的 不同元素数目差 数组可以用一个长度为 n 的数组 diff 表示,其中 diff [i] 等于前缀 nums [0, …, i] 中不同元素的数目 减去 后缀 nums [i + 1, …, n - 1] 中不同元素的数目。
返回 nums 的 不同元素数目差 数组。
解题思路
由于数据范围很小,n <= 50,我们可以直接枚举每个位置 i 作为前后缀的分界点,然后分别统计前缀和后缀中不同元素的个数,用集合或者哈希表来维护。时间复杂度为 O(n^2)。
另一种优化的方法是,我们可以用一个哈希表来记录每个元素的出现次数,然后从左到右遍历数组,在每个位置 i ,我们将 nums[i] 的次数减一,这样就相当于将 nums[i] 从前缀移动到后缀。同时,我们用两个变量来记录前缀和后缀中不同元素的个数,如果某个元素在前缀中出现次数从 1 变为 0 ,则前缀中不同元素个数减一;如果某个元素在后缀中出现次数从 0 变为 1 ,则后缀中不同元素个数加一。这样就可以在 O(n) 的时间内得到 diff 数组。
Python代码
class Solution:
def distinctDifferenceArray(self, nums: List[int]) -> List[int]:
# 方法一:暴力枚举
# n = len(nums)
# diff = []
# for i in range(n):
# left = set(nums[:i+1]) # 前缀中不同元素的集合
# right = set(nums[i+1:]) # 后缀中不同元素的集合
# diff.append(len(left) - len(right)) # 计算差值
# return diff
# 方法二:哈希表+前后缀统计
n = len(nums)
diff = []
count = {} # 记录每个元素的出现次数
for num in nums:
count[num] = count.get(num, 0) + 1
left = n # 前缀中不同元素的个数
right = 0 # 后缀中不同元素的个数
for i in range(n):
num = nums[i]
count[num] -= 1 # 将当前元素从前缀移动到后缀
if count[num] == 0: # 如果前缀中该元素消失,则前缀中不同元素个数减一
left -= 1
if count[num] == 1: # 如果后缀中该元素出现,则后缀中不同元素个数加一
right += 1
diff.append(left - right) # 计算差值
return diff
2671. 频率跟踪器
题目描述
实现一个 FrequencyTracker 类,该类支持以下操作:
- FrequencyTracker():初始化 FrequencyTracker 类。
- void add(int number):将 number 加入到数据结构中。
- void deleteOne(int number):如果 number 存在于数据结构中,则删除其中一个 number 。如果 number 不在数据结构中,则什么也不做。
- bool hasFrequency(int frequency):如果存在至少一个 number ,其出现频率等于 frequency ,则返回 true 。否则返回 false 。
解题思路
我们需要设计一个数据结构来实现两种操作:
- 实现对某个元素的频率 ±1
- 查询某个频率的不同元素个数
我们可以利用一个哈希表来记录每个元素的频率,再建立一个哈希表来记录频率到元素个数的映射。当我们对某个元素进行 add 或 deleteOne 操作时,我们需要更新其频率,并同时更新其原频率和新频率对应的元素个数。当我们进行 hasFrequency 操作时,我们只需要查询频率到元素个数的映射是否存在即可。
由于数据范围为 1 <= number <= 10^5 和 1 <= frequency <= 10^5 ,我们可以使用两个长度为10^5 的数组来替代哈希表,以避免哈希冲突和额外空间开销。时间复杂度为 O(1)。
Python代码
class FrequencyTracker:
def __init__(self):
self.freq = [0] * (10**5 + 1) # 记录每个元素的频率
self.freq2num = [0] * (2 * 10**5 + 1) # 记录每个频率对应的不同元素个数
def add(self, number: int) -> None:
f = self.freq[number] # 获取当前元素的频率
if f > 0: # 如果当前频率大于0,则原频率对应的元素个数减一
self.freq2num[f] -= 1
self.freq[number] += 1 # 更新当前元素的频率
self.freq2num[self.freq[number]] += 1 # 新频率对应的元素个数加一
def deleteOne(self, number: int) -> None:
if self.freq[number] == 0: # 如果当前元素不存在,则直接返回
return
f = self.freq[number] # 获取当前元素的频率
self.freq2num[f] -= 1 # 原频率对应的元素个数减一
self.freq[number] -= 1 # 更新当前元素的频率
if self.freq[number] > 0: # 如果新频率大于0,则新频率对应的元素个数加一
self.freq2num[self.freq[number]] += 1
def hasFrequency(self, frequency: int) -> bool:
return self.freq2num[frequency] > 0 # 判断是否存在该频率对应的不同元素
2672. 有相同颜色的相邻元素数目
题目描述
给你一个下标从0开始、长度为n且包含数字范围在[0,n-1]内(包含边界)且没有重复数字的数组nums。你想给nums重新染色(即修改nums),使得nums满足以下条件:
- 对于所有下标i(i>0),nums[i-1]!=nums[i]
- 对于所有下标i(i<n),nums[i]!=nums[i+1]
请你返回满足条件后有相邻颜色相同(即nums[i]==nums[i+1])数字对数量最多可能是多少。
解题思路
关键思想是先消除前一次染色带来的影响,再染新的色,先分手再找新的(不是)。如果该点为未染色,则不需要消除。
我们可以用一个变量 ans 来记录当前有相同颜色的相邻元素数目,初始为 0 。然后我们从左到右遍历数组,在每个位置 i ,我们先判断 nums[i] 是否和 nums[i-1] 相同,如果相同,则说明前一次染色导致了相邻元素相同,我们需要将 ans 减一。然后我们判断 nums[i] 是否和 nums[i+1] 相同,如果相同,则说明我们可以染一个新的色来使得相邻元素相同,我们需要将 ans 加一。最后我们将 nums[i] 染成一个新的色,可以随便选一个不和 nums[i-1] 和 nums[i+1] 相同的色即可。时间复杂度为 O(n)。
Python代码
class Solution:
def colorTheArray(self, n: int, queries: List[List[int]]) -> List[int]:
nums = [0] * n # 初始化数组
ans = 0 # 记录有相同颜色的相邻元素数目
res = [] # 记录每次查询的结果
for i, color in queries: # 遍历每个查询
if nums[i] > 0 and i - 1 >= 0 and nums[i - 1] == nums[i]: # 如果当前位置已经染色且和左边相同,则消除影响
ans -= 1
if nums[i] > 0 and i + 1 < n and nums[i + 1] == nums[i]: # 如果当前位置已经染色且和右边相同,则消除影响
ans -= 1
if i - 1 >= 0 and nums[i - 1] == color: # 如果左边和要染的颜色相同,则增加影响
ans += 1
if i + 1 < n and nums[i + 1] == color: # 如果右边和要染的颜色相同,则增加影响
ans += 1
nums[i] = color # 染新的颜色,可以随便选一个不和左右相同的颜色即可
res.append(ans) # 记录结果
return res
2673. 使二叉树所有路径值相等的最小代价
题目描述
给你一个下标从0开始、长度为n且包含数字范围在[0,n-1]内(包含边界)且没有重复数字的数组cost。你想把cost转换成一棵满二叉树,使得二叉树中所有从根节点到叶子节点的路径值都相等。
满二叉树是指每个非叶子节点都有两个子节点的二叉树。路径值是指从根节点到叶子节点经过的所有节点值之和。
你可以通过以下操作来转换cost:
- 将任意非叶子节点cost[i]加上任意正整数x。
- 将任意非叶子节点cost[i]减去任意正整数x。
请你返回使二叉树中所有从根节点到叶子节点的路径值都相等所需操作的最小代价。
解题思路
如果已经实现了二叉树所有路径值相等,那么对于根节点来说,以他的左右儿子为根节点的子树对应的路径值也应该是相等的,思考不断向下递归下去,每个非叶子节点的左右儿子对应的子树的路径值也必须相等(满二叉树的非叶子节点一定有两个子节点)。
那么我们从下向上,先找叶节点,其中互为兄弟节点的叶节点的值应该为他们中较大的那个,之后这个值应该传递给他们的父节点(因为父节点和父节点的兄弟不相等应该由父节点去加,这样次数比两兄弟加的次数更少)。模拟即可。
注意cost下标从0开始。
Python代码
class Solution:
def minIncrements(self, n: int, cost: List[int]) -> int:
res = 0 # 记录最小代价
for i in range(n - 1, 0, -2): # 从下向上遍历非叶子节点
t = max(cost[i], cost[i - 1]) # 取两个兄弟节点中较大的值作为路径值
res += t - cost[i] + t - cost[i - 1] # 计算代价
cost[(i - 1) // 2] += t # 将路径值传递给父节点
return res # 返回结果