【leetcode】4月份每日一题专栏(java版本含注释)

前言

本栏目主要是针对leetcode的每日一题进行按时按量刷
本博文的笔记主要是总结自已的逻辑思路
也慢慢积累算法,感兴趣的也可关注该栏目
大部分思路都是在代码中(含注释)

796. 旋转字符串(简单)(22.4.7)

该题目链接:
796. 旋转字符串

给定两个字符串, s 和 goal。如果在若干次旋转操作之后,s 能变成 goal ,那么返回 true 。

s 的 旋转操作 就是将 s 最左边的字符移动到最右边。

例如, 若 s = ‘abcde’,在旋转一次之后结果就是’bcdea’ 。

示例 1:

输入: s = “abcde”, goal = “cdeab”
输出: true

示例 2:

输入: s = “abcde”, goal = “abced”
输出: false

提示:

1 <= s.length, goal.length <= 100
s 和 goal 由小写英文字母组成


思路:

通过字符串的拼接对比,使用substring,以及equals比较

class Solution {
    public boolean rotateString(String s, String goal) {
        //先判断两者长度是否相等,如果不等,直接返回false
        if(s.length()!=goal.length())return false;

        //在相应遍历s的字符串长度,通过substring拼接相应的字符串,如果两者相等直接返回true
        for(int i=1;i<s.length();i++){
            String str=s.substring(i,goal.length())+s.substring(0,i);
            if(str.equals(goal))return true;
        }
        return false;

    }
}

本身该题为简单题
官方可能就想教会我们使用某种api
如下:

class Solution {
    public boolean rotateString(String s, String goal) {
        return s.length() == goal.length() && (s + s).contains(goal);
    }
}

字符串 s +s 包含了所有 s 可以通过旋转操作得到的字符串,只需要检查子字符串

429. N 叉树的层序遍历(中等)(22.4.8)

给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。

树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。

示例 1:

在这里插入图片描述

输入:root = [1,null,3,2,4,null,5,6]
输出:[[1],[3,2,4],[5,6]]

示例 2:

在这里插入图片描述

输入:root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]
输出:[[1],[2,3,4,5],[6,7,8,9,10],[11,12,13],[14]]

提示:

树的高度不会超过 1000
树的节点总数在 [0, 10^4] 之间


思路:

很明显的思路就是广度优先遍历
通过层次遍历进行输出即可
区分不同的二叉树结构(层次输出其子节点,也就是遍历子节点,一个个放入队列中即可)

class Solution {
    public List<List<Integer>> levelOrder(Node root) {
        List<List<Integer>> list=new ArrayList<List<Integer>>();
        if(root==null)return list;

        LinkedList<Node> que=new LinkedList<>();
        que.offer(root);
        while(!que.isEmpty()){
            List<Integer> sonlist=new ArrayList<>();
            int n=que.size();
            for(int i=0;i<n;i++){
                Node node=que.poll();
                sonlist.add(node.val);
                //和其他二叉树的区别是,n叉树是孩子,直接遍历n叉树的children节点
                //主要是这个的区别
                for(Node child:node.children){
                    que.offer(child);
                }
            }
            list.add(sonlist);
        }
        return list;
    }
}

780. 到达终点(困难)(22.4.9)

该题目链接:
780. 到达终点

给定四个整数 sx , sy ,tx 和 ty,如果通过一系列的转换可以从起点 (sx, sy) 到达终点 (tx, ty),则返回 true,否则返回 false。

从点 (x, y) 可以转换到 (x, x+y) 或者 (x+y, y)。

示例 1:

输入: sx = 1, sy = 1, tx = 3, ty = 5
输出: true
解释:
可以通过以下一系列转换从起点转换到终点:
(1, 1) -> (1, 2)
(1, 2) -> (3, 2)
(3, 2) -> (3, 5)

示例 2:

输入: sx = 1, sy = 1, tx = 2, ty = 2
输出: false

示例 3:

输入: sx = 1, sy = 1, tx = 1, ty = 1
输出: true

提示:

1 <= sx, sy, tx, ty <= 109


思路:

这道题跟某一个公司的笔试题有些相像,都是求能否加到某个数
如果正向肯定方向很多,反向的话通过求余去直接一步到位比较

具体代码思路如下:

class Solution {
    public boolean reachingPoints(int sx, int sy, int tx, int ty) {
        //通过反向的迭代,tx不可等于ty,否则跳出while条件
        while(tx>sx && ty>sy && tx!=ty){

            //本身通过求余,可以一步到位,而不是一步一步减
            if(tx>ty){
                tx=tx%ty;
            }else if(tx<ty){
                ty=ty%tx;
            }
        }

        //终止条件 两个相等,或者某个不相等
        if(tx==sx && ty==sy)return true;
        else if(tx==sx && ty>sy) return (ty-sy)%tx==0; //本身最后一步,直接用减法,之后求余其对应值即可
        else if(ty==sy && tx>sx) return (tx-sx)%ty==0;

        //以上所有情况都不满足则返回false
        return false;
    }
}

357. 统计各位数字都不同的数字个数(中等)(22.4.11)

该题目链接:
357. 统计各位数字都不同的数字个数

给你一个整数 n ,统计并返回各位数字都不同的数字 x 的个数,其中 0 <= x < 10n

示例 1:

输入:n = 2
输出:91
解释:答案应为除去 11、22、33、44、55、66、77、88、99 外,在 0 ≤ x < 100 范围内的所有数字。

示例 2:

输入:n = 0
输出:1

提示:

0 <= n <= 8


思路:

class Solution {
    public int countNumbersWithUniqueDigits(int n) {
        //默认初始值 给予0的时候返回1
        if(n==0)return 1;
        //第一位的时候返回 10
        if(n==1)return 10;
        
        int ans=9,cur=10;
        //本身是数学组合,超过两位数的时候。第一位为1-9选择(共有9个数字),第二位有0-9(排除第一位,也有9个数字),第三位以此递减
        for(int i=0;i<n-1;i++){
            //在这里开始实现组合的思想
            ans=ans*(9-i);
            //将第一位的特殊情况有10个数字加起来(小于第n的数字累加起来)
            cur=cur+ans;
        }
        return cur;
    }
}

806. 写字符串需要的行数(简单)(22.4.12)

我们要把给定的字符串 S 从左到右写到每一行上,每一行的最大宽度为100个单位,如果我们在写某个字母的时候会使这行超过了100 个单位,那么我们应该把这个字母写到下一行。我们给定了一个数组 widths ,这个数组 widths[0] 代表 ‘a’ 需要的单位, widths[1] 代表 ‘b’ 需要的单位,…, widths[25] 代表 ‘z’ 需要的单位。

现在回答两个问题:至少多少行能放下S,以及最后一行使用的宽度是多少个单位?将你的答案作为长度为2的整数列表返回。

示例 1:

输入:
widths = [10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10]
S = “abcdefghijklmnopqrstuvwxyz”
输出: [3, 60]
解释:
所有的字符拥有相同的占用单位10。所以书写所有的26个字母,
我们需要2个整行和占用60个单位的一行。

示例 2:

输入:
widths = [4,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10]
S = “bbbcccdddaaa”
输出: [2, 4]
解释:
除去字母’a’所有的字符都是相同的单位10,并且字符串 “bbbcccdddaa” 将会覆盖 9 * 10 + 2 * 4 = 98 个单位.
最后一个字母 ‘a’ 将会被写到第二行,因为第一行只剩下2个单位了。
所以,这个答案是2行,第二行有4个单位宽度。

注:

字符串 S 的长度在 [1, 1000] 的范围。
S 只包含小写字母。
widths 是长度为 26的数组。
widths[i] 值的范围在 [2, 10]。


思路:

题目老长老长了,光是看题目就看了大半天,但是还是很好理解很好做的
具体思路如下:

左到右遍历字符串 s 中的每个字母
注意边界条件以及临界值的赋值即可

class Solution {
    public int[] numberOfLines(int[] widths, String s) {
        int n=s.length();
        int sum=0;
        //定义层面上的高度,一开始第一层就是1
        int height=1;
        for(int i=0;i<n;i++){
            //将其字符-‘a’ 去配对哪个数组,然后将其遍历加起来即可
            sum+=widths[ s.charAt(i)-'a'];
            //如果超过100,则高度加1,而且把原先的值赋值给sum即可,让sum重新带着这个值遍历(不用给sum清空,在加上去,状态变量赋值即可)
            if(sum>100){
                height++;
                sum=widths[ s.charAt(i)-'a'];
            }
        }

        //返回的其数组变量即可
        return new int[]{height,sum};
    }
}

380. O(1) 时间插入、删除和获取随机元素(中等)(22.4.13)

实现RandomizedSet 类:

RandomizedSet() 初始化 RandomizedSet 对象
bool insert(int val) 当元素 val 不存在时,向集合中插入该项,并返回 true ;否则,返回 false 。
bool remove(int val) 当元素 val 存在时,从集合中移除该项,并返回 true ;否则,返回 false 。
int getRandom() 随机返回现有集合中的一项(测试用例保证调用此方法时集合中至少存在一个元素)。每个元素应该有 相同的概率 被返回。
你必须实现类的所有函数,并满足每个函数的 平均 时间复杂度为 O(1) 。

示例:

输入
[“RandomizedSet”, “insert”, “remove”, “insert”, “getRandom”, “remove”, “insert”, “getRandom”]
[[], [1], [2], [2], [], [1], [2], []]
输出
[null, true, false, true, 2, true, false, 2]
解释
RandomizedSet randomizedSet = new RandomizedSet();
randomizedSet.insert(1); // 向集合中插入 1 。返回 true 表示 1 被成功地插入。
randomizedSet.remove(2); // 返回 false ,表示集合中不存在 2 。
randomizedSet.insert(2); // 向集合中插入 2 。返回 true 。集合现在包含 [1,2] 。
randomizedSet.getRandom(); // getRandom 应随机返回 1 或 2 。
randomizedSet.remove(1); // 从集合中移除 1 ,返回 true 。集合现在包含 [2] 。
randomizedSet.insert(2); // 2 已在集合中,所以返回 false 。
randomizedSet.getRandom(); // 由于 2 是集合中唯一的数字,getRandom 总是返回 2 。

提示:

-231 <= val <= 231 - 1
最多调用 insert、remove 和 getRandom 函数 2 * 105 次
在调用 getRandom 方法时,数据结构中 至少存在一个 元素。


思路:

想到了用哈希去计数每一个数字,但是返回随机元素的时候可能不好返回,因为要遍历哈希结构
后面看了叶总以及官方的大致意思
结合多了一种数组或者列表的这个结构
大致代码如下:
因为本身题目要求 每个函数的 平均 时间复杂度为 O(1) 。

class RandomizedSet {
    //创建一个数组。也可以通过使用列表来代替数组
    static int [] nums = new int [200000];
    //数组下标,从-1开始遍历
    int idx=-1;    
    
    //random主要是为了随机元素,map引入减少它的时间复杂度,用数组和哈希的结合
    Random random ;
    Map <Integer,Integer> map ;
    public RandomizedSet() {
        map = new HashMap <> ();
        random = new Random();

    }
    
    public boolean insert(int val) {
        if(map.containsKey(val))return false;

        //放到数组中
        nums[++idx] = val;
        //数组的元素对应元素的下标值
        map.put(val,idx);
        return true;

    }
    
    public boolean remove(int val) {
        if(!map.containsKey(val)) return false;
        //取出值,然后将该值在数组删除,删除的用法是直接用尾数组覆盖其值即可
        int temp = map.remove(val);

        //判断删除的位置是最后一个还是中间的元素,如果不是最后一个位置
        //则将其最后一个位置map集合放到其被删除的元素位置那里
        if(temp!=idx) map.put(nums[idx],temp);

        //将其数组的最后一个元素赋值覆盖原先删除的元素,然后idx下标减1
        nums[temp] = nums[idx--];
        return true;
    }
    
    public int getRandom() {
        //调用random的随机nextInt函数
        return nums[random.nextInt(idx + 1)];

    }
}

链表加哈希计数具体如下:

class RandomizedSet {
    List<Integer> nums;
    Map<Integer, Integer> indices;
    Random random;

    public RandomizedSet() {
        nums = new ArrayList<Integer>();
        indices = new HashMap<Integer, Integer>();
        random = new Random();
    }

    public boolean insert(int val) {
        if (indices.containsKey(val)) {
            return false;
        }
        int index = nums.size();
        nums.add(val);
        indices.put(val, index);
        return true;
    }

    public boolean remove(int val) {
        if (!indices.containsKey(val)) {
            return false;
        }
        int index = indices.get(val);
        int last = nums.get(nums.size() - 1);
        nums.set(index, last);
        indices.put(last, index);
        nums.remove(nums.size() - 1);
        indices.remove(val);
        return true;
    }

    public int getRandom() {
        int randomIndex = random.nextInt(nums.size());
        return nums.get(randomIndex);
    }
}

1672. 最富有客户的资产总量(简单)(22.4.14)

给你一个 m x n 的整数网格 accounts ,其中 accounts[i][j] 是第 i​​​​​​​​​​​​ 位客户在第 j 家银行托管的资产数量。返回最富有客户所拥有的 资产总量 。

客户的 资产总量 就是他们在各家银行托管的资产数量之和。最富有客户就是 资产总量 最大的客户。

示例 1:

输入:accounts = [[1,2,3],[3,2,1]]
输出:6
解释:
第 1 位客户的资产总量 = 1 + 2 + 3 = 6
第 2 位客户的资产总量 = 3 + 2 + 1 = 6
两位客户都是最富有的,资产总量都是 6 ,所以返回 6 。

示例 2:

输入:accounts = [[1,5],[7,3],[3,5]]
输出:10
解释:
第 1 位客户的资产总量 = 6
第 2 位客户的资产总量 = 10
第 3 位客户的资产总量 = 8
第 2 位客户是最富有的,资产总量是 10

示例 3:

输入:accounts = [[2,8,7],[7,1,3],[1,9,5]]
输出:17

提示:

m == accounts.length
n == accounts[i].length
1 <= m, n <= 50
1 <= accounts[i][j] <= 100


思路:

class Solution {
    public int maximumWealth(int[][] accounts) {
    //这道题还是比较简单,求二维数组中每一维的最大值,输出最大的一维数组之和即可
        int m=accounts.length;
        int n=accounts[0].length;
        int max=Integer.MIN_VALUE;
        for(int i=0;i<m;i++){
            int sum=0;
            for(int j=0;j<n;j++){
                sum+=accounts[i][j];
            }
            max=Math.max(sum,max);
        }
        return max;

    }
}

这么简单的每日一题可能是早睡的一天

但官方可能想教会我们某个api的调用
那就是stream类

Arrays.stream(account)返回的是Optional。目的是尽可能避免 NullPointerException
然后调用该函数的sum值进行统计输出即可

关于这个类的其他解释可看这篇文章的讲解
Arrays.stream()

class Solution {
    public int maximumWealth(int[][] accounts) {
        int maxWealth = Integer.MIN_VALUE;
        for (int[] account : accounts) {
            maxWealth = Math.max(maxWealth, Arrays.stream(account).sum());
        }
        return maxWealth;
    }
}

905. 按奇偶排序数组(简单)(22.4.28)

题目:leetcode:905. 按奇偶排序数组

class Solution {
    public int[] sortArrayByParity(int[] nums) {

        int i=0,j=nums.length-1;
        while(i<j){
            //在while条件中不要忘记i小于j这个大条件判断
            while(i<j && nums[i]%2==0)i++;
            while(i<j && nums[j]%2==1)j--;

            int temp=nums[i];
            nums[i]=nums[j];
            nums[j]=temp;

            //原地置换之后 要记得i++以及j--
            i++;
            j--;
        }
        return nums;



    }
}

908. 最小差值 I(简单)(22.4.30)

题目:leetcode:908. 最小差值 I

class Solution {
    public int smallestRangeI(int[] nums, int k) {
        
        //用第一个值保存最大和最小的值
        int max=nums[0];
        int min=nums[0];

        //通过遍历数组找到最大值和最小值
        for(int i=0;i<nums.length;i++){
            min=Math.min(nums[i],min);
            max=Math.max(nums[i],max);
        }

        //返回的值主要通过最大值减去最小值再减去2*k
        //如果上面这个值小于0,说明可以变为一样的值,也就是最小值为0
        //如果不小于0,说明不能变为一样的值,即(min+k,max-k)往两边缩,范围最小,总值为max-min-2*k
        return Math.max(0,max-min-2*k);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码农研究僧

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

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

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

打赏作者

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

抵扣说明:

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

余额充值