难度简单0
给你一个下标从 0 开始的数组 nums
,数组长度为 n
。
nums
的 不同元素数目差 数组可以用一个长度为 n
的数组 diff
表示,其中 diff[i]
等于前缀 nums[0, ..., i]
中不同元素的数目 减去 后缀 nums[i + 1, ..., n - 1]
中不同元素的数目。
返回 nums
的 不同元素数目差 数组。
注意 nums[i, ..., j]
表示 nums
的一个从下标 i
开始到下标 j
结束的子数组(包含下标 i
和 j
对应元素)。特别需要说明的是,如果 i > j
,则 nums[i, ..., j]
表示一个空子数组。
示例 1:
输入:nums = [1,2,3,4,5]
输出:[-3,-1,1,3,5]
解释:
对于 i = 0,前缀中有 1 个不同的元素,而在后缀中有 4 个不同的元素。因此,diff[0] = 1 - 4 = -3 。
对于 i = 1,前缀中有 2 个不同的元素,而在后缀中有 3 个不同的元素。因此,diff[1] = 2 - 3 = -1 。
对于 i = 2,前缀中有 3 个不同的元素,而在后缀中有 2 个不同的元素。因此,diff[2] = 3 - 2 = 1 。
对于 i = 3,前缀中有 4 个不同的元素,而在后缀中有 1 个不同的元素。因此,diff[3] = 4 - 1 = 3 。
对于 i = 4,前缀中有 5 个不同的元素,而在后缀中有 0 个不同的元素。因此,diff[4] = 5 - 0 = 5 。
示例 2:
输入:nums = [3,2,3,4,2]
输出:[-2,-1,0,2,3]
解释:
对于 i = 0,前缀中有 1 个不同的元素,而在后缀中有 3 个不同的元素。因此,diff[0] = 1 - 3 = -2 。
对于 i = 1,前缀中有 2 个不同的元素,而在后缀中有 3 个不同的元素。因此,diff[1] = 2 - 3 = -1 。
对于 i = 2,前缀中有 2 个不同的元素,而在后缀中有 2 个不同的元素。因此,diff[2] = 2 - 2 = 0 。
对于 i = 3,前缀中有 3 个不同的元素,而在后缀中有 1 个不同的元素。因此,diff[3] = 3 - 1 = 2 。
对于 i = 4,前缀中有 3 个不同的元素,而在后缀中有 0 个不同的元素。因此,diff[4] = 3 - 0 = 3 。
提示:
1 <= n == nums.length <= 50
1 <= nums[i] <= 50
python
class Solution:
def distinctDifferenceArray(self, nums: List[int]) -> List[int]:
# 预处理后缀不同元素的个数
n = len(nums)
suf = [0] * (n+1)
s = set()
for i in range(n-1, -1, -1):
s.add(nums[i])
suf[i] = len(s)
print(suf)
ans = [0] * n
s = set()
for i in range(n):
s.add(nums[i])
# 这里减去suf[i+1] 因为题目要求后缀不包括i
ans[i] = len(s) - suf[i+1]
return ans
比赛时写的暴力
class Solution {
public int[] distinctDifferenceArray(int[] nums) {
int n = nums.length;
int[] res = new int[n];
Set<Integer> pre = new HashSet<>();
for(int i = 0; i < n; i++){
pre.add(nums[i]);
Set<Integer> suf = new HashSet<>();
for(int j = i+1; j < n; j++){
suf.add(nums[j]);
}
res[i] = pre.size() - suf.size();
}
return res;
}
}
难度中等2
请你设计并实现一个能够对其中的值进行跟踪的数据结构,并支持对频率相关查询进行应答。
实现 FrequencyTracker
类:
FrequencyTracker()
:使用一个空数组初始化 FrequencyTracker
对象。void add(int number)
:添加一个 number
到数据结构中。void deleteOne(int number)
:从数据结构中删除一个 number
。数据结构 可能不包含 number
,在这种情况下不删除任何内容。bool hasFrequency(int frequency)
: 如果数据结构中存在出现 frequency
次的数字,则返回 true
,否则返回 false
。示例 1:
输入
["FrequencyTracker", "add", "add", "hasFrequency"]
[[], [3], [3], [2]]
输出
[null, null, null, true]
解释
FrequencyTracker frequencyTracker = new FrequencyTracker();
frequencyTracker.add(3); // 数据结构现在包含 [3]
frequencyTracker.add(3); // 数据结构现在包含 [3, 3]
frequencyTracker.hasFrequency(2); // 返回 true ,因为 3 出现 2 次
示例 2:
输入
["FrequencyTracker", "add", "deleteOne", "hasFrequency"]
[[], [1], [1], [1]]
输出
[null, null, null, false]
解释
FrequencyTracker frequencyTracker = new FrequencyTracker();
frequencyTracker.add(1); // 数据结构现在包含 [1]
frequencyTracker.deleteOne(1); // 数据结构现在为空 []
frequencyTracker.hasFrequency(1); // 返回 false ,因为数据结构为空
示例 3:
输入
["FrequencyTracker", "hasFrequency", "add", "hasFrequency"]
[[], [2], [3], [1]]
输出
[null, false, null, true]
解释
FrequencyTracker frequencyTracker = new FrequencyTracker();
frequencyTracker.hasFrequency(2); // 返回 false ,因为数据结构为空
frequencyTracker.add(3); // 数据结构现在包含 [3]
frequencyTracker.hasFrequency(1); // 返回 true ,因为 3 出现 1 次
提示:
1 <= number <= 105
1 <= frequency <= 105
add
、deleteOne
和 hasFrequency
共计 2 * 105
次一个哈希表记录数字和该数字出现频率,另一个哈希表记录出现频率的频率
class FrequencyTracker {
Map<Integer, Integer> map; // num, freq
Map<Integer, Integer> fre; // freq, cnt
public FrequencyTracker() {
map = new HashMap<>();
fre = new HashMap<>();
}
public void add(int number) {
if(map.containsKey(number)){
int f = map.get(number);
map.put(number, f+1);
fre.put(f, fre.get(f) - 1);
fre.put(f+1, fre.getOrDefault(f+1, 0) + 1);
}else{
map.put(number, 1);
fre.put(1, fre.getOrDefault(1, 0) + 1);
}
}
public void deleteOne(int number) {
if(!map.containsKey(number) || map.get(number) == 0)
return;
int f = map.get(number);
map.put(number, f-1);
fre.put(f, fre.get(f) - 1);
fre.put(f-1, fre.getOrDefault(f-1, 0) + 1);
}
public boolean hasFrequency(int frequency) {
return fre.containsKey(frequency) && fre.get(frequency) != 0;
}
}
难度中等0
给你一个下标从 0 开始、长度为 n
的数组 nums
。一开始,所有元素都是 未染色 (值为 0
)的。
给你一个二维整数数组 queries
,其中 queries[i] = [indexi, colori]
。
对于每个操作,你需要将数组 nums
中下标为 indexi
的格子染色为 colori
。
请你返回一个长度与 queries
相等的数组 answer
,其中 answer[i]
是前 i
个操作 之后 ,相邻元素颜色相同的数目。
更正式的,answer[i]
是执行完前 i
个操作后,0 <= j < n - 1
的下标 j
中,满足 nums[j] == nums[j + 1]
且 nums[j] != 0
的数目。
示例 1:
输入:n = 4, queries = [[0,2],[1,2],[3,1],[1,1],[2,1]]
输出:[0,1,1,0,2]
解释:一开始数组 nums = [0,0,0,0] ,0 表示数组中还没染色的元素。
- 第 1 个操作后,nums = [2,0,0,0] 。相邻元素颜色相同的数目为 0 。
- 第 2 个操作后,nums = [2,2,0,0] 。相邻元素颜色相同的数目为 1 。
- 第 3 个操作后,nums = [2,2,0,1] 。相邻元素颜色相同的数目为 1 。
- 第 4 个操作后,nums = [2,1,0,1] 。相邻元素颜色相同的数目为 0 。
- 第 5 个操作后,nums = [2,1,1,1] 。相邻元素颜色相同的数目为 2 。
示例 2:
输入:n = 1, queries = [[0,100000]]
输出:[0]
解释:一开始数组 nums = [0] ,0 表示数组中还没染色的元素。
- 第 1 个操作后,nums = [100000] 。相邻元素颜色相同的数目为 0 。
提示:
1 <= n <= 105
1 <= queries.length <= 105
queries[i].length == 2
0 <= indexi <= n - 1
1 <= colori <= 105
核心在于:对i
位置的修改只会影响到i-1
和i
、i
和i+1
两个位置的对答案的贡献
我们把“改掉 color[idx]
”分成两步:第一步是把 color[idx]
原来的颜色去掉,第二步是把 color[idx]
新的颜色加上。
color[idx]
,只会影响两对相邻元素,即 (color[idx - 1], color[idx])
和 (color[idx], color[idx + 1])
。因此检查这两对元素的变化情况即可。class Solution {
public int[] colorTheArray(int n, int[][] queries) {
int sum = 0;
int[] res = new int[queries.length];
int[] arr = new int[n];
for(int i = 0; i < queries.length; i++){
int pos = queries[i][0], val = queries[i][1];
// 把 A[idx] 原来的颜色去掉,检查受影响的两对元素原来是不是相等的
if(pos > 0 && arr[pos-1] != 0 && arr[pos] != 0 && arr[pos-1] == arr[pos]){
sum -= 1;
}
if(pos < n-1 && arr[pos+1] != 0 && arr[pos] != 0 && arr[pos] == arr[pos+1]){
sum -= 1;
}
// 把 A[idx] 新的颜色加上,检查受影响的两对元素现在是不是相等的
arr[pos] = val;
if(pos > 0 && arr[pos-1] != 0 && arr[pos] != 0 && arr[pos-1] == arr[pos]) sum += 1;
if(pos < n-1 && arr[pos+1] != 0 && arr[pos] != 0 && arr[pos] == arr[pos+1]) sum += 1;
res[i] = sum;
}
return res;
}
}
难度中等1
给你一个整数 n
表示一棵 满二叉树 里面节点的数目,节点编号从 1
到 n
。根节点编号为 1
,树中每个非叶子节点 i
都有两个孩子,分别是左孩子 2 * i
和右孩子 2 * i + 1
。
树中每个节点都有一个值,用下标从 0 开始、长度为 n
的整数数组 cost
表示,其中 cost[i]
是第 i + 1
个节点的值。每次操作,你可以将树中 任意 节点的值 增加 1
。你可以执行操作 任意 次。
你的目标是让根到每一个 叶子结点 的路径值相等。请你返回 最少 需要执行增加操作多少次。
注意:
示例 1:
输入:n = 7, cost = [1,5,2,2,3,3,1]
输出:6
解释:我们执行以下的增加操作:
- 将节点 4 的值增加一次。
- 将节点 3 的值增加三次。
- 将节点 7 的值增加两次。
从根到叶子的每一条路径值都为 9 。
总共增加次数为 1 + 3 + 2 = 6 。
这是最小的答案。
示例 2:
输入:n = 3, cost = [5,3,3]
输出:0
解释:两条路径已经有相等的路径值,所以不需要执行任何增加操作。
提示:
3 <= n <= 105
n + 1
是 2
的幂cost.length == n
1 <= cost[i] <= 104
题解:https://leetcode.cn/problems/make-costs-of-paths-equal-in-a-binary-tree/solution/tan-xin-jian-ji-xie-fa-pythonjavacgo-by-5svh1/
考虑根到两个互为兄弟节点的叶子的两条路径。
由于这两条路径除了叶子节点不一样,其余节点都一样,所以为了让这两条路径的路径和相等,必须修改叶子节点的值。 可不可以修改根节点的值使两条路径相等呢?不行,因为每个左右子树都要单独对齐,他们之间的差距不能靠上层弥补
设叶子节点的值分别为 x 和 y,假设 x <= y,是否需要同时增加 x 和 y ?
这是不需要的,把 x 增加 y - x 就行,因为我们可以增加它们的祖先节点的值,使得它们俩的路径和与其它的路径和相等,这样可以节省操作次数
**对于不是叶子的兄弟节点,又要如何比较和计算呢?**和上面的分析一样,从根到当前节点的路径,除了这两个兄弟节点不一样,其余节点都一样。所以把路径和从叶子往上传,这样就可以比较了。
class Solution:
def minIncrements(self, n: int, cost: List[int]) -> int:
for i in range(2, n+1):
cost[i - 1] += cost[i // 2 - 1] # 累加路径和
ans = 0
for i in range(n // 2, 0 , -1):
ans += abs(cost[i * 2 - 1] - cost[i * 2]) # 把两个子节点变成一样的
cost[i-1] = max(cost[i * 2 - 1], cost[i * 2]) # 把子节点的路径和返回给当前节点
return ans
也可以直接一次遍历上计算和累加路径和
class Solution:
def minIncrements(self, n: int, cost: List[int]) -> int:
ans = 0
for i in range(n // 2, 0 , -1):
ans += abs(cost[i * 2 - 1] - cost[i * 2]) # 把两个子节点变成一样的
cost[i-1] += max(cost[i * 2 - 1], cost[i * 2]) # 把子节点的路径和返回给当前节点
return ans