刷leetCode算法题+解析(四十六)

查询后的偶数和

题目:给出一个整数数组 A 和一个查询数组 queries。对于第 i 次查询,有 val = queries[i][0], index = queries[i][1],我们会把 val 加到 A[index] 上。然后,第 i 次查询的答案是 A 中偶数值的和。(此处给定的 index = queries[i][1] 是从 0 开始的索引,每次查询都会永久修改数组 A。)返回所有查询的答案。你的答案应当以数组 answer 给出,answer[i] 为第 i 次查询的答案。

示例:
输入:A = [1,2,3,4], queries = [[1,0],[-3,1],[-4,0],[2,3]]
输出:[8,6,2,4]
解释:
开始时,数组为 [1,2,3,4]。
将 1 加到 A[0] 上之后,数组为 [2,2,3,4],偶数值之和为 2 + 2 + 4 = 8。
将 -3 加到 A[1] 上之后,数组为 [2,-1,3,4],偶数值之和为 2 + 4 = 6。
将 -4 加到 A[0] 上之后,数组为 [-2,-1,3,4],偶数值之和为 -2 + 4 = 2。
将 2 加到 A[3] 上之后,数组为 [-2,-1,3,6],偶数值之和为 -2 + 6 = 4。
提示:

1 <= A.length <= 10000
-10000 <= A[i] <= 10000
1 <= queries.length <= 10000
-10000 <= queries[i][0] <= 10000
0 <= queries[i][1] < A.length

思路:这道题感觉首先每次都暴力加肯定是不太合理。目前的思路先算出偶数和,然后每次每个元素改动在这基础上改。
emmmm...我用一种直接的办法实现了,直接贴代码:

class Solution {
    public int[] sumEvenAfterQueries(int[] A, int[][] queries) {
        int[] res = new int[queries.length];
        int sum = 0;
        for(int i : A){
            if(i%2==0) sum += i;
        }
        int idx = 0;
        for(int[] i : queries){
            int old = A[i[1]];
            A[i[1]] = old+i[0];
            if(old%2==0)  {
                sum = sum-old;
            } 
            if(A[i[1]]%2==0){
                sum = sum+A[i[1]];               
            }
            res[idx] = sum;
            idx++;
        }
        return res;
    }
}

但是性能很是可怜,我要好好理理思路,看看哪里写的有问题。
呵,我就是突然想起了昨天那招用二进制来判断奇数偶数的小技巧了。所以刚刚灵机一动改了一下,结果就这么小小的改动一下子从超过百分之四十多到超过百分之九十九,,,我贴上最新版代码:

class Solution {
    public int[] sumEvenAfterQueries(int[] A, int[][] queries) {
        int[] res = new int[queries.length];
        int sum = 0;
        for(int i : A){
            if((i&1)==0) sum += i;
        }
        int idx = 0;
        for(int[] i : queries){
            int old = A[i[1]];
            A[i[1]] = old+i[0];
            if((old&1)==0)  {
                sum = sum-old;
            } 
            if((A[i[1]]&1)==0){
                sum = sum+A[i[1]];               
            }
            res[idx] = sum;
            idx++;
        }
        return res;
    }
}

数组形式的整式加法

题目:对于非负整数 X 而言,X 的数组形式是每位数字按从左到右的顺序形成的数组。例如,如果 X = 1231,那么其数组形式为 [1,2,3,1]。给定非负整数 X 的数组形式 A,返回整数 X+K 的数组形式。

示例 1:
输入:A = [1,2,0,0], K = 34
输出:[1,2,3,4]
解释:1200 + 34 = 1234
示例 2:
输入:A = [2,7,4], K = 181
输出:[4,5,5]
解释:274 + 181 = 455
示例 3:
输入:A = [2,1,5], K = 806
输出:[1,0,2,1]
解释:215 + 806 = 1021
示例 4:
输入:A = [9,9,9,9,9,9,9,9,9,9], K = 1
输出:[1,0,0,0,0,0,0,0,0,0,0]
解释:9999999999 + 1 = 10000000000
提示:

1 <= A.length <= 10000
0 <= A[i] <= 9
0 <= K <= 10000
如果 A.length > 1,那么 A[0] != 0

思路:这道题很有怀念感啊,一开始刷算法接触过类似的,主要是进位问题。不要妄想换成数字操作。我血的教训告诉我给定的数组不定几十位呢~~所以还是老老实实的处理吧。目前的想法,给定值加到最后一位,然后从后往前遍历,有进位则进位。就酱。我去代码实现了。
我刚刚又看了下题目,是有进位的,所以可能和原数组长度都不一样,所以这里不应该在原数组上操作,直接list存结果就好了。那这道题更好做了。
好了,思路是对的,代码不困难,一次ac切及格(超过百分之九十四的人),我直接贴代码了:

class Solution {
    public List addToArrayForm(int[] A, int K) {
        LinkedList res = new LinkedList();
        int l = A.length-1;
        A[l] = A[l]+K;
        for(int i = l;i>=0;i--){
            int n = A[i];
            res.addFirst(n%10);
            if(i!=0 && n/10!=0){
                A[i-1] = A[i-1]+n/10;
            }
            if(i==0 && n/10!=0){     
                n = n/10;                       
                while(n>=10){
                    res.addFirst(n%10);  
                    n = n/10;                                     
                } 
                res.addFirst(n);               
            } 
        }
        return res;
    }
}

二叉树的堂兄弟节点

题目:在二叉树中,根节点位于深度 0 处,每个深度为 k 的节点的子节点位于深度 k+1 处。如果二叉树的两个节点深度相同,但父节点不同,则它们是一对堂兄弟节点。我们给出了具有唯一值的二叉树的根节点 root,以及树中两个不同节点的值 x和 y。只有与值 x 和 y 对应的节点是堂兄弟节点时,才返回 true。否则,返回 false。
题目截图

提示:

二叉树的节点数介于 2 到 100 之间。
每个节点的值都是唯一的、范围为 1 到 100 的整数。

思路:讲真,二叉树的题目做的都好笑,堂兄弟,下一个是不是堂姐妹了?其实这个判断就是两点:1.深度,2.是否是一个父节点。都不是很难实现的。老规矩,遍历,遍历出两个给定元素的深度和父节点,比较是否深度一样,父节点一样就行了,我去实现了。
0 0 实现了,思路也还凑合。。。就是性能不如人意,ims,只超过了百分之七十五。我先贴上代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    int f = 0;
    public boolean isCousins(TreeNode root, int x, int y) {
        int xd = getNodeD(root,x);
        int f1 = f;
        int yd = getNodeD(root,y);   
        return xd==yd && f1!=f;
    }
    public int getNodeD(TreeNode root, int n){
        if(root==null) return -100;
        if(root.left!=null && root.left.val==n){
            f = root.val;
            return 1;
        }
        if(root.right!=null && root.right.val==n){
            f = root.val;
            return 1;
        }
        return Math.max(getNodeD(root.left,n)+1,getNodeD(root.right,n)+1);
    }
}

其实就是我刚刚的思路,这个题就是看深度,看父节点值。因为每一个节点都是唯一的,所以很好判断。不过我觉得我性能不好的原因可能就是找到正确答案了另一边还要递归算结果?反正就是这里处理的不好,我寻思寻思怎么处理。
好的吧,没好想法了,我还是直接看性能排行第一的代码吧。额,仅限于能看懂的情况,可能是思路不同,反正换成我我不会这么写,感觉没有我这种明确。当时写代码我也考虑过是分开求xy还是一起。觉得一起求太乱了才分开的。贴出来共享下:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    int depth1=0;
    int depth2=0;
    TreeNode parent1=null;
    TreeNode parent2 = null;
    public boolean isCousins(TreeNode root, int x, int y) {        
        dfs(root,root,x,y,0);
        if( depth1 == depth2 && parent1 != parent2) return true;
        else return false;
    }

    void dfs(TreeNode root,TreeNode parent, int x, int y,int depth)
    {
         if( root == null) return ;
         if( x == root.val ) {depth1 = depth;parent1= parent;}
         if( y == root.val ) { depth2 = depth;parent2= parent;}
         if( parent1 !=null && parent2 !=null) return;
         dfs(root.left,root,x,y,depth+1);
         dfs(root.right,root,x,y,depth+1);
    }
}

腐烂的橘子

题目:在给定的网格中,每个单元格可以有以下三个值之一:
值 0 代表空单元格;
值 1 代表新鲜橘子;
值 2 代表腐烂的橘子。
每分钟,任何与腐烂的橘子(在 4 个正方向上)相邻的新鲜橘子都会腐烂。返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1。
题目截图

提示:

1 <= grid.length <= 10
1 <= grid[0].length <= 10
grid[i][j] 仅为 0、1 或 2

思路:这道题我见过啊,好像是之前的一个渲染还是像素啥的,都是上下左右可以扩散,只不过那个题是最后扩散的结果,这个题是扩散需要的时间,啧啧。。。其实这种题我感觉也没啥特别好的技巧,就是计算呗。然后这个题 的话,如果直系都是空肯定就不会被腐烂。我画了个草图,一看就能明白

左下角的橘子不会烂

所以这道题分两种情况:

  1. 有真空隔离的橘子(上下左右是0.)则直接返回-1、
  2. 没有真空隔离的橘子就计算时间咯

其实还能细分几种情况:没有好橘子(例如示例三)则直接返回0;
没有坏橘子就不能腐烂,所以也直接是-1.
剩下的还没想到,我先按照这几个情况写代码,没想法的情况测试案例会告诉我。
说一下随着做随着思路的变化:我之前的想法并不好实现,因为这个每一次扩散,要保证这一分钟扩散的不会扩散,很麻烦,目前能想到的就是两个数组来回倒,但是真的真的太麻烦,然后我就盯着题目开始想是不是我思路有什么问题,现在看出点心得不知道对不对。首先一个橘子想扩散到斜对角的橘子需要两秒。所以我们是不是可以根据好橘子与怀橘子的相差最大值来判断?我打算按照这个思路去看看。
很好,经历了一个小时总算做出来了,这里说一下,斜对角的判断是不靠谱的。反正我理来理去没理明白,所以最终还是数组来回来去复制了。直接贴代码吧:

class Solution {
    public int orangesRotting(int[][] grid) {       
        int n = orange(grid);
        return n>=0?n:-1;
    }
    //扩散方法
    public int orange(int[][] grid){
        boolean flag = false;
        int[][] temp = cArray(grid);
        int l = grid.length;
        int r = grid[0].length;
        for(int i=0;i0 && temp[i-1][j]==1) {
                        grid[i-1][j] = 2;
                        flag = true;
                    }
                    if(i0 && temp[i][j-1]==1) {
                        grid[i][j-1] = 2;
                        flag = true;
                    }
                    if(j

一边写一遍心里没底,怀疑我思路错了,不然怎么会这么麻烦,但是提交性能超过百分之九十九。所以。。。就酱吧。这道题可以过了。

找到小镇的法官

题目:在一个小镇里,按从 1 到 N 标记了 N 个人。传言称,这些人中有一个是小镇上的秘密法官。如果小镇的法官真的存在,那么:
小镇的法官不相信任何人。
每个人(除了小镇法官外)都信任小镇的法官。
只有一个人同时满足属性 1 和属性 2 。
给定数组 trust,该数组由信任对 trust[i] = [a, b] 组成,表示标记为 a 的人信任标记为 b 的人。如果小镇存在秘密法官并且可以确定他的身份,请返回该法官的标记。否则,返回 -1。

示例 1:
输入:N = 2, trust = [[1,2]]
输出:2
示例 2:
输入:N = 3, trust = [[1,3],[2,3]]
输出:3
示例 3:
输入:N = 3, trust = [[1,3],[2,3],[3,1]]
输出:-1
示例 4:
输入:N = 3, trust = [[1,2],[2,3]]
输出:-1
示例 5:
输入:N = 4, trust = [[1,3],[1,4],[2,3],[2,4],[4,3]]
输出:3
提示:

1 <= N <= 1000
trust.length <= 10000
trust[i] 是完全不同的
trust[i][0] != trust[i][1]
1 <= trust[i][0], trust[i][1] <= N

思路:我看了前半段差点以为是狼人杀了。哈哈,说一下这个题吧,目前的想法,首先法官不信任任何人,所以这个法官不会在子数组的0号位出现。这打算用set来判断法官是否出现过。其次这个全部人都要信任,一开始我想用map计数,后来觉得既然是都是数组表示,自然用数组更好了。从而set我也决定用数组下标代替数值了。我去实现下看看
做是做出来了,完全不知道为啥性能百分之五十都没超过,贴第一个代码:

class Solution {
    public int findJudge(int N, int[][] trust) {
        if(trust.length==0 && N==1) return 1;
        //投票人 
        int[] p = new int[N+1];
        //票数
        int[] vote = new int[N+1];
        for(int[] i : trust){
            p[i[0]] = i[0];
            vote[i[1]] = vote[i[1]]+1;
        }
        for(int i=0;i<=N;i++){
            if(vote[i]==N-1){
                if(p[i]==0) return i;
            }
        }
        return -1;        
    }
}

讲真,理解不了性能为啥这么低。勉勉强强优化到3ms,超过百分之八十五的人:

class Solution {
    public int findJudge(int N, int[][] trust) {
        if(trust.length

剩下的直接看排行第一的代码吧,我是无能为力了。
好了,看了别人的代码,大同小异,细节上我是for新循环,人家是老的,不知道有没有关系,反正是性能好1ms:

class Solution {
    public int findJudge(int N, int[][] trust) {
        int[] outCount = new int[N+1];
        int[] inCount = new int[N+1];

        for (int[] fromTo : trust) {
            outCount[fromTo[0] ]++;
            inCount[fromTo[1]]++;
        }


        int res = -1;
        for (int i = 1; i <= N; i++) {
            if (outCount[i] == 0 && inCount[i] == N - 1) {
                res = i;
                break;
            }
        }

        return res;
    }
}

车的可用捕获量

题目:在一个 8 x 8 的棋盘上,有一个白色车(rook)。也可能有空方块,白色的象(bishop)和黑色的卒(pawn)。它们分别以字符 “R”,“.”,“B” 和 “p” 给出。大写字符表示白棋,小写字符表示黑棋。车按国际象棋中的规则移动:它选择四个基本方向中的一个(北,东,西和南),然后朝那个方向移动,直到它选择停止、到达棋盘的边缘或移动到同一方格来捕获该方格上颜色相反的卒。另外,车不能与其他友方(白色)象进入同一个方格。返回车能够在一次移动中捕获到的卒的数量。
题目截图

题目截图

题目截图

提示:

board.length == board[i].length == 8
board[i][j] 可以是 'R','.','B' 或 'p'
只有一个格子上存在 board[i][j] == 'R'

思路:先吐个槽,这个题又没看懂,在群友的帮助下才明白题意的,感谢群友~~~一开始我以为是一次移动车能一次吃三个卒,怎么看怎么不懂,这个题的翻译是不是没学过语文啊?自己说表述:返回车能够在一次移动中捕获到的卒的数量。 其实明白题意还是很简单的,我目前最无脑的想法:先找到车,然后前后左右判断,over。我去实现了
emmmm....实现了,就是很奇怪啊我觉得char数组处理起来麻烦,所有偷懒用了字符串处理,本来以为会性能不好呢结果并没有,性能超过百分百,这个算是无心插柳吧,哈哈~我直接贴代码:

class Solution {
    public int numRookCaptures(char[][] board) {
        String x = "";
        int a = 0;
        int b = 0;
        for(int i = 0;i<8;i++){
            int n = new String(board[i]).indexOf("R");
            if(n!=-1){
                x = new String(board[i]);
                a = i;
                b = n;
            }
        }
        StringBuffer sb = new StringBuffer();
        for(int i = 0;i<8;i++){
           if(board[i][b]!='.') sb.append(board[i][b]);
        }
        int res = 0;
        x = x.replace(".","");
        if(x.indexOf("pR")!=-1) res++;
        if(x.indexOf("Rp")!=-1) res++;
        if(sb.toString().indexOf("pR")!=-1) res++;
        if(sb.toString().indexOf("Rp")!=-1) res++;
        return res;
    }
}

查找常用字符

题目:给定仅有小写字母组成的字符串数组 A,返回列表中的每个字符串中都显示的全部字符(包括重复字符)组成的列表。例如,如果一个字符在每个字符串中出现 3 次,但不是 4 次,则需要在最终答案中包含该字符 3 次。你可以按任意顺序返回答案。

示例 1:
输入:["bella","label","roller"]
输出:["e","l","l"]
示例 2:
输入:["cool","lock","cook"]
输出:["c","o"]
提示:
1 <= A.length <= 100
1 <= A[i].length <= 100
A[i][j] 是小写字母

思路:这道题就是单纯的判重?目前思路第一个和第二个字符串比较,重复的元素添加到数组,然后这个数组和第三个比较,最后数组中的元素就是每个字符都用到的元素。但是思路是这样怎么操作还是很有讲究的,可以用数组啊,可以用map啊啥啥的,反正我得出的经验要想要性能就得数组,所以我这里用数组了。因为不超过26字母。所以我这里下标代表字母,值代表出现的次数了,我去实现了。
emmmm....做出来了,一次过了,就是性能又差的离谱!先贴代码:

class Solution {
    int[] res ; 
    public List commonChars(String[] A) {
        res = new int[26];
        char[] c = A[0].toCharArray();
        for(char i : c){
            res[i-'a'] = res[i-'a']+1;
                        
        }
        for(int i = 1;i list = new ArrayList();
        for(int i = 0;i<26;i++){
            while(res[i]>0){
                char n = (char)(i+97);
                list.add(n+"");
                res[i]--;
            }
        }
        return list;
    }
}

真真的奇了怪了,今天觉得性能能不错的一运行只超过百分之四五十,感觉不怎么样的一运行百分之九十多甚至百分之一百。我这是有毒吧~~~
除了最后的数组转成list感觉不太合理剩下我觉得处理的不错啊,我好好想想怎么优化~~
好了,略改几个地方就过了及格线了,首先这个Math.min我换成了三目运算,其次这个减去'a'换成了减去97,,,我也不知道是哪块调对了,反正是超过百分之九十了。贴上最终版本代码:

class Solution {
    int[] res ; 
    public List commonChars(String[] A) {
        res = new int[26];
        char[] c = A[0].toCharArray();
        for(char i : c){
            res[i-97] = res[i-97]+1;
                        
        }
        for(int i = 1;i list = new ArrayList();
        for(int i = 0;i<26;i++){
            while(res[i]>0){
                char n = (char)(i+97);
                list.add(n+"");
                res[i]--;
            }
        }
        return list;
    }
}

然后今天的笔记就记到这里,如果稍微帮到你了记得点个喜欢点个关注~~
另外今天为止1000题以内的简单难度题目都刷过了,也算是个里程碑了~给自己点个赞!也祝大家工作顺顺利利!

你可能感兴趣的:(刷leetCode算法题+解析(四十六))