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

682. 棒球比赛

你现在是棒球比赛记录员。 给定一个字符串列表,每个字符串可以是以下四种类型之一: 1.整数(一轮的得分):直接表示您在本轮中获得的积分数。 2. "+"(一轮的得分):表示本轮获得的得分是前两轮有效 回合得分的总和。 3. "D"(一轮的得分):表示本轮获得的得分是前一轮有效 回合得分的两倍。 4. "C"(一个操作,这不是一个回合的分数):表示您获得的最后一个有效 回合的分数是无效的,应该被移除。

每一轮的操作都是永久性的,可能会对前一轮和后一轮产生影响。 你需要返回你在所有回合中得分的总和。

示例 1:

输入: ["5","2","C","D","+"]
输出: 30
解释: 
第1轮:你可以得到5分。总和是:5。
第2轮:你可以得到2分。总和是:7。
操作1:第2轮的数据无效。总和是:5。
第3轮:你可以得到10分(第2轮的数据已被删除)。总数是:15。
第4轮:你可以得到5 + 10 = 15分。总数是:30。

示例 2:

输入: ["5","-2","4","C","D","9","+","+"]
输出: 27
解释: 
第1轮:你可以得到5分。总和是:5。
第2轮:你可以得到-2分。总数是:3。
第3轮:你可以得到4分。总和是:7。
操作1:第3轮的数据无效。总数是:3。
第4轮:你可以得到-4分(第三轮的数据已被删除)。总和是:-1。
第5轮:你可以得到9分。总数是:8。
第6轮:你可以得到-4 + 9 = 5分。总数是13。
第7轮:你可以得到9 + 5 = 14分。总数是27。

注意:

输入列表的大小将介于1和1000之间。 列表中的每个整数都将介于-30000和30000之间。

class Solution {
     public int calPoints(String[] ops) {
        int[] arr = new int[ops.length];
        int i=0;
        for(String s:ops){
            switch (s){
                case "+":arr[i]=arr[i-1]+arr[i-2];i++;break;
                case "D":arr[i]=2*arr[i-1];i++;break;
                case "C":arr[i-1]=0;i--;break;
                default:
                    arr[i]=Integer.valueOf(s);
                    i++;
            }
        }
        int sum=0;
        for (int j = 0; j <arr.length ; j++) {
            sum+=arr[j];
        }
        return sum;
    }
}

684. 冗余连接

在本问题中, 树指的是一个连通且无环的无向图。

输入一个图,该图由一个有着N个节点 (节点值不重复1, 2, ..., N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。

结果图是一个以边组成的二维数组。每一个边的元素是一对[u, v] ,满足 u < v,表示连接顶点u 和v的无向图的边。

返回一条可以删去的边,使得结果图是一个有着N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。答案边 [u, v] 应满足相同的格式 u < v。

示例 1:

输入: [[1,2], [1,3], [2,3]]
输出: [2,3]
解释: 给定的无向图为:
  1
 / \
2 - 3

示例 2:

输入: [[1,2], [2,3], [3,4], [1,4], [1,5]]
输出: [1,4]
解释: 给定的无向图为:
5 - 1 - 2
    |   |
    4 - 3

注意:

输入的二维数组大小在 3 到 1000。 二维数组中的整数在1到N之间,其中N是输入数组的大小。 更新(2017-09-26): 我们已经重新检查了问题描述及测试用例,明确图是无向 图。对于有向图详见冗余连接II。对于造成任何不便,我们深感歉意。

class Solution {
     int[] f;
    boolean union(int a,int b){
        int n=0;
        //找a的父级
        while(a!=f[a]){
            a=f[a];
            n++;
        }
        //找b的父级
        while(b!=f[b]){
            b=f[b];
            n--;
        }
        //如果相等,就可以跳出了,
        if(a==b){
            return false;
        }
        //不相等,n>0说明a的深度高,就把a给f[b]
        //反之,b的深度高,就把b给f[a]
        if(n>0){
            f[b]=a;
        }else{
            f[a]=b;
        }
        return true;
    }
    public int[] findRedundantConnection(int[][] edges) {
        f=new int[edges.length+1];
        for(int i=0;i<f.length;i++){
            f[i]=i;
        }
        for(int[] e:edges){
            if(!union(e[0],e[1])){
                return e;
            }
        }
        return new int[]{};
    }
}

685. 冗余连接 II

在本问题中,有根树指满足以下条件的有向图。该树只有一个根节点,所有其他节点都是该根节点的后继。每一个节点只有一个父节点,除了根节点没有父节点。

输入一个有向图,该图由一个有着N个节点 (节点值不重复1, 2, ..., N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。

结果图是一个以边组成的二维数组。 每一个边 的元素是一对 [u, v],用以表示有向图中连接顶点 u and v和顶点的边,其中父节点u是子节点v的一个父节点。

返回一条能删除的边,使得剩下的图是有N个节点的有根树。若有多个答案,返回最后出现在给定二维数组的答案。

示例 1:

输入: [[1,2], [1,3], [2,3]]
输出: [2,3]
解释: 给定的有向图如下:
  1
 / \
v   v
2-->3

示例 2:

输入: [[1,2], [2,3], [3,4], [4,1], [1,5]]
输出: [4,1]
解释: 给定的有向图如下:
5 <- 1 -> 2
     ^    |
     |    v
     4 <- 3

注意:

二维数组大小的在3到1000范围内。 二维数组中的每个整数在1到N之间,其中 N 是二维数组的大小。

class Solution {
      public int[] findRedundantDirectedConnection(int[][] edges) {
        int[] path = new int[edges.length+1];
        Arrays.fill(path, -1);
        int path2 = -1;
        for(int i = 0; i < edges.length; i++) {
            if (path[edges[i][1]] != -1) {
                path2 = i;
            } else {
                path[edges[i][1]] = i;
            }
        }
        //边中出现了入度为2的点
        if (path2 != -1) {
            int node = edges[path2][1];
            int firstEdge = path[node];
            int temp = firstEdge;
            while (temp != -1) {
                int index = edges[temp][0];
                temp = path[index];
                if (index == node) {
                    //找到第一条边引发的环
                    return edges[firstEdge];
                }
            }
            return edges[path2];//前一条边无环则直接删除后一条边
        }
        //所有点的入度都为1,必有环,要删除环中最后输入的边
        boolean[] visited = new boolean[edges.length+1];
        Arrays.fill(visited, false);
        int index = 1;
        while (!visited[index]) {
            visited[index] = true;
            int firstEdge = path[index];
            index = edges[firstEdge][0];
        }
        int result = path[index];
        int temp = edges[result][0];
        int edgeNum = 0;
        while (temp != index) {
            edgeNum = path[temp];
            result = Math.max(result, edgeNum);
            temp = edges[edgeNum][0];
        }    
        return edges[result];
    }
}

686. 重复叠加字符串匹配

给定两个字符串 A 和 B, 寻找重复叠加字符串A的最小次数,使得字符串B成为叠加后的字符串A的子串,如果不存在则返回 -1。

举个例子,A = "abcd",B = "cdabcdab"。

答案为 3, 因为 A 重复叠加三遍后为 “abcdabcdabcd”,此时 B 是其子串;A 重复叠加两遍后为"abcdabcd",B 并不是其子串。

注意:

A 与 B 字符串的长度在1和10000区间范围内。

class Solution {
   public int repeatedStringMatch(String A, String B) {
        
        
        //滚轮? A作为滚轮
        char[] a = A.toCharArray();
        char[] b = B.toCharArray();
        for(int i=0;i<a.length;i++){
            int len = loop(a,b,i);
            if(len > 0){// 
                int count = 1;
                
                len = B.length() - a.length + i;
                count += len/a.length;
                count += len%a.length > 0 ? 1 : 0;
                return count;
            }else if(len + a.length <= 0){
                return -1;
            }
        }
        return -1;
        
    }
    //使用a滚轮印刷b,start为起始点
    public int loop(char[] a,char[] b,int start){
        int count = start;
        for(char c : b){
            if(a[start % a.length] != c){
                return count - start;
            }
            start++;
        }
        return 1; 
    }
}

687. 最长同值路径

给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。 这条路径可以经过也可以不经过根节点。

注意:两个节点之间的路径长度由它们之间的边数表示。

示例 1:

输入:

   5
     / \
    4   5
   / \   \
  1   1   5

输出:

2 示例 2:

输入:

    1
     / \
    4   5
   / \   \
  4   4   5

输出:

2 注意: 给定的二叉树不超过10000个结点。 树的高度不超过1000。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    private int maxL = 0;
    
    public int longestUnivaluePath(TreeNode root) {
       
        if(root == null) return 0;
        getMaxL(root, root.val);
        return maxL;
    }
    
    private int getMaxL(TreeNode r, int val) {
        if(r == null) return 0;
        int left = getMaxL(r.left, r.val);
        int right = getMaxL(r.right, r.val);
        maxL = Math.max(maxL, left+right); // 路径长度为节点数减1所以此处不加1
        if(r.val == val) // 和父节点值相同才返回以当前节点所能构成的最长通知路径长度, 否则返回0
            return Math.max(left, right) + 1;
        return 0;
    }
}

688. “马”在棋盘上的概率

已知一个 NxN 的国际象棋棋盘,棋盘的行号和列号都是从 0 开始。即最左上角的格子记为 (0, 0),最右下角的记为 (N-1, N-1)。

现有一个 “马”(也译作 “骑士”)位于 (r, c) ,并打算进行 K 次移动。

如下图所示,国际象棋的 “马” 每一步先沿水平或垂直方向移动 2 个格子,然后向与之相垂直的方向再移动 1 个格子,共有 8 个可选的位置。

在这里插入图片描述

现在 “马” 每一步都从可选的位置(包括棋盘外部的)中独立随机地选择一个进行移动,直到移动了 K 次或跳到了棋盘外面。

求移动结束后,“马” 仍留在棋盘上的概率。

示例:

输入: 3, 2, 0, 0 输出: 0.0625 解释: 输入的数据依次为 N, K, r, c 第 1 步时,有且只有 2 种走法令 “马” 可以留在棋盘上(跳到(1,2)或(2,1))。对于以上的两种情况,各自在第2步均有且只有2种走法令 “马” 仍然留在棋盘上。 所以 “马” 在结束后仍在棋盘上的概率为 0.0625。

注意:

N 的取值范围为 [1, 25] K 的取值范围为 [0, 100] 开始时,“马” 总是位于棋盘上

class Solution {
    int[][] move = { { 1, 2 }, { 1, -2 }, { 2, 1 }, { 2, -1 }, { -1, 2 }, { -1, -2 }, { -2, 1 }, { -2, -1 } };
	double[][][] dp;

	public double knightProbability(int N, int K, int r, int c) {
		dp = new double[N][N][K + 1];
		if (K == 0)
			return 1;
		return dfs(N, K, r, c);
	}

	private double dfs(int N, int K, int r, int c) {
		if (dp[r][c][K] != 0)
			return dp[r][c][K];
		double res = 0;
		for (int i = 0; i < 8; i++) {
			int r1 = r + move[i][0];
			int c1 = c + move[i][1];
			if (r1 >= 0 && r1 < N && c1 >= 0 && c1 < N) {
				res += (K == 1 ? 1 : dfs(N, K - 1, r1, c1));
			}
		}
		return dp[r][c][K] = res / 8;
	}
}

689. 三个无重叠子数组的最大和

给定数组 nums 由正整数组成,找到三个互不重叠的子数组的最大和。

每个子数组的长度为k,我们要使这3*k个项的和最大化。

返回每个区间起始索引的列表(索引从 0 开始)。如果有多个结果,返回字典序最小的一个。

示例:

输入: [1,2,1,2,6,7,5,1], 2 输出: [0, 3, 5] 解释: 子数组 [1, 2], [2, 6], [7, 5] 对应的起始索引为 [0, 3, 5]。 我们也可以取 [2, 1], 但是结果 [1, 3, 5] 在字典序上更大。 注意:

nums.length的范围在[1, 20000]之间。 nums[i]的范围在[1, 65535]之间。 k的范围在[1, floor(nums.length / 3)]之间。

class Solution {
   private int maxSum;

    public int[] maxSumOfThreeSubarrays(int[] nums, int k) {
       

        int n = nums.length;

        int[] sum = new int[n+1], left = new int[n], right = new int[n];

        for(int i=0; i<n; i++){
            sum[i+1] = sum[i]+nums[i];
        }
        //从左面筛选
        for(int i=k, leftmax = sum[k]-sum[0]; i<n ;i++){
            if(sum[i+1]-sum[i+1-k] > leftmax){
                leftmax = sum[i+1] - sum[i+1-k];
                left[i] = i+1-k;
            }else{
                left[i] = left[i-1];
            }
        }
        //右面筛选
        right[n-k] = n-k;
        for(int i=n-k-1, rightMax = sum[n]-sum[n-k]; i>=0; i--){
            if(sum[i+k]-sum[i]>= rightMax){
                right[i] = i;
                rightMax = sum[i+k] - sum[i];
            }else{
                right[i] = right[i+1];
            }
        }
        //去中间找,然后记录总和
        int maxsum = 0; int[] result = new int[3];
        for(int i=k; i<=n-2*k; i++){
            int l = left[i-1], r = right[i+k];
            int total = (sum[i+k]-sum[i]) + (sum[l+k] - sum[l]) + (sum[r+k]-sum[r]);
            if(total>maxsum){
                maxsum = total;
                result[0] = l; result[1] = i; result[2] =r;
            }
        }

        return result;

       
    } 
}

690. 员工的重要性

给定一个保存员工信息的数据结构,它包含了员工唯一的id,重要度 和 直系下属的id。

比如,员工1是员工2的领导,员工2是员工3的领导。他们相应的重要度为15, 10, 5。那么员工1的数据结构是[1, 15, [2]],员工2的数据结构是[2, 10, [3]],员工3的数据结构是[3, 5, []]。注意虽然员工3也是员工1的一个下属,但是由于并不是直系下属,因此没有体现在员工1的数据结构中。

现在输入一个公司的所有员工信息,以及单个员工id,返回这个员工和他所有下属的重要度之和。

示例 1:

输入: [[1, 5, [2, 3]], [2, 3, []], [3, 3, []]], 1 输出: 11 解释: 员工1自身的重要度是5,他有两个直系下属2和3,而且2和3的重要度均为3。因此员工1的总重要度是 5 + 3 + 3 = 11。 注意:

一个员工最多有一个直系领导,但是可以有多个直系下属 员工数量不超过2000。

/*
// Employee info
class Employee {
    // It's the unique id of each node;
    // unique id of this employee
    public int id;
    // the importance value of this employee
    public int importance;
    // the id of direct subordinates
    public List<Integer> subordinates;
};
*/

class Solution {
  int ans = 0;
    
    Map<Integer, Employee> map = new HashMap<>();
    
    public int getImportance(List<Employee> employees, int id) {
        for (int i = 0; i < employees.size(); i++) {
            map.putIfAbsent(employees.get(i).id, employees.get(i));
        }
        dfs(map.get(id));
        return ans;
    }
    
    private void dfs(Employee employee) {
        if (employee == null) return;
        ans += employee.importance;
        for (int subId : employee.subordinates) {
            dfs(map.get(subId));
        }
    }
}