LeetCode第365场周赛学习笔记(学习自灵茶山艾府 AKA 灵神)

本次学习笔记将分析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场题目的解析和分析。希望这些解题思路能够帮助你更好地理解和解决类似的问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值