LeetCode高频题:新冠密接者排查,第一密接和第二密接都是谁?按顺序输出

LeetCode高频题:新冠密接者排查,第一密接和第二密接都是谁?按顺序输出

提示:本题是系列LeetCode的150道高频题,你未来遇到的互联网大厂的笔试和面试考题,基本都是从这上面改编而来的题目
互联网大厂们在公司养了一大批ACM竞赛的大佬们,吃完饭就是设计考题,然后去考应聘人员,你要做的就是学基础树结构与算法,然后打通任督二脉,以应对波云诡谲的大厂笔试面试题!
你要是不扎实学习数据结构与算法,好好动手手撕代码,锻炼解题能力,你可能会在笔试面试过程中,连题目都看不懂!比如华为,字节啥的,足够让你读不懂题
在这里插入图片描述


题目

新冠密接者排查

某公司食堂是一个m×n的矩阵,现在接到通知,某一天有一批核酸阳性的员工在该食堂就餐,请按照如下规则找到第一密接和第二密接(第一密接的接触者),并按照第一密接和第二密接顺序输出,每一类密接内部按照字典序排序
**密接的定义:**所有与新冠阳性就餐位置相邻的8个人(上下左右,左上,左下,右上,右下)
题目保证没有重名人,并且每个座位都有人,输出不能包含确诊人员
如果输入的确诊为阳性步在食堂,那就返回空列表

输入描述:
第一行:字符串列表,空格隔开,阳性人员名字
第二行:m n,食堂矩阵的大小
第三行:后续所有员工名字

输出描述;
密接们的名字(一密在前,二密在后)一二内部格子按照字典序升序


一、审题

示例:

L
6 5
A B C D E
F G H I J
K L M N O
P Q R S T
U V W X Y
Z 0 1 2 3

[F, G, H, K, M, P, Q, R, A, B, C, D, I, N, S, U, V, W, X]

解释:
第一密接:
在这里插入图片描述
然后第一密接的接触者:二密
在这里插入图片描述
组合起来就是
[F, G, H, K, M, P, Q, R【一密】, A, B, C, D, I, N, S, U, V, W, X【二密】]


二、解题

非常简单的一个判断而已

用visited数组表示ij位置是否被访问过了,访问过了不要访问了

准备一个比较器cptr,俩字符串ab来了,升序:

        //比较器,根据字典序排序
        public static class cptr implements Comparator<String>{
            @Override
            public int compare(String o1, String o2){
                return o1.compareTo(o2);
            }
        }

准备俩有序表,miJie1和miJie2,用上面的比较器初始化,因为密接结果要升序排序

arr用来接收食堂的所有人,它是一个mn矩阵

用哈希集setEmp来接收阳性患者名字列表,这样方便我们查询食堂中arrij是否在感染列表,这样菜有必要收集结果,否则不用找了

寻找密接2个步骤,就跟题目说得一样
一密:setEmp中有arrij,那就看arrij的8个邻居,直接就是miJie1,访问过了visited标记
二密:现在一密就是感染者了呗,把他们当做感染者来看的话,把一密的一密放入miJie2,不就得了???

手撕代码:非常简单

        public static void main(String[] args) {
            // please define the JAVA input here. For example: Scanner s = new Scanner(System.in);
            // please finish the function body here.
            // please define the JAVA output here. For example: System.out.println(s.nextInt());
            Scanner in = new Scanner(System.in);
            String[] employee = in.nextLine().split(" ");
            HashSet<String> setEmp = new HashSet<>();
            for (int i = 0; i < employee.length; i++) {
                setEmp.add(employee[i]);//用来查字符串
            }
            String[] mn = in.nextLine().split(" ");
            int m = Integer.valueOf(mn[0]);
            int n = Integer.valueOf(mn[1]);//mn

            String[][] arr = new String[m][n];
            for (int i = 0; i < m; i++) {
                String[] s = in.nextLine().split(" ");
                for (int j = 0; j < n; j++) {
                    arr[i][j] = s[j];//导入arr
                }
            }

            TreeSet<String> miJie1 = new TreeSet<>(new cptr());
            TreeSet<String> miJie2 = new TreeSet<>(new cptr());//2个密接

            boolean[][] visited = new boolean[m][n];//访问过的节点,就扔到这里,不要访问了,本人不要访问
            //查询第一层密接
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    if (!visited[i][j] && setEmp.contains(arr[i][j])){
                        //它的第一层密接,升序并自动去重
                        visited[i][j] = true;//自己不算
                        //一圈8个方向查一下,OK就进入
                        if (isValid(arr, i - 1, j, visited)) {
                            miJie1.add(arr[i - 1][j]);//上
                            visited[i - 1][j] = true;
                        }
                        if (isValid(arr, i + 1, j, visited)) {
                            miJie1.add(arr[i + 1][j]);//下
                            visited[i + 1][j] = true;
                        }
                        if (isValid(arr, i, j - 1, visited)) {
                            miJie1.add(arr[i][j - 1]);//左
                            visited[i][j - 1] = true;
                        }
                        if (isValid(arr, i, j + 1, visited)) {
                            miJie1.add(arr[i][j + 1]);//右
                            visited[i][j - 1] = true;
                        }
                        if (isValid(arr, i - 1, j - 1, visited)) {
                            miJie1.add(arr[i - 1][j - 1]);//左上
                            visited[i - 1][j - 1] = true;
                        }
                        if (isValid(arr, i + 1, j - 1, visited)) {
                            miJie1.add(arr[i + 1][j - 1]);//左下
                            visited[i + 1][j - 1] = true;
                        }
                        if (isValid(arr, i - 1, j + 1, visited)) {
                            miJie1.add(arr[i - 1][j + 1]);//右上
                            visited[i - 1][j + 1] = true;
                        }
                        if (isValid(arr, i + 1, j + 1, visited)) {
                            miJie1.add(arr[i + 1][j + 1]);//右下
                            visited[i + 1][j + 1] = true;
                        }
                    }
                }
            }

            //查询第2层密接--这里第一密接还不能算
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    if (miJie1.contains(arr[i][j])) visited[i][j] = false;//第一密接是需要访问的
                    if (!visited[i][j] && miJie1.contains(arr[i][j])){//要把第一密接访问一遍
                        //它的第一层密接,升序并自动去重
                        visited[i][j] = true;//自己不算
                        //一圈8个方向查一下,OK就进入
                        if (isValid2(arr, i - 1, j, visited, miJie1)) {
                            miJie2.add(arr[i - 1][j]);//上
                            visited[i - 1][j] = true;
                        }
                        if (isValid2(arr, i + 1, j, visited, miJie1)) {
                            miJie2.add(arr[i + 1][j]);//下
                            visited[i + 1][j] = true;
                        }
                        if (isValid2(arr, i, j - 1, visited, miJie1)) {
                            miJie2.add(arr[i][j - 1]);//左
                            visited[i][j - 1] = true;
                        }
                        if (isValid2(arr, i, j + 1, visited, miJie1)) {
                            miJie2.add(arr[i][j + 1]);//右
                            visited[i][j - 1] = true;
                        }
                        if (isValid2(arr, i - 1, j - 1, visited, miJie1)) {
                            miJie2.add(arr[i - 1][j - 1]);//左上
                            visited[i - 1][j - 1] = true;
                        }
                        if (isValid2(arr, i + 1, j - 1, visited, miJie1)) {
                            miJie2.add(arr[i + 1][j - 1]);//左下
                            visited[i + 1][j - 1] = true;
                        }
                        if (isValid2(arr, i - 1, j + 1, visited, miJie1)) {
                            miJie2.add(arr[i - 1][j + 1]);//右上
                            visited[i - 1][j + 1] = true;
                        }
                        if (isValid2(arr, i + 1, j + 1, visited, miJie1)) {
                            miJie2.add(arr[i + 1][j + 1]);//右下
                            visited[i + 1][j + 1] = true;
                        }
                    }
                }
            }

            List<String> ans = new ArrayList<>();//自动去重
            for(String s:miJie1) ans.add(s);
            for(String s:miJie2) if(!miJie1.contains(s)) ans.add(s);

            System.out.print(ans);
        }
        
        public static boolean isValid(String[][] arr, int i, int j, boolean[][] visited){
            //判断arr的ij位置是否有效的
            if (i < 0 || i >= arr.length || j < 0 || j >= arr[0].length || visited[i][j]) return false;//越界了--访问过了就算了
            return true;//否则就OK
        }
        
        public static boolean isValid2(String[][] arr, int i, int j, boolean[][] visited, TreeSet<String> miJie1){
            //判断arr的ij位置是否有效的——有密接2不行的
            if (i < 0 || i >= arr.length || j < 0 || j >= arr[0].length || visited[i][j] || miJie1.contains(arr[i][j])) return false;//越界了--访问过了就算了
            return true;//否则就OK
        }

注意,里面的valid1和2
valid1是判断setEmp的一密时所用,专门判断ij是否越界,另外,ij访问过了不能访问了
同理,valid2是判段一密miJie1的密接者用的,作用除了valid1,还要考虑,是miJie1了就不要加了

测试一把:

L
6 5
A B C D E
F G H I J
K L M N O
P Q R S T
U V W X Y
Z 0 1 2 3
[F, G, H, K, M, P, Q, R, A, B, C, D, I, N, S, U, V, W, X]

搞定


总结

提示:重要经验:

1)可以考虑用dfs,但是何必呢,就是ij邻居8个判断一下就是了,非常简单
2)当你花了足够的心思和努力练习和准备好数据结构与算法,那互联网大厂的机试相比之前没有那么困难了
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

冰露可乐

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

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

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

打赏作者

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

抵扣说明:

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

余额充值