Java实现LeetCode 题号:551 - 560

127 阅读5分钟

「这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战」。

LeetCode习题集 有些题可能直接略过了,整理一下之前刷leetcode

551. 学生出勤记录 I

给定一个字符串来代表一个学生的出勤记录,这个记录仅包含以下三个字符:

'A' : Absent,缺勤 'L' : Late,迟到 'P' : Present,到场 如果一个学生的出勤记录中不超过一个'A'(缺勤)并且不超过两个连续的'L'(迟到),那么这个学生会被奖赏。

你需要根据这个学生的出勤记录判断他是否会被奖赏。

示例 1:

输入: "PPALLP" 输出: True 示例 2:

输入: "PPALLL" 输出: False


    PS:
        暴力循环
            一个变量记录缺席,一个变量记录迟到
            如果是缺席了,判断缺席次数是否大于1,如果大于1就返回false
            如果是迟到了,判断迟到次数是否大于2,如果大于2就返回false
            如果是到场,就把迟到记录清零
class Solution {
    public boolean checkRecord(String s) {
        int absent=0;
        int late=0;
        for (int i=0;i<s.length();i++){
            if (s.charAt(i)=='A'){
                late=0;
                absent++;
                if (absent>1)
                    return false;
            }
            else if (s.charAt(i)=='L'){
                late++;
                if (late>2)
                    return false;
            }
            else
                late=0;
        }
        return true;
    }
}

552. 学生出勤记录 II

给定一个正整数 n,返回长度为 n 的所有可被视为可奖励的出勤记录的数量。 答案可能非常大,你只需返回结果mod 109 + 7的值。

学生出勤记录是只包含以下三个字符的字符串:

'A' : Absent,缺勤 'L' : Late,迟到 'P' : Present,到场 如果记录不包含多于一个'A'(缺勤)或超过两个连续的'L'(迟到),则该记录被视为可奖励的。

示例 1:

输入: n = 2 输出: 8 解释: 有8个长度为2的记录将被视为可奖励: "PP" , "AP", "PA", "LP", "PL", "AL", "LA", "LL" 只有"AA"不会被视为可奖励,因为缺勤次数超过一次。 注意:n 的值不会超过100000。

先附上大佬用数学方法做的:
	用数组放入关系,太顶了
下面才是我写的勉强过关的代码
class Solution {
   public int checkRecord(int n) {
		long[][] a = new long[][]{{1},{1},{0},{1},{0},{0}};
		long[][] aMatrix = new long[][]{{1,1,1,0,0,0},{1,0,0,0,0,0},{0,1,0,0,0,0},{1,1,1,1,1,1},{0,0,0,1,0,0},{0,0,0,0,1,0}};
		while (n>0) {
			int m = n & 1;
			if (m == 1) {
				a = this.multipleMatrix(aMatrix, a);
			}
			aMatrix = this.multipleMatrix(aMatrix, aMatrix);
			n = n>>1;
		}
		/**
		* 0 A0L0 
		* 1 A0L1
		* 2 A0L2
		* 3 A1L0
		* 4 A1L1
		* 5 A1L2
		*/
		return (int)a[3][0];

	}
	public long[][] multipleMatrix(long[][] a,long[][] b) {
		long mod = (long)1e9+7;
		long c[][] = new long[a.length][b[0].length];
		for (int i = 0; i < a.length; i++) {
			for (int j = 0; j < b[i].length; j++) {
				for (int k = 0; k < a[i].length; k++) {
					c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % mod;
				}
			}
		}
		return c;
	}
}
class Solution {
    long M = 1000000007;
    public int checkRecord(int n) {
        long[] f = new long[n <= 5 ? 6 : n + 1];
        f[0] = 1;
        f[1] = 2;
        f[2] = 4;
        f[3] = 7;
        //四种情况下,我前三个可奖励,我最后一个是l是p无所谓
        //如果中间出现两个LL,那么我必定无效
        //2*f[i-1]和一个-f[i-4]
        for (int i = 4; i <= n; i++)
            f[i] = ((2 * f[i - 1]) % M + (M - f[i - 4])) % M;
        long sum = f[n];
        for (int i = 1; i <= n; i++) {
            sum += (f[i - 1] * f[n - i]) % M;
        }
        return (int)(sum % M);
    }
 
}

 

553. 最优除法

给定一组正整数,相邻的整数之间将会进行浮点除法操作。例如, [2,3,4] -> 2 / 3 / 4 。

但是,你可以在任意位置添加任意数目的括号,来改变算数的优先级。你需要找出怎么添加括号,才能得到最大的结果,并且返回相应的字符串格式的表达式。你的表达式不应该含有冗余的括号。

示例:

输入: [1000,100,10,2] 输出: "1000/(100/10/2)" 解释: 1000/(100/10/2) = 1000/((100/10)/2) = 200 但是,以下加粗的括号 "1000/((100/10)/2)" 是冗余的, 因为他们并不影响操作的优先级,所以你需要返回 "1000/(100/10/2)"。

其他用例: 1000/(100/10)/2 = 50 1000/(100/(10/2)) = 50 1000/100/10/2 = 0.5 1000/100/(10/2) = 2 说明:

输入数组的长度在 [1, 10] 之间。 数组中每个元素的大小都在 [2, 1000] 之间。 每个测试用例只有一个最优除法解。

PS:

捋一下思路,大于两个数,就是让除数变小就好,所以就是第一个数/(剩下的数相除)

class Solution {
     public String optimalDivision(int[] nums) {
     
        StringBuilder sb = new StringBuilder();
        int len = nums.length;

        //元素个数小于 2,直接 a / b 或 a 即可
        if(len < 3){
            for(int i = 0; i < nums.length; i++){
                sb.append(nums[i]);
                if(i != nums.length - 1){
                    sb.append("/");
                }
            }
        }else{
            for(int i = 0; i < nums.length; i++){
                sb.append(nums[i]);
                if(i == 0){
                    sb.append("/(");
                }else if(i != nums.length - 1){
                    sb.append("/");
                }
            }
                sb.append(")");
        }
        return sb.toString();
    }
}

554. 砖墙

你的面前有一堵方形的、由多行砖块组成的砖墙。 这些砖块高度相同但是宽度不同。你现在要画一条自顶向下的、穿过最少砖块的垂线。

砖墙由行的列表表示。 每一行都是一个代表从左至右每块砖的宽度的整数列表。

如果你画的线只是从砖块的边缘经过,就不算穿过这块砖。你需要找出怎样画才能使这条线穿过的砖块数量最少,并且返回穿过的砖块数量。

你不能沿着墙的两个垂直边缘之一画线,这样显然是没有穿过一块砖的。

示例:

输入: [[1,2,2,1], [3,1,2], [1,3,2], [2,4], [3,1,2], [1,3,1,1]]

输出: 2

解释: 在这里插入图片描述

提示:

每一行砖块的宽度之和应该相等,并且不能超过 INT_MAX。 每一行砖块的数量在 [1,10,000] 范围内, 墙的高度在 [1,10,000] 范围内, 总的砖块数量不超过 20,000。

PS:
    记录一下每层砖缝的位置, 最后看哪个位置,砖缝出现的次数最多,就用那个位置,
    总层数减去砖缝出现的次数,就是要穿过的次数。
class Solution {
    int[] arr = new int[65536];

    public int leastBricks(List<List<Integer>> wall) {
        for (List<Integer> item : wall) {
            int count = 0;
            for (int i = 0; i < item.size() - 1; i++) {
                arr[count + item.get(i)]++;
                count = count + item.get(i);
            }
        }
        int max = 0;
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        return wall.size()-max;
    }
}

556. 下一个更大元素 III

给定一个32位正整数 n,你需要找到最小的32位整数,其与 n 中存在的位数完全相同,并且其值大于n。如果不存在这样的32位整数,则返回-1。

示例 1:

输入: 12 输出: 21 示例 2:

输入: 21 输出: -1

PS:
    因为是要和n位数相同,先从低位到高位开始找,找第一个低位大于高位的数
    如果找完所有都是高位大于低位,则找不到比n大的数,返回-1
    current是第一个小于低位的数,
        current之前的数(右面的数都是高位大于低位),所以我们从最末尾开始找起始就是从后面最小的数开始找,找到第一个比current大的数,把这个数拿出来给tmp,然后把current放到大的数的那个位置,
        然后把tmp先放回原数,list在依次放回
        eg:
            597621      list里面是:  1  2  6  7  9  
            6是第一个比5大的     list修改后是:1  2  5  7  9
            先把6放进去  然后list按顺序进入   612579
class Solution {
   public int nextGreaterElement(int n) {
        LinkedList<Integer> nums = new LinkedList<>();
        int current = n%10;
        while(n > 0 && (nums.isEmpty() || (current = n%10) >= nums.getLast())) {
            nums.addLast(current);
            n = n / 10;
        }
        if (n == 0) {
            return -1;
        }
        n = n / 10;
        for (int i = 0; i <  nums.size(); i ++) {
            if (nums.get(i)> current) {
                int tmp = nums.get(i);
                nums.set(i, current);
                current = tmp;
                break;
            }
        }
        long result = n *10 + current;
        while(!nums.isEmpty()) {
            result = result*10 + nums.pop();
            if (result > Integer.MAX_VALUE) {
                return -1;
            }
        }
        return (int)result;
    }
}

557. 反转字符串中的单词 III

给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。

示例 1:

输入: "Let's take LeetCode contest" 输出: "s'teL ekat edoCteeL tsetnoc" 注意:在字符串中,每个单词由单个空格分隔,并且字符串中不会有任何额外的空格。

PS:
    把字符串通过空格进行分割,然后把每个分割后的字符串反转(StringBuffer现成的反转方法)
    然后在组成一个新的字符串,返回
class Solution {
        public String reverseWords(String s) {
	String[] strs = s.split(" ");
	StringBuffer buffer = new StringBuffer();
	for (int i = 0; i < strs.length; i++) {
		buffer.append(new StringBuffer(strs[i]).reverse().toString());
		buffer.append(" ");
	}
	return buffer.toString().trim();
    }
}

558. 四叉树交集

四叉树是一种树数据,其中每个结点恰好有四个子结点:topLeft、topRight、bottomLeft 和 bottomRight。四叉树通常被用来划分一个二维空间,递归地将其细分为四个象限或区域。

我们希望在四叉树中存储 True/False 信息。四叉树用来表示 N * N 的布尔网格。对于每个结点, 它将被等分成四个孩子结点直到这个区域内的值都是相同的。每个节点都有另外两个布尔属性:isLeaf 和 val。当这个节点是一个叶子结点时 isLeaf 为真。val 变量储存叶子结点所代表的区域的值。

例如,下面是两个四叉树 A 和 B:

A:

+-------+-------+   T: true
|       |       |   F: false
|   T   |   T   |
|       |       |
+-------+-------+
|       |       |
|   F   |   F   |
|       |       |
+-------+-------+
topLeft: T
topRight: T
bottomLeft: F
bottomRight: F

B:

+-------+---+---+
|       | F | F |
|   T   +---+---+
|       | T | T |
+-------+---+---+
|       |       |
|   T   |   F   |
|       |       |
+-------+-------+
topLeft: T
topRight:
     topLeft: F
     topRight: F
     bottomLeft: T
     bottomRight: T
bottomLeft: T
bottomRight: F

你的任务是实现一个函数,该函数根据两个四叉树返回表示这两个四叉树的逻辑或(或并)的四叉树。

A: B: C (A or B):

+-------+-------+  +-------+---+---+  +-------+-------+
|       |       |  |       | F | F |  |       |       |
|   T   |   T   |  |   T   +---+---+  |   T   |   T   |
|       |       |  |       | T | T |  |       |       |
+-------+-------+  +-------+---+---+  +-------+-------+
|       |       |  |       |       |  |       |       |
|   F   |   F   |  |   T   |   F   |  |   T   |   F   |
|       |       |  |       |       |  |       |       |
+-------+-------+  +-------+-------+  +-------+-------+

提示:

A 和 B 都表示大小为 N * N 的网格。 N 将确保是 2 的整次幂。 如果你想了解更多关于四叉树的知识,你可以参考这个 wiki 页面。 逻辑或的定义如下:如果 A 为 True ,或者 B 为 True ,或者 A 和 B 都为 True,则 "A 或 B" 为 True。

/*
// Definition for a QuadTree node.
class Node {
    public boolean val;
    public boolean isLeaf;
    public Node topLeft;
    public Node topRight;
    public Node bottomLeft;
    public Node bottomRight;

    public Node() {}

    public Node(boolean _val,boolean _isLeaf,Node _topLeft,Node _topRight,Node _bottomLeft,Node _bottomRight) {
        val = _val;
        isLeaf = _isLeaf;
        topLeft = _topLeft;
        topRight = _topRight;
        bottomLeft = _bottomLeft;
        bottomRight = _bottomRight;
    }
};
*/
class Solution {
    //是不是叶子节点
    public Node intersect(Node quadTree1, Node quadTree2) {
        if(quadTree1.isLeaf && quadTree2.isLeaf){
            Node res = new Node(false, false, null, null, null, null);
            res.val = quadTree1.val || quadTree2.val;
            res.isLeaf = true;
            return res;
        }
        else if(quadTree1.isLeaf && !quadTree2.isLeaf){
            if(quadTree1.val){
                return quadTree1;
            }
            else{
                return quadTree2;
            }
        }
        else if(quadTree2.isLeaf && !quadTree1.isLeaf){
            if(quadTree2.val){
                return quadTree2;
            }
            else{
                return quadTree1;
            }
        }
        else{
            //都不是叶子结点,就创建结点递归
            Node res = new Node(false, false, null, null, null, null);
            res.topLeft = intersect(quadTree1.topLeft, quadTree2.topLeft);
            res.topRight = intersect(quadTree1.topRight, quadTree2.topRight);
            res.bottomLeft = intersect(quadTree1.bottomLeft, quadTree2.bottomLeft);
            res.bottomRight = intersect(quadTree1.bottomRight, quadTree2.bottomRight); 
            //如果都为true,就向下搜索
            if(res.topLeft.isLeaf && res.topRight.isLeaf
               && res.bottomLeft.isLeaf && res.bottomRight.isLeaf
               && res.topLeft.val == res.topRight.val
               && res.topRight.val == res.bottomLeft.val
               && res.bottomLeft.val == res.bottomRight.val){
                res = res.topLeft;
            }
            return res;
        }
    }
}

559. N叉树的最大深度

给定一个 N 叉树,找到其最大深度。

最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。

例如,给定一个 3叉树 :

在这里插入图片描述

我们应返回其最大深度,3。

说明:

树的深度不会超过 1000。 树的节点总不会超过 5000。

/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
};
*/
class Solution {
    public int maxDepth(Node root) {   
        if(root == null)
            return 0;
        int depth = 0;
        for(int i = 0;i<root.children.size();i++){
            depth = Math.max(depth,maxDepth(root.children.get(i)));
        }
        return depth+1;
    }
}

560. 和为K的子数组

给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。

示例 1 :

输入:nums = [1,1,1], k = 2 输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。 说明 :

数组的长度为 [1, 20,000]。 数组中元素的范围是 [-1000, 1000] ,且整数 k 的范围是 [-1e7, 1e7]。

PS:
	先附上简单一些的
        使用前缀和,然后每次到一个位置,就看一下是否存在前缀和为sum - k 的
        如果存在,证明这段子数组的和为k,然后把前缀和为sum-k的数量记录
        并把当前前缀和记录,如果之前有和为sum的,就是之前的数量加1,否则当前和的数量为1
class Solution {
     public int subarraySum(int[] nums, int k) { 
        Map<Integer, Integer> map = new HashMap<>();
        map.put(0, 1);
        int sum = 0, ret = 0;

        for(int i = 0; i < nums.length; ++i) {
            sum += nums[i];
            if(map.containsKey(sum-k))
                ret += map.get(sum-k);
            map.put(sum, map.getOrDefault(sum, 0)+1);
        }
        
        return ret;
    }
}
PS:
	 
        改编自计数排序,其实也和上面一样的道理,
        记录前缀和,然后加上当前 sum - k 的数量
class Solution {
     public int subarraySum(int[] nums, int k) {
        int sum = 0;
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        for (int n : nums) {
            sum += n;
            min = Math.min(min, sum);
            max = Math.max(max, sum);
        }
        int[] sums = new int[max - min + 1];
        int count = 0;
        sum = 0;
        for (int n : nums) {
            sum += n;
            int target = sum - min - k;
            if (target >= 0 && target < sums.length) {
                count += sums[target];
            }
            sums[sum - min]++;
        }
        if (k - min >= 0 && k - min < sums.length) {
            count += sums[k - min];
        }
        return count;
    }
}