LeetCode刷题笔记(Java)---更新至729题

前言

需要开通vip的题目暂时跳过

笔记导航

点击链接可跳转到所有刷题笔记的导航链接

721. 账户合并

给定一个列表 accounts,每个元素 accounts[i] 是一个字符串列表,其中第一个元素 accounts[i][0] 是 名称 (name),其余元素是 emails 表示该账户的邮箱地址。

现在,我们想合并这些账户。如果两个账户都有一些共同的邮箱地址,则两个账户必定属于同一个人。请注意,即使两个账户具有相同的名称,它们也可能属于不同的人,因为人们可能具有相同的名称。一个人最初可以拥有任意数量的账户,但其所有账户都具有相同的名称。

合并账户后,按以下格式返回账户:每个账户的第一个元素是名称,其余元素是按顺序排列的邮箱地址。账户本身可以以任意顺序返回。

在这里插入图片描述

  • 解答

    class Solution {
          
          
        public List<List<String>> accountsMerge(List<List<String>> accounts) {
          
          
            Map<String,Integer> email2Index = new HashMap<>();
            Map<Integer,String> index2Email = new HashMap<>();
            Map<String,String> email2Person = new HashMap<>();
            int index = 0;
            for(int i = 0;i < accounts.size();i++){
          
          
                List<String> curAccounts = accounts.get(i);
                String person = curAccounts.get(0);
                for(int j = 1;j < curAccounts.size();j++){
          
          
                    String email = curAccounts.get(j);
                    if(!email2Index.containsKey(email)){
          
          
                        email2Index.put(email,index);
                        index2Email.put(index++,email);
                        email2Person.put(email,person);
                    }
                }
            }
            UnionFind union = new UnionFind(index);
            for(int i = 0;i < accounts.size();i++){
          
          
                List<String> curAccounts = accounts.get(i);
                int firstIndex = email2Index.get(curAccounts.get(1));
                for(int j = 2;j < curAccounts.size();j++){
          
          
                    int secondIndex = email2Index.get(curAccounts.get(j));
                    union.union(firstIndex,secondIndex);
                }
            }
            int[] p = union.parent;
            Map<Integer,List<String>> personAllEmail = new HashMap<>();
            for(int i = 0 ;i < p.length;i++){
          
          
                int parentIndex = union.find(i);
                String email = index2Email.get(parentIndex);
                List<String> l = personAllEmail.getOrDefault(parentIndex,new ArrayList<>());
                l.add(index2Email.get(i));
                personAllEmail.put(parentIndex,l);
            }
            List<List<String>> res = new ArrayList<>();
            for(Integer key : personAllEmail.keySet()){
          
          
                List<String> emails = personAllEmail.get(key);
                Collections.sort(emails);
                List<String> r = new ArrayList<>();
                r.add(email2Person.get(emails.get(0)));
                r.addAll(emails);
                res.add(r);
            }
            return res;
        }
    }
    
    class UnionFind {
          
          
        int[] parent;
        int[] sz;
    
        public UnionFind(int n) {
          
          
            parent = new int[n];
            sz = new int[n];
            for (int i = 0; i < n; i++) {
          
          
                parent[i] = i;
                sz[i] = 1;
            }
        }
    
        public void union(int index1, int index2) {
          
          
            int p = find(index1);
            int q = find(index2);
            if(sz[p] > sz[q]){
          
          
                sz[p] += sz[q];
                parent[q] = p;
            }else{
          
          
                sz[q] += sz[p];
                parent[p] = q;
            }
        }
    
        public int find(int index) {
          
          
            if (parent[index] != index) {
          
          
                parent[index] = find(parent[index]);
            }
            return parent[index];
        }
    }
    
  • 分析

    1. 并查集 + hash表
    2. 首先将email 映射成整数,方便使用并查集
    3. 遍历account,更新并查集
    4. 得到并查集后 相同父亲的email,属于同一个账户的 所以之前还需要维护一个email 对应的所属账户
    5. 有了相同父亲的eamil 集合 和他们对应的所属人 就可以得到结果了。
  • 提交结果
    在这里插入图片描述

722. 删除注释

给一个 C++ 程序,删除程序中的注释。这个程序source是一个数组,其中source[i]表示第i行源码。 这表示每行源码由\n分隔。

在 C++ 中有两种注释风格,行内注释和块注释。

字符串// 表示行注释,表示//和其右侧的其余字符应该被忽略。

字符串/* 表示一个块注释,它表示直到*/的下一个(非重叠)出现的所有字符都应该被忽略。(阅读顺序为从左到右)非重叠是指,字符串/*/并没有结束块注释,因为注释的结尾与开头相重叠。

第一个有效注释优先于其他注释:如果字符串//出现在块注释中会被忽略。 同样,如果字符串/*出现在行或块注释中也会被忽略。

如果一行在删除注释之后变为空字符串,那么不要输出该行。即,答案列表中的每个字符串都是非空的。

样例中没有控制字符,单引号或双引号字符。比如,source = “string s = “/* Not a comment. */”;” 不会出现在测试样例里。(此外,没有其他内容(如定义或宏)会干扰注释。)

我们保证每一个块注释最终都会被闭合, 所以在行或块注释之外的/*总是开始新的注释。

最后,隐式换行符可以通过块注释删除。 有关详细信息,请参阅下面的示例。

从源代码中删除注释后,需要以相同的格式返回源代码。

在这里插入图片描述

  • 解答

    public List<String> removeComments(String[] source) {
          
          
            List<String> res = new ArrayList<>();
            StringBuilder sb = new StringBuilder();
            boolean flag = false;
            for(String s:source){
          
          
                for(int i = 0;i < s.length();i++){
          
          
                    char cur = s.charAt(i);
                    if(!flag && cur == '/' && i + 1 < s.length() && s.charAt(i + 1) == '/'){
          
          
                        break;
                    }
                    if(!flag && cur == '/' && i + 1 < s.length() && s.charAt(i + 1) == '*'){
          
          
                        i++;
                        flag = true;
                    }else if(flag && cur == '*' && i + 1 < s.length() && s.charAt(i + 1) == '/'){
          
          
                        i++;
                        flag = false;
                    }else if(!flag){
          
          
                        sb.append(cur);
                    }
                }
                if(!flag && sb.length() > 0){
          
          
                    res.add(sb.toString());
                    sb = new StringBuilder();
                }
            }
            return res;
        }
    
  • 分析

    1. 模拟过程
    2. flag标记是否处于/**/代码块。
    3. 若不在/**/代码块中,当遇到//的时候,则跳过之后的部分。
    4. 若处于代码块中,则忽略这一些。
    5. 其余的部分拼接起来。
    6. 每次遍历完一个字符串,就判断当前的位置是否在代码块当中,如果不是的话,则将目前得到的结果加入到答案集合中。
  • 提交结果
    在这里插入图片描述

724. 寻找数组的中心索引

给定一个整数类型的数组 nums,请编写一个能够返回数组 “中心索引” 的方法。

我们是这样定义数组 中心索引 的:数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和。

如果数组不存在中心索引,那么我们应该返回 -1。如果数组有多个中心索引,那么我们应该返回最靠近左边的那一个。

在这里插入图片描述

  • 解答

    public int pivotIndex(int[] nums) {
          
          
            int len = nums.length;
            int[] preSum = new int[len + 1];
            for(int i = 0;i < len;i++){
          
          
                preSum[i+1] = preSum[i] + nums[i]; 
            }
            for(int i = 0;i < len;i++){
          
          
                if(preSum[i] == preSum[len] - preSum[i + 1])
                    return i;
            }
            return -1;
        }
    
  • 分析

    1. 前缀和
    2. 遍历分割点,如果存在左右两边和相等的中心点,则返回
  • 提交结果
    在这里插入图片描述

725. 分隔链表

给定一个头结点为 root 的链表, 编写一个函数以将链表分隔为 k 个连续的部分。

每部分的长度应该尽可能的相等: 任意两部分的长度差距不能超过 1,也就是说可能有些部分为 null。

这k个部分应该按照在链表中出现的顺序进行输出,并且排在前面的部分的长度应该大于或等于后面的长度。

返回一个符合上述规则的链表的列表。

举例: 1->2->3->4, k = 5 // 5 结果 [ [1], [2], [3], [4], null ]

在这里插入图片描述

  • 解答

    public ListNode[] splitListToParts(ListNode r, int k) {
          
          
            int count = 0;
            ListNode p = r;
            while (p != null) {
          
          
                count++;
                p = p.next;
            }
            int remain = count % k;
            int num = count / k;
            ListNode[] res = new ListNode[k];
            p = r;
            int index = 0;
            if (num == 0) {
          
          
                while (p != null) {
          
          
                    res[index] = p;
                    ListNode pre = p;
                    p = p.next;
                    pre.next = null;
                    index++;
                }
            } else {
          
          
                while (remain > 0) {
          
          
                    res[index] = p;
                    int temp = num + 1;
                    remain--;
                    while (temp > 1) {
          
          
                        p = p.next;
                        temp--;
                    }
                    ListNode pre = p;
                    p = p.next;
                    pre.next = null;
                    index++;
                }
                while (p != null) {
          
          
                    res[index] = p;
                    int temp = num;
                    while (temp > 1) {
          
          
                        p = p.next;
                        temp--;
                    }
                    ListNode pre = p;
                    p = p.next;
                    pre.next = null;
                    index++;
                }
            }
            return res;
        }
    
  • 分析

    1. 因为分组之间的个数差不能大于1
    2. 所以先根据k求出每组最少num个,num = count / k。
    3. 然后求余数 remain = count % k。
    4. remain个分组在最少的个数的基础上+1,其余的组个数不变。
    5. 然后就是简单的遍历链表,切割链表了。
  • 提交结果
    在这里插入图片描述

728. 自除数

自除数 是指可以被它包含的每一位数除尽的数。

例如,128 是一个自除数,因为 128 % 1 == 0,128 % 2 == 0,128 % 8 == 0。

还有,自除数不允许包含 0 。

给定上边界和下边界数字,输出一个列表,列表的元素是边界(含边界)内所有的自除数。

在这里插入图片描述

  • 解答

    public List<Integer> selfDividingNumbers(int left, int right) {
          
          
            List<Integer> res = new ArrayList<>();
            for(int i = left;i <= right;i++){
          
          
                if(f(i)){
          
          
                    res.add(i);
                }
            }
            return res;
        }
        public boolean f(int number){
          
          
            int n = number;
            if(number == 0)return false;
            while(number != 0){
          
          
                int temp = number % 10;
                if(temp == 0 || n % temp != 0)return false;
                number /= 10;
            }
            return true;
        }
    
  • 分析

    1. 遍历left~right的数字。
    2. 每个数字都判断是否满足自除数
    3. 满足的加入到集合当中。
  • 提交结果
    在这里插入图片描述

729. 我的日程安排表 I

实现一个 MyCalendar 类来存放你的日程安排。如果要添加的时间内没有其他安排,则可以存储这个新的日程安排。

MyCalendar 有一个 book(int start, int end)方法。它意味着在 start 到 end 时间内增加一个日程安排,注意,这里的时间是半开区间,即 [start, end), 实数 x 的范围为, start <= x < end。

当两个日程安排有一些时间上的交叉时(例如两个日程安排都在同一时间内),就会产生重复预订。

每次调用 MyCalendar.book方法时,如果可以将日程安排成功添加到日历中而不会导致重复预订,返回 true。否则,返回 false 并且不要将该日程安排添加到日历中。

请按照以下步骤调用 MyCalendar 类: MyCalendar cal = new MyCalendar(); MyCalendar.book(start, end)

在这里插入图片描述

  • 解答

    public class MyCalendar {
          
          
        TreeMap<Integer, Integer> calendar;
    
        MyCalendar() {
          
          
            calendar = new TreeMap();
        }
    
        public boolean book(int start, int end) {
          
          
            Integer prev = calendar.floorKey(start),
                    next = calendar.ceilingKey(start);
            if ((prev == null || calendar.get(prev) <= start) && (next == null || end <= next)) {
          
          
                calendar.put(start, end);
                return true;
            }
            return false;
        }
    }
    
  • 分析

    1. 利用TreeMap来维护没有重叠的线段。
    2. 默认是按照key 升序排列。
    3. 找到比start小的key,判断这个key对应的value是否小于等于start。
    4. 找到比start大的key,判断这个key是否大于等于end。
    5. 如果上面其中一个不能满足,则表示不能插入。
  • 提交结果
    在这里插入图片描述

738. 单调递增的数字

给定一个非负整数 N,找出小于或等于 N 的最大的整数,同时这个整数需要满足其各个位数上的数字是单调递增。

(当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称这个整数是单调递增的。)

在这里插入图片描述

  • 解答

    public int monotoneIncreasingDigits(int N) {
          
          
            String res = String.valueOf(N);
            char[] chars = res.toCharArray();
            char cur = chars[0];
            boolean isdizeng = false;
            for(int i = 1;i < chars.length;i++){
          
          
                if(chars[i] >= cur){
          
          
                    cur = chars[i];
                    isdizeng = true;
                }else {
          
          
                    isdizeng = false;
                    break;
                }
            }
            if(isdizeng)return Integer.valueOf(new String(chars));
            cur = chars[0];
            int index = 0;
            for(int i = 1;i < chars.length;i++){
          
          
                if(chars[i] < cur) {
          
          
                    break;
                }else{
          
          
                    cur = chars[i];
                    index = i;
                }
            }
            for(int i = index-1;i >=0 ;i--){
          
          
                if(chars[i] == chars[index]){
          
          
                    index = i;
                }else break;
            }
            chars[index] = (char) (chars[index] - 1);
            for(int i = index + 1;i < chars.length;i++){
          
          
                chars[i] = '9';
            }
            return Integer.valueOf(new String(chars));
        }
    
  • 分析

    1. 首先第一个for循环判断是否是递增序列,如果是的话 直接返回结果
    2. 第二个for循环 找到满足递增序列的最后一位
    3. 第三个for循环 往前找与上一步找到的最后一位相等且最先出现的字符。
    4. 之后修改上一步找到的数字 -1 其余后面的数字修改为9
  • 提交结果
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/gongsenlin341/article/details/113704492