LeetCode第284场周赛


6031. 找出数组中的所有 K 近邻下标

题目描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

暴力解

算法流程:

  • 遍历nums数组,统计所有nums[i]==key的下标i,并保存在keyList
  • 再次遍历nums数组
    • 对于每个nums[i],依次与keyList中记录的下标比较是否满足 i − i d x ≤ k i-idx \le k iidxk,若满足,则将i加入结果集并退出内层循环
class Solution {
    public List<Integer> findKDistantIndices(int[] nums, int key, int k) {
        List<Integer> res = new ArrayList<>();
        List<Integer> keyList = new ArrayList<>();
        int n = nums.length;
        
        for (int i = 0; i < n; i++) {
            if (nums[i] == key) {
                keyList.add(i);
            }
        }
        
        for (int i = 0; i < n; i++) {
            for (int idx : keyList) {
                if (Math.abs(i - idx) <= k) {
                    res.add(i);
                    break;
                }
            }
        }
        return res;
    }
}

5203. 统计可以提取的工件

题目描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

并查集+暴力模拟

首先对于第 i 个工件在子网格中的填埋情况 a r t i f a c t s [ i ] = [ r 1 i , c 1 i , r 2 i , c 2 i ] artifacts[i] = [r1_i, c1_i, r2_i, c2_i] artifacts[i]=[r1i,c1i,r2i,c2i] 再做一点补充解释:令 r m i n = m i n ( r 1 i , r 2 i ) r_{min}=min(r1_i,r2_i) rmin=min(r1i,r2i) r m a x = m a x ( r 1 i , r 2 i ) r_{max}=max(r1_i,r2_i) rmax=max(r1i,r2i) c m i n = m i n ( c 1 i , c 2 i ) c_{min}=min(c1_i,c2_i) cmin=min(c1i,c2i) c m a x = m a x ( c 1 i , c 2 i ) c_{max}=max(c1_i,c2_i) cmax=max(c1i,c2i),则该工件共占用了 ( r m a x − r m i n + 1 ) × ( c m a x − c m i n + 1 ) (r_{max}-r_{min}+1) \times (c_{max} - c_{min} + 1) (rmaxrmin+1)×(cmaxcmin+1)个单元格。例如, a r t i f a c t s [ i ] = [ 0 , 2 , 0 , 5 ] artifacts[i] = [0, 2, 0, 5] artifacts[i]=[0,2,0,5]则其共占用了$[0,2],[0,3],[0,4],[0,5]$4个单元格。

算法流程如下:

  • 首先遍历artifacts数组,将每个工件占用的单元格合并为一个集合(借助并查集实现)
    • 对于每个单元格的二维表示映射到一维,并合并为一个集合,记录集合中单元格的数目并标记此集合有工件
  • 遍历dig数组
    • 对于要挖掘的单元格(也映射到一维),在并查集中查找该单元格所在的集合是否有工件存在,若存在,将该集合记录的数目减1,若该集合记录的数目减到0,代表该集合代表的工件被挖出
  • 返回挖出工件的数目
class Solution {
    public int digArtifacts(int n, int[][] artifacts, int[][] dig) {
        UnionFind uf = new UnionFind(n * n);
        for (int[] art : artifacts) {
            int r_max = Math.max(art[0], art[2]), r_min = Math.min(art[0], art[2]);
            int c_max = Math.max(art[1], art[3]), c_min = Math.min(art[1], art[3]);
            // 工件占据的单元格数
            int[] a = new int[(r_max - r_min + 1) * (c_max - c_min + 1)];
            int idx = 0;
            for (int i = r_min; i <= r_max; i++) {
                for (int j = c_min; j <= c_max; j++) {
                    // 二维映射到一维
                    a[idx++] = i * n + j;
                }
            }
            // 仅占据一个单元格
            if (idx == 1) {
                uf.hasArtifact[a[0]] = true;
            } else {
                // 占据多个单元格
                for (int i = 1; i < idx; i++) {
                    uf.unionElements(a[i], a[i - 1]); // hasArtifact 在unionElements函数内部维护
                }
            }
        }
        
        int res = 0;
        for (int[] d : dig) {
            // 二维映射到一维
            int a = d[0] * n + d[1];
            // 查找根节点 hasArtifact仅有根节点维护
            int root = uf.find(a);
            // 该集合有工件
            if (uf.hasArtifact[root] == true) {                
                uf.size[root]--;
                // size 为 0 代表全部挖出
                if (uf.size[root] == 0) {
                    res++;
                }
            }
        }
        return res;
    }
}

class UnionFind {
    private int[] parent;
    private int[] rank;         // rank[i]表示以i为根的集合所表示的树的层数
                                // 不反应高度/深度
    public int[] size;          // 统计每个集合的元素个数 即工件占据的单元格数
    public boolean[] hasArtifact;      // 该集合是否有工件 只维护了集合的根节点
    public UnionFind(int length) {
        parent = new int[length];
        rank = new int[length];
        size = new int[length];
        hasArtifact = new boolean[length];
        for (int i = 0; i < length; i++) {
            parent[i] = i;
            rank[i] = 1;
            size[i] = 1;
            hasArtifact[i] = false;
        }
    }
     /**
     * 查找元素p所对应的集合编号
     * O(h),h为树的高度
     * @param p
     * @return
     */
    public int find(int p) {

        if (p < 0 || p >= parent.length) {
            throw new IllegalArgumentException("p is out of bound.");
        }

        while (p != parent[p]) {
            parent[p] = parent[parent[p]];
            p = parent[p];
        }
        return p;
    }
    
     /**
     * 元素p和元素q是否属于同一个集合
     * O(h)
     * @param p
     * @param q
     * @return
     */
    public boolean isConnected(int p, int q) {
        return find(p) == find(q);
    }
    
    /**
     * 合并元素pq所属的集合
     * O(h)
     * @param p
     * @param q
     */
    public void unionElements(int p, int q) {
        int pRoot = find(p);
        int qRoot = find(q);

        if (pRoot == qRoot) {
            hasArtifact[pRoot] = true;    // 该集合有工件 特殊情况:工件只占据一个单元格
            return ;
        }

        /*
         * 根据两个元素所在树的rank不同判断合并方向
         * 将rank低的集合合并到rank高的集合上
         * 并不实际反应节点的深度/高度值
         */
        if (rank[pRoot] < rank[qRoot]) {
            parent[pRoot] = qRoot;   // pRoot指向qRoot
            size[qRoot] += size[pRoot];     // 更新该集合中的元素数目
            hasArtifact[qRoot] = true;      // 集合有工件

        } else if (rank[qRoot] < rank[pRoot]) {
            parent[qRoot] = pRoot;
            size[pRoot] += size[qRoot];
            hasArtifact[pRoot] = true;
        } else {
            parent[qRoot] = pRoot;
            rank[pRoot] += 1;
            size[pRoot] += size[qRoot];
            hasArtifact[pRoot] = true;
        }
    }
}

暴力模拟

使用并查集复杂了些,这样写更简单

class Solution {
    public int digArtifacts(int n, int[][] artifacts, int[][] dig) {
        //记录单元格是否已经裸露
        boolean[] f = new boolean[n * n];
        for (int[] d : dig) {
            int idx = d[0] * n + d[1];
            f[idx] = true;
        }

        int ans = 0;
        for (int[] art : artifacts) {
            // 是否裸露
            boolean flag = true;
            for (int r = art[0]; r <= art[2] && flag; r++) {
                for (int c = art[1]; c <= art[3] && flag; c++) {
                    int idx = r * n + c;
                    flag = f[idx];
                }
            }

            // 范围内所有单元格都已经裸露
            if (flag) ans++;
        }

        return ans;
    }
}

5227. K 次操作后最大化顶端元素

题目描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

贪心 & 分类讨论

尽可能的寻找更大的值(贪心) + 分类讨论

  • 如果 nums.length = 1 \text{nums.length} = 1 nums.length=1,那么如果 k k k 是偶数,那么栈里存在 nums[0] \text{nums[0]} nums[0];如果 k k k 是奇数,那么栈里一定没有任何元素,返回 − 1 -1 1
  • 否则:
    • 1 ≤ k ≤ n 1 \le k \le n 1kn:栈顶元素可以是
      • nums的前 k − 1 k - 1 k1个数的最大值:首先将栈顶的 k − 1 k - 1 k1 个元素弹出,最后一步操作加入这些元素中的最大值
      • nums的第 k + 1 k + 1 k+1个数(此时出栈了前 k k k个数)
    • k > n k > n k>n:一定能取到所有数中的最大值。首先将所有数弹出,然后还剩 k − n k - n kn 步操作
      • k − n k - n kn 是奇数,则反复将最大值加入弹出栈即可
      • k − n k - n kn 是偶数,先将一个非最大值加入栈,然后反复将最大值加入弹出栈即可
  • 注意:nums的第 k k k个数永远不会出现在栈顶。
class Solution {
    public int maximumTop(int[] nums, int k) {
        int n = nums.length;
        if (n == 1 && k%2 == 1) {
            return -1;
        }

        int max = 0;
        max = 0;
        for (int i = 0; i < k - 1 && i < n; i++) {
            max = Math.max(max, nums[i]);
        }
        if (k < n) {
            max = Math.max(max, nums[k]);
        }
        return max;
    }
}

6032. 得到要求路径的最小带权子图

题目描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

解题思路

Reference

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xylitolz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值