Page 10 of 61

LeetCode刷题【剑指 Offer 15】二进制中1的个数

编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为 汉明重量).)。

 

提示:

  • 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
  • 在 Java 中,编译器使用 二进制补码 记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3

 

示例 1:

输入:n = 11 (控制台输入 00000000000000000000000000001011)
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。

示例 2:

输入:n = 128 (控制台输入 00000000000000000000000010000000)
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。

示例 3:

输入:n = 4294967293 (控制台输入 11111111111111111111111111111101,部分语言中 n = -3)
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。

 

提示:

  • 输入必须是长度为 32二进制串

 

注意:本题与主站 191 题相同:https://leetcode-cn.com/problems/number-of-1-bits/

Related Topics
  • 位运算

  • 👍 255
  • 👎 0
  • 位运算
    简单
    1&1 = 1
    1&0 = 0
    0&0 = 0

    public class Solution {
        // you need to treat n as an unsigned value
        public int hammingWeight(int n) {
            int ans = 0;
            int p = 0;
            while (++p <= 32){
                ans += n & 1;
                n >>= 1;
            }
            return ans;
        }
    }

    LeetCode刷题【剑指 Offer 43】1~n 整数中 1 出现的次数

    输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。

    例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。

     

    示例 1:

    输入:n = 12
    输出:5
    

    示例 2:

    输入:n = 13
    输出:6

     

    限制:

    • 1 <= n < 2^31

    注意:本题与主站 233 题相同:https://leetcode-cn.com/problems/number-of-digit-one/

    Related Topics
    • 递归
    • 数学
    • 动态规划

  • 👍 343
  • 👎 0
  • 按位计算统计,(【左侧数值】当前位1【右侧数值】)
    直接拿一个数字31261分析下,过程如下

        3      1      2      6      1
                                    3126 + 1
                             3130
                      3200
                3000 + 261 + 1
        10000
    1. 最低位当前数字是1,这个位置上1粗线的次数由他前面的数字决定,所以我们可以非常明白的知道,这个时候这一位一共出现了3127次字符1
      • 分别为有前缀的3126 次
      • 和无前缀的 当数字n = 1的时候的1次
    2. 倒数第二位数字6,这个数字大于1,根据上面算最低位的时候的情况分析依据,我们可以知道这个位置一共出现了3130次,这个结果可以也可以分为两部分
      • 有前缀的时候3120种情况,数字xxx1x的情况,当前位前面有312种情况,而又因为当前位大于1,后面一位的0-9的10种情况,即为3120种
      • 无前缀的时候1019的10种情况
      • 这样,我们暂时把这部分换为另外一种算法,即为( 312 + 1 ) * 10 种。
    3. 再往前一位数字2,同前面的数字6的情况,因为大于1所以结果为(31 + 1) * 100
    4. 再次往前一位,数字1,虽然前面为1的情况我们已经分析过了,但是那个是不完整的,在这里,我们将完整分析下为1的时候的情况
      • 因为后面还有其他数字,整个31xxx区段是还没完全结束的,所以我们不能直接得到结果为4000
      • 那么前面部分的情况为012的3000种情况,也可以认为如果左侧数字是k,那么现在就有k000
      • 又因为之前说了这个31xxx区段是还没完全结束,所以应当还有当前位后面部分的261种情况,外加如果后面都为0的情况
      • 那么这个位置数字1出现的次数就是3000 + 261 + 1 = 3262
    5. 最高位为3,大于1,可以直白的知道有10000次出现了字符1,那么按照前面的规律,我们假定最高位为0,按照之前的方法可以得到( 0 + 1 ) * 10000
    6. 举例数据中没有出现的当前位为0的情况,这个就比较简单了,直接就是左侧数据出现的次数了
      • 如 30261 中0的位置,
      • 就是数字1xxx,11xxx,21xxx的3000种情况

    计算中间的问题

    我们在计算中使用了一个辅助变量10,100,100010000,这个数值是比入参数字大一位的

    又因为题目给的入参条件1 <= n < 2^31

    所以中间计算过程我就偷了个懒给转成了long型数据

    另外,葱低往高取每一位的数字的时候可以直接用余10,之后再除10的方法,我这边一开始是准备转成数组直接根据数组计算左右侧数据影响的当前位置1出现次数的,最后就还是没改了

    代码

    class Solution {
        /*
        3      1      2      6      1
                                    3127
                             3130
                      3200
                3000 + 261 + 1
         10000
         */
    
    
        final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
                99999999, 999999999, Integer.MAX_VALUE };
    
        public int countDigitOne(int n) {
            long ans = 0;
            long num = (long) n;
            int size = numSize(n);
            int[] arr = new int[size];
            int idx = size-1;
            while (n != 0){
                arr[idx] = n%10;
                n /= 10;
                idx--;
            }
            idx = size-1;
            long tmp = 10;
            while (idx >= 0){
                long r = 0;
                if (arr[idx] == 0){
                    r = ( num / tmp ) * ( tmp / 10 );
                }
                if (arr[idx] == 1){
                    r = ( num / tmp ) * ( tmp / 10 ) + ( num % (tmp / 10) ) + 1;
                }
                if (arr[idx] > 1){
                    r = ( (num / tmp)+1 ) * ( tmp / 10 );
                }
                ans += r;
                tmp *= 10;
                idx--;
            }
    
            return (int)ans;
        }
    
    
    
        static int numSize(int x) {
            for (int i=0; ; i++)
                if (x <= sizeTable[i])
                    return i+1;
        }
    }

    LeetCode刷题【794】有效的井字游戏

    给你一个字符串数组 board 表示井字游戏的棋盘。当且仅当在井字游戏过程中,棋盘有可能达到 board 所显示的状态时,才返回 true

    井字游戏的棋盘是一个 3 x 3 数组,由字符 ' ''X''O' 组成。字符 ' ' 代表一个空位。

    以下是井字游戏的规则:

    • 玩家轮流将字符放入空位(' ')中。
    • 玩家 1 总是放字符 'X' ,而玩家 2 总是放字符 'O'
    • 'X''O' 只允许放置在空位中,不允许对已放有字符的位置进行填充。
    • 当有 3 个相同(且非空)的字符填充任何行、列或对角线时,游戏结束。
    • 当所有位置非空时,也算为游戏结束。
    • 如果游戏结束,玩家不允许再放置字符。

     

    示例 1:

    输入:board = ["O  ","   ","   "]
    输出:false
    解释:玩家 1 总是放字符 "X" 。
    

    示例 2:

    输入:board = ["XOX"," X ","   "]
    输出:false
    解释:玩家应该轮流放字符。
    

    示例 3:

    输入:board = ["XOX","O O","XOX"]
    输出:true
    

     

    提示:

    • board.length == 3
    • board[i].length == 3
    • board[i][j]'X''O'' '
    Related Topics
    • 数组
    • 字符串

  • 👍 112
  • 👎 0
  • 略坑,又臭又长,可以看看

    1. XO数量情况符合 0 <= XO <= 1
    2. 输赢情况判断
      • X赢 :X三连数量为1个或者两个,O三连数量为0个, 此时因为X先手,必然有 数量统计结果XO == 1
      • O赢 : X三连数量为0个,O三连数量为1个,因为O后手,必然有 数量统计结果XO == 0
      • 两人都还没赢: X三连数量为0个,O三连数量为0个

    写二维坐标计算统计太麻烦了,我把board[0] + board[1] + board[2]拼起来了,
    用这样的一维坐标计算统计了

        0 1 2
        3 4 5
        6 7 8
    
        对应为
    
        0 1 2 3 4 5 6 7 8

    代码

    class Solution {
    
        char X = 'X';
        char O = 'O';
    
        public boolean validTicTacToe(String[] board) {
            int xCnt = 0;
            int oCnt = 0;
            String str = board[0] + board[1] + board[2];
            for (int i = 0; i < str.length(); i++) {
                if (str.charAt(i) == X){
                    xCnt++;
                }
                if (str.charAt(i) == O){
                    oCnt++;
                }
            }
            if (xCnt - oCnt == 1 || xCnt - oCnt == 0){
                int[] res = countLine(str);
                if (res[0] >=1 && res[1]==0){
                    //X赢
                    // 有个特殊情况,X是可以连成两个3连的
                    // X X X
                    // O O X
                    // O O X
                    return  xCnt - oCnt == 1;
                }else if(res[0]==0 && res[1]==1){
                    //O赢
                    return xCnt - oCnt == 0;
                }else if (res[0] == 0 && res[1] == 0){
                    //都还没赢 比如
                    // X O X
                    // O   O
                    // X O X
                    return true;
                }
            }
            return false;
        }
    
    
        public int[] countLine(String str){
            // 下标转换下,写起来方便
            // 0 1 2
            // 3 4 5
            // 6 7 8
            int[][] arr = new int[8][2];
            arr[0][0] = charCount(str , X, 0,1,2);
            arr[1][0] = charCount(str , X, 3,4,5);
            arr[2][0] = charCount(str , X, 6,7,8);
            arr[0][1] = charCount(str , O, 0,1,2);
            arr[1][1] = charCount(str , O, 3,4,5);
            arr[2][1] = charCount(str , O, 6,7,8);
            arr[3][0] = charCount(str , X, 0,3,6);
            arr[4][0] = charCount(str , X, 1,4,7);
            arr[5][0] = charCount(str , X, 2,5,8);
            arr[3][1] = charCount(str , O, 0,3,6);
            arr[4][1] = charCount(str , O, 1,4,7);
            arr[5][1] = charCount(str , O, 2,5,8);
            arr[6][0] = charCount(str , X, 0,4,8);
            arr[6][1] = charCount(str , O, 0,4,8);
            arr[7][0] = charCount(str , X, 2,4,6);
            arr[7][1] = charCount(str , O, 2,4,6);
            int[] res = new int[2];
            for (int[] ints : arr) {
                res[0] += ints[0]==3?1:0;
                res[1] += ints[1]==3?1:0;
            }
            return res;
        }
    
        public int charCount(String str, char c, int i1, int i2, int i3){
            int count = 0;
            count += str.charAt(i1)==c? 1 : 0;
            count += str.charAt(i2)==c? 1 : 0;
            count += str.charAt(i3)==c? 1 : 0;
            return count;
        }
    
    }

    LeetCode刷题【689】三个无重叠子数组的最大和

    给你一个整数数组 nums 和一个整数 k ,找出三个长度为 k 、互不重叠、且全部数字和(3 * k 项)最大的子数组,并返回这三个子数组。

    以下标的数组形式返回结果,数组中的每一项分别指示每个子数组的起始位置(下标从 0 开始)。如果有多个结果,返回字典序最小的一个。

     

    示例 1:

    输入:nums = [1,2,1,2,6,7,5,1], k = 2
    输出:[0,3,5]
    解释:子数组 [1, 2], [2, 6], [7, 5] 对应的起始下标为 [0, 3, 5]。
    也可以取 [2, 1], 但是结果 [1, 3, 5] 在字典序上更大。
    

    示例 2:

    输入:nums = [1,2,1,2,1,2,1,2,1], k = 2
    输出:[0,2,4]
    

     

    提示:

    • 1 <= nums.length <= 2 * 104
    • 1 <= nums[i] < 216
    • 1 <= k <= floor(nums.length / 3)
    Related Topics
    • 数组
    • 动态规划

  • 👍 320
  • 👎 0
  • 动态规划【图解】

    解题思路

    1. 根据题面长度k区间,自然会想到的是滑动窗口来构建一个新的数组sumK
    2. 接下来的思路稍微棘手点,但是如果你做过42. 接雨水,并且是用动态规划的方法来做的话,那么就会非常好理解了学了好久的动态规划再来做这题
    3. 接雨水的思路是:找到左侧的最大值,同时找到右侧的最大值
    4. 而这里,不能单纯的从左边右边移动一位,而是对应的需要移动距离k之外的最大值
    5. 看图示例

    1. 首先求得sumK数组 3 3 3 8 13 12 6
    2. 接下来分别求左侧和右侧的最大值,下标0,1没有左侧区间,下标5,6也没有右侧区间
    3. 左侧最大值从下标2开始,对应值为sumK[0],之后往右移动,下标5的左侧最大值为sumK[3],下标6同理
    4. 右侧最大值为从右往左,原理同左侧最大值
    5. 最终只遍历中间部分左右侧最大值都有的区间,找到和最大的那个值,并记下对应的下标索引

    代码

    class Solution {
    
        /*
        1,  2,  1,  2,  6,  7,  5,  1    k = 2
        3   3   3   8  13  12   6
                3   3   3   8  13
       13  13  13  12   6
         */
    
    
        public int[] maxSumOfThreeSubarrays(int[] nums, int k) {
            int[] sumK = new int[nums.length-k+1];
            for (int i = 0; i < k; i++) {
                sumK[0] += nums[i];
            }
            for (int i = 1; i < sumK.length; i++) {
                sumK[i] = sumK[i-1] + nums[i+k-1] - nums[i-1];
            }
    //        System.out.println(Arrays.toString(sumK));
            int[][] dp = new int[sumK.length][2];
            boolean flag = false;
            for (int i = k; i < sumK.length - k; i++) {
                int j = sumK.length - i - 1;
                if (flag){
                    dp[i][0] = sumK[i-k] > sumK[dp[i-1][0]] ? i-k : dp[i-1][0];
                    dp[j][1] = sumK[j+k] >= sumK[dp[j+1][1]] ? j+k : dp[j+1][1];
                }else{
                    flag = true;
                    dp[i][0] = i-k;
                    dp[j][1] = j+k;
                }
    //            System.out.println(sumK[dp[i][0]] + "    "+sumK[dp[j][1]]);
            }
    //        for (int[] ints : dp) {
    //            System.out.println(Arrays.toString(ints));
    //        }
            int[] res = new int[3];
            int max = 0;
            for (int i = k; i + k < sumK.length; i++) {
                int sum = sumK[i] + sumK[dp[i][0]] + sumK[dp[i][1]];
    //            System.out.println("i  "+i + "  sum"+ sum);
                if ( sum > max){
                    max = sum;
                    res[0] = dp[i][0];
                    res[1] = i;
                    res[2] = dp[i][1];
                }
            }
            return res;
        }
    }

    LeetCode刷题【剑指 Offer 49】丑数

    我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。

     

    示例:

    输入: n = 10
    输出: 12
    解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。

    说明:  

    1. 1 是丑数。
    2. n 不超过1690。

    注意:本题与主站 264 题相同:https://leetcode-cn.com/problems/ugly-number-ii/

    Related Topics
    • 哈希表
    • 数学
    • 动态规划
    • 堆(优先队列)

  • 👍 356
  • 👎 0
  • dp,算是dp吧

    解题思路

    此处撰写解题思路

    代码

    class Solution {
        public int nthUglyNumber(int n) {
            int[] dp = new int[n];
            dp[0] = 1;
            int[] numArr = new int[]{2,3,5};
            int[] pArr = new int[3];
            for (int i = 1; i < n; i++) {
                dp[i] = Math.min(Math.min(dp[pArr[0]]*numArr[0],dp[pArr[1]]*numArr[1]),dp[pArr[2]]*numArr[2]);
                for (int p = 0; p < pArr.length; p++) {
                    if (dp[i] == dp[pArr[p]]*numArr[p]){
                        pArr[p]++;
                    }
                }
            }
            return dp[n-1];
        }
    }

    LeetCode刷题【剑指 Offer 17】打印从1到最大的n位数

    输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。

    示例 1:

    输入: n = 1
    输出: [1,2,3,4,5,6,7,8,9]
    

     

    说明:

    • 用返回一个整数列表来代替打印
    • n 为正整数
    Related Topics
    • 数组
    • 数学

  • 👍 230
  • 👎 0
  • 打表
    简单打表

    class Solution {
        final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
                99999999, 999999999, Integer.MAX_VALUE };
        public int[] printNumbers(int n) {
            int max = sizeTable[n-1];
            int[] arr = new int[max];
            int i = -1;
            while (++i < max){
                arr[i] = i + 1;
            }
            return arr;
        }
    }

    这个sizeTable在java基础类库java.lang.Integer中就有,挺好用的

        final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
                                          99999999, 999999999, Integer.MAX_VALUE };
    
        // Requires positive x
        static int stringSize(int x) {
            for (int i=0; ; i++)
                if (x <= sizeTable[i])
                    return i+1;
        }

    LeetCode刷题【1034】边界着色

    给你一个大小为 m x n 的整数矩阵 grid ,表示一个网格。另给你三个整数 rowcolcolor 。网格中的每个值表示该位置处的网格块的颜色。

    两个网格块属于同一 连通分量 需满足下述全部条件:

    • 两个网格块颜色相同
    • 在上、下、左、右任意一个方向上相邻

    连通分量的边界 是指连通分量中满足下述条件之一的所有网格块:

    • 在上、下、左、右任意一个方向上与不属于同一连通分量的网格块相邻
    • 在网格的边界上(第一行/列或最后一行/列)

    请你使用指定颜色 color 为所有包含网格块 grid[row][col]连通分量的边界 进行着色,并返回最终的网格 grid

     

    示例 1:

    输入:grid = [[1,1],[1,2]], row = 0, col = 0, color = 3
    输出:[[3,3],[3,2]]

    示例 2:

    输入:grid = [[1,2,2],[2,3,2]], row = 0, col = 1, color = 3
    输出:[[1,3,3],[2,3,3]]

    示例 3:

    输入:grid = [[1,1,1],[1,1,1],[1,1,1]], row = 1, col = 1, color = 2
    输出:[[2,2,2],[2,1,2],[2,2,2]]

     

    提示:

    • m == grid.length
    • n == grid[i].length
    • 1 <= m, n <= 50
    • 1 <= grid[i][j], color <= 1000
    • 0 <= row < m
    • 0 <= col < n

     

    Related Topics
    • 深度优先搜索
    • 广度优先搜索
    • 数组
    • 矩阵

  • 👍 147
  • 👎 0
  • 海星,理解了题意的话就比较简单了(补充更新)
    给个图吧,题目写得太拗口了

    给出如图的矩阵grid[][],给出了对应坐标row = 3col = 4,和一个color随便是什么,并不重要

    要求把row, col位置连通分量的边界的值修改为color

    题目中并给出了连通分量的定义,即四个方向中任意一个方向上相邻,且值相等的区块,那么自然的边界的定义我们就能理解出来,如果上下左右4个方向上有任意一个和连通分量的值不同的话,那么就可以说明这个格子是边框,且如果这个连通分量格子本身是矩阵的边界的话,那么他也是边框

    那么按照题意,这个图上的应该要修改的位置就是如下

    理解了这层的话就好办了,直接上DFS

    代码

    class Solution {
    
        int[][] grid;
        int m;
        int n;
        int connectedComponentValue;
        int borderColor;
        boolean[][] visited;
        int[][] dir = new int[][]{{1,0},{-1,0},{0,1},{0,-1}};
    
        public int[][] colorBorder(int[][] grid, int row, int col, int color) {
            this.grid = grid;
            m = grid.length;
            n = grid[0].length;
            visited = new boolean[m][n];
            connectedComponentValue = grid[row][col];
            borderColor = color;
    
            dfs(row,col);
    
            return this.grid;
        }
    
        public void dfs(int row, int col){
            boolean borderFlag = false;
            visited[row][col] = true;
            for (int[] ints : dir) {
                int x = row + ints[0];
                int y = col + ints[1];
    
                if (isOutOfBoundary(x,y)){
                    borderFlag = true;
                    continue;
                }
                if (visited[x][y]){
                    continue;
                }
                if (grid[x][y] != connectedComponentValue){
                    borderFlag = true;
                    continue;
                }
                dfs(x,y);
            }
            if (borderFlag){
                grid[row][col] = borderColor;
            }
        }
    
    //    boolean isEdge(int row, int col){
    //        return row == 0 || col == 0 || row == m-1 || col==n-1;
    //    }
    
        boolean isOutOfBoundary(int row, int col){
            return row < 0 || col < 0 || row >= m || col >= n;
        }
    }

    补充更新个小细节

    1. isOutOfBoundary判断需要在visited[x][y]之前,为了防止发生越界错误
    2. grid[x][y] != connectedComponentValue判断需要在visited[x][y]之后,深搜会先改变染色掉之前访问到的格子,如果不先判断visited[x][y]的话会导致类似缩圈的效果,把整个连通区域全都染色