记录 LeetCode 第 365 场周赛
是谁在国庆节还在打比赛?原来是我啊!本次周赛 参加比赛的人都少了很多,而且题目也简单不少,顺利 AK。
战局详情
排名 | 用户名 | 得分 | 完成时间 | 题目1(3) | 题目2(4) | 题目3(5) | 题目4(6) |
---|---|---|---|---|---|---|---|
271 / 2909 | Juruoer | 18 | 1:21:22 | 0:04:07 | 0:14:14 1 | 0:33:48 1 | 1:11:22 |
题目及解答
有序三元组中的最大值 I
给你一个下标从 0 开始的整数数组 nums
。
请你从所有满足 i < j < k
的下标三元组 (i, j, k)
中,找出并返回下标三元组的最大值。如果所有满足条件的三元组的值都是负数,则返回 0
。
下标三元组 (i, j, k)
的值等于 (nums[i] - nums[j]) * nums[k]
。
提示:
3 <= nums.length <= 100
1 <= nums[i] <= 1e6
示例一:
1 | 输入:nums = [12,6,1,2,7] |
示例二:
1 | 输入:nums = [1,2,3] |
解决方案:
由于数据量较小,直接暴力即可。
代码:
1 | class Solution { |
时间复杂度: ;空间复杂度: 。n
为 nums
的长度。
有序三元组中的最大值 II
给你一个下标从 0 开始的整数数组 nums
。
请你从所有满足 i < j < k
的下标三元组 (i, j, k)
中,找出并返回下标三元组的最大值。如果所有满足条件的三元组的值都是负数,则返回 0
。
下标三元组 (i, j, k)
的值等于 (nums[i] - nums[j]) * nums[k]
。
提示:
3 <= nums.length <= 1e5
1 <= nums[i] <= 1e6
示例一:
1 | 输入:nums = [12,6,1,2,7] |
示例二:
1 | 输入:nums = [1,10,3,4,19] |
示例三:
1 | 输入:nums = [1,2,3] |
解决方案:
模拟:
本题与上一题一模一样,但是数据量比较大,暴力一定超时。
观察 (nums[i] - nums[j]) * nums[k]
,需要获得此数值的最大值即需要获得 (nums[i] - nums[j])
的最大值和 nums[k]
的最大值,其中 j < k
。
我们可以先用数组 max_nums
来记录 nums
的最大值,其中 max_nums[i]
表示 nums[0]
~ nums[i]
的最大值。
然后用数组 max_subs
来记录两数只差的最大值,其中 max_subs[idx]
表示 nums[i] - nums[j]
的最大值,其中 i < j <= idx
。
最后计算出结果。
代码:
1 | class Solution { |
时间复杂度: ,空间复杂度: 。n
为数组 nums
的长度。
无限数组的最短子数组
给你一个下标从 0 开始的数组 nums
和一个整数 target
。
下标从 0 开始的数组 infinite_nums
是通过无限地将 nums 的元素追加到自己之后生成的。
请你从 infinite_nums
中找出满足 元素和 等于 target
的 最短 子数组,并返回该子数组的长度。如果不存在满足条件的子数组,返回 -1
。
提示:
1 <= nums.length <= 1e5
1 <= nums[i] <= 1e5
1 <= target <= 1e9
示例一:
1 | 输入:nums = [1,2,3], target = 5 |
示例二:
1 | 输入:nums = [1,1,1,2,3], target = 4 |
示例三:
1 | 输入:nums = [2,4,6,8], target = 3 |
解决方案:
设 nums
的元素和为 sum
。
一、 target
小于 sum
只需要将 nums
看作一个 环形数组,并找出此环形数组的一个最短连续子数组,使之元素和为 target
即可,我们可以使用一个临时数组 _nums
来模拟此环形数组,其中 _nums
为两个 nums
连接而成。
利用前缀和 pre_sum
来记录 _nums
的前缀和,若两个前缀和之差为 target
,那么即存在元素和为 target
的子数组,子数组长度即两前缀和所累加的元素的个数之差,需要找到最小的子数组的长度。
另外,如果使用双重循环来寻找两个差为 target
的前缀和会超时,我们需要一个哈希表来记录前缀和以及此前缀和所累加的元素的个数。
二、target
大于 sum
若存在子数组和为 target
,则此子数组包含 target / sum
个 nums
。并且我们需要按照上述一中的方法找到元素和为 target % sum
的子数组的最短长度,假设为 x
,则和为 target
的最短子数组长度为 targe / sum * n + x
,n
为 nums
的长度。
代码:
1 | class Solution { |
时间复杂度: ;空间复杂度: 。n
为 nums
的长度。
有向图访问计数
现有一个有向图,其中包含 n
个节点,节点编号从 0
到 n - 1
。此外,该图还包含了 n
条有向边。
给你一个下标从 0 开始的数组 edges
,其中 edges[i]
表示存在一条从节点 i
到节点 edges[i]
的边。
想象在图上发生以下过程:
- 你从节点
x
开始,通过边访问其他节点,直到你在 此过程 中再次访问到之前已经访问过的节点。
返回数组 answer
作为答案,其中 answer[i]
表示如果从节点 i
开始执行该过程,你可以访问到的不同节点数。
提示:
n == edges.length
2 <= n <= 1e5
0 <= edges[i] <= n - 1
edges[i] != i
示例一:
1 | 输入:edges = [1,2,0,0] |
示例二:
1 | 输入:edges = [1,2,3,4,0] |
解决方案:
深度优先搜索:
因为 n
个节点只有 n
条边,并且每个节点有且只有一个出边,所有整个图可以看作是若干个类似于示例一所示的最小连通子图,该子图由一个 x
个节点组成的环和由 y
个节点组成的长链,此长链最后指向环,其中 x > 0
, y >= 0
。
对每一个未计算出结果的节点,我们可以使用一次深度优先搜索来计算此节点的结果,以及此节点的后继结点及子孙节点的结果。为方便,我们以示例一为案例来表述计算过程,其他所有的情况可以看作是示例一的拓展(有更多或更少的节点组成的长链或者有更多或更少节点组成的环)。
假设我们先计算节点 3
的结果(虽然后续的代码中我们会从 0
开始计算结果,但这并不重要),那么他会先计算它的后继 0
的结果,并将自身(节点 3
)置为已访问,在递归计算的过程中,一定为访问到某个节点,此节点之前已经访问过(这是由 n
个节点 n
条边,且每个节点有且只有一个出边所决定的必然结果),在此案例中为节点 0
那么从第一次访问到 0
到再次访 0
过程中所访问的所有节点组成一个环,此环中所有节点的结果即环的长度,最终在计算节点 0
那一层的递归会返回节点 0
的结果,节点 3
在受到此结果后,将其加 1
即节点 3
的结果。
倘若有一个节点 4
指向节点 3
,并且节点 3
的结果已经计算出来了,那么在其递归计算节点 3
的时候就不用继续递归了,直接返回节点 3
的结果即可。
但是按照若如上方法,我们只能判断 0
节点是环内的节点,对于其他节点无法判断是环内还是环外,我们发现,只有对于环内的节点而言,其后继节点所返回的值是有效的,代表后继节点的结果,对于环内的节点而言,其结果都是环的长度,我们可以从第二个访问到相同节点开始,其递归返回的节点即此节点的相反数,用于代表此环的入口。那么,当某次递归计算时,发现其向下递归的返回值大于 0
,则表示此次计算的结果不在环内,返回值表示其后继节点的结果,否则表示当前节点在环内,返回值为环的起点,此节点的结果与环起点的结果相同,均为环的长度。对案例一而言,第二次访问 0
已经访问 1
和 2
时,返回的结果均为 0
,表示环的起点为 0
,而第一次访问 0
时,其返回的结果为 3
,表示 0
的结果为 3
,然后节点 3
的结果在此基础上加一,结果为 4
。
代码:
1 | class Solution { |
时间复杂度: ;空间复杂度: 。n
为节点个数。