本次学习笔记将分析LeetCode周赛第365场的题目,并提供相关的解题思路和代码实现。这场周赛包括四道题目,分别讨论每道题的解法。
声明:
这篇学习笔记是在学习了灵神对于这次周赛的解答然后整理出来的,大家如果不满意可以去看灵神写的解析:
题解+多种语言代码:
Q1 https://leetcode.cn/problems/maximum-value-of-an-ordered-triplet-i/solution/on-zuo-fa-by-endlesscheng-01x5/
Q2 https://leetcode.cn/problems/maximum-value-of-an-ordered-triplet-ii/solution/mei-ju-jzhao-qian-hou-zui-da-zhi-pythonj-um8q/
Q3 https://leetcode.cn/problems/minimum-size-subarray-in-infinite-array/solution/hua-dong-chuang-kou-on-shi-jian-o1-kong-cqawc/
Q4 https://leetcode.cn/problems/count-visited-nodes-in-a-directed-graph/solution/nei-xiang-ji-huan-shu-pythonjavacgo-by-e-zrzh/
第一,二题:最大三元组值
题目要求:
给你一个下标从 0 开始的整数数组 nums
。
请你从所有满足 i < j < k
的下标三元组 (i, j, k)
中,找出并返回下标三元组的最大值。如果所有满足条件的三元组的值都是负数,则返回 0
。
下标三元组 (i, j, k)
的值等于 (nums[i] - nums[j]) * nums[k]
。
解题思路:
第一,二题:
第二题与第一题差别在于 两者输入的数组的大小不一样,一个是1000,一个是1000000,因此使用暴力搜索会超时。
class Solution {
public long maximumTripletValue(int[] nums) {
/*灵神-version02:维护nums[i]-nums[j]的最大值(简称:枚举k) */
int maxDiff=0, preMax=0;
long ans=0;
for(int x:nums){
ans=Math.max(ans, (long) maxDiff*x);
maxDiff=Math.max(maxDiff,preMax-x);
preMax=Math.max(preMax,x);
}
return ans;
}
}
/*自己-version:暴力枚举*/
/* int n=nums.length;
long ans=0;
for(int i=0;i<n-2;i++){
for(int j=i+1;j<n-1;j++){
for(int k=j+1;k<n;k++){
long tmp=nums[i]-nums[j];
tmp*=nums[k];
if(tmp>ans){
ans=tmp;
}
}
}
}
return ans;*/
/*灵神-version01:处理前后缀数组(维护i和k的最大值)(简称:枚举j)*/
/*int n=nums.length;
int[] sufMax=new int[n+1];
for(int i=n-1;i>1;i--){
sufMax[i]=Math.max(sufMax[i+1], nums[i]);
}
long ans=0;
int preMax=nums[0];
for(int j=1;j<n-1;j++){
ans=Math.max(ans,(long) (preMax-nums[j])*sufMax[j+1]);
preMax=Math.max(preMax,nums[j]);
}
return ans;*/
第三题:最短目标子数组
题目要求:
给你一个下标从 0 开始的数组 nums
和一个整数 target
。
下标从 0 开始的数组 infinite_nums
是通过无限地将 nums 的元素追加到自己之后生成的。
请你从 infinite_nums
中找出满足 元素和 等于 target
的 最短 子数组,并返回该子数组的长度。如果不存在满足条件的子数组,返回 -1
。
解题思路:
由于无限数组,所以需要考虑符合要求的答案数组的长度可能会比nums的长度n要大,仔细想想我们可以发现即使答案数组的长度再大(数组连续),我们都可以把其中大于长度n的部分去掉,只要在返回时加上这部分,写成公式可以得到target/total:(total是nums数组的元素总和),剩下的是target%total,那么问题可以转化为在(nums+nums)这个 两个nums拼成的数组中寻找元素之和为target%total的最小子数组长度,由于都是正整数,而且随着数组的某一边界缩小,对应的元素之和也在减小,满足单调性,故使用滑动数组方法(即同向双指针)。
public int minSizeSubarray(int[] nums, int target) {
long total = 0;
for (int x : nums) total += x;
int n = nums.length;
int ans = Integer.MAX_VALUE;
int left = 0;
long sum = 0;
for (int right = 0; right < n * 2; right++) {
sum += nums[right % n];
while (sum > target % total) {
sum -= nums[left++ % n];
}
if (sum == target % total) {
ans = Math.min(ans, right - left + 1);
}
}
return ans == Integer.MAX_VALUE ? -1 : ans + (int) (target / total) * n;
}
第四题:统计内向基环树中的节点
题目要求:现有一个有向图,其中包含 n
个节点,节点编号从 0
到 n - 1
。此外,该图还包含了 n
条有向边。
给你一个下标从 0 开始的数组 edges
,其中 edges[i]
表示存在一条从节点 i
到节点 edges[i]
的边。
想象在图上发生以下过程:
- 你从节点
x
开始,通过边访问其他节点,直到你在 此过程 中再次访问到之前已经访问过的节点。
返回数组 answer
作为答案,其中 answer[i]
表示如果从节点 i
开始执行该过程,你可以访问到的不同节点数。
解题思路:内向基环树(问题): n个结点n条边——有环(因为树是n-1条边) 每个点都有一条出边,而一个点可能有多条入边 本题特殊点:可能是内向基环森林 解决方案: 1.使用拓扑排序,减掉g上的所有树枝 2.收集在基环上的点 3.遍历每个与基环上的点相连的非基环上的点 所组成的图(将原来的图的边全部取反——反图)
class Solution {
/*内向基环树(问题):
n个结点n条边——有环(因为树是n-1条边)
每个点都有一条出边,而一个点可能有多条入边
本题特殊点:可能是内向基环森林
解决方案:
1.使用拓扑排序,减掉g上的所有树枝
2.收集在基环上的点
3.遍历每个与基环上的点相连的非基环上的点 所组成的图(将原来的图的边全部取反——反图)
*/
public int[] countVisitedNodes(List<Integer> edges) {
int[] g=edges.stream().mapToInt(i->i).toArray();
int n=g.length;
List<Integer>[] rg=new ArrayList[n];//反图
Arrays.setAll(rg,e-> new ArrayList<>());
int[] deg=new int[n];
for(int x=0;x<n;x++){
int y=g[x];
rg[y].add(x);
deg[y]++;
}
//拓扑排序,减掉g上的所有树枝
//拓扑排序后 deg的值为 1 的点必定在基环上,为 0 的点必定在树枝上
var q=new ArrayDeque<Integer>();
for(int i=0;i<n;i++){
if(deg[i]==0){
q.add(i);
}
}
while(!q.isEmpty()){
int x=q.poll();
int y=g[x];
if(--deg[y]==0){
q.add(y);
}
}
int[] ans=new int[n];
for(int i=0;i<n;i++){
if(deg[i]<=0){
continue;
}
var ring=new ArrayList<Integer>();
for(int x=i;;x=g[x]){
deg[x] = -1;// 将基环上的点的入度标记为 -1,避免重复访问
ring.add(x);// 收集在基环上的点
if(g[x]==i){
break;
}
}
for(int r:ring){
rdfs(r,ring.size(),rg,deg,ans);// 为方便计算,以 ring.size() 作为初始深度
}
}
return ans;
}
//在反图上遍历树枝
private void rdfs(int x,int depth,List<Integer>[] rg,int[] deg,int[] ans){
ans[x] = depth;
for(int y:rg[x]){
if(deg[y]==0){//在树枝上的点在拓扑排序后, 入度均为0
rdfs(y,depth+1,rg,deg,ans);
}
}
}
}
以上就是对LeetCode周赛第365场题目的解析和分析。希望这些解题思路能够帮助你更好地理解和解决类似的问题。