月度归档: 2021年8月

LeetCode刷题【190】 颠倒二进制位

颠倒给定的 32 位无符号整数的二进制位。

 

提示:

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

 

进阶:
如果多次调用这个函数,你将如何优化你的算法?

 

示例 1:

输入: 00000010100101000001111010011100
输出: 00111001011110000010100101000000
解释: 输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596     因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000

示例 2:

输入:11111111111111111111111111111101
输出:10111111111111111111111111111111
解释:输入的二进制串 11111111111111111111111111111101 表示无符号整数 4294967293,
     因此返回 3221225471 其二进制表示形式为 10111111111111111111111111111111 。

示例 1:

输入:n = 00000010100101000001111010011100
输出:964176192 (00111001011110000010100101000000)
解释:输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596 因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000

示例 2:

输入:n = 11111111111111111111111111111101
输出:3221225471 (10111111111111111111111111111111)
解释:输入的二进制串 11111111111111111111111111111101 表示无符号整数 4294967293,
     因此返回 3221225471 其二进制表示形式为 10111111111111111111111111111111 。

 

提示:

  • 输入是一个长度为 32 的二进制字符串
Related Topics
  • 位运算
  • 分治
  • \n
  • 👍 420
  • 👎 0
  • 题解

    头皮发麻。需要重新复习下二进制运算的相关知识

    与&:0&0=0 0&1=0 1&0=0 1&1=1

    或|:0|0=0 0|1=1 1|0=1 1|1=1

    异或^:0^0=0 0^1=1 1^0=1 1^1=0

    取反~:~1=0 ~0=1

    左移<<:左边的二进制位丢弃,右边补0

    右移>>:正数左补0,负数左补1,右边丢弃

    无符号左移<<<:左边的二进制位丢弃,右边补0

    无符号右移>>>:忽略符号位,空位都以0补齐

    按照题意,我们需要把原本32位二进制数的第0位移动到第31位,第1位移动到第30位,第2位移动到第29位,依次处理

    public int reverseBits(int n) {
            int rev = 0;
            for (int i = 0; i < 32; i++) {
                //tmp = n 与上 1
                //实际操作为
                // 00000000000000000000001010101110
                // 与上
                // 00000000000000000000000000000001
                // 的操作
                //结果为 n的最末尾数, 因为与后面的数字  前面都为0,最末尾为1,
                //根据与运算的规则 结果为
                // 00000000000000000000000000000001
                // 或者
                // 00000000000000000000000000000000
                int tmp = n&1;
                //把上面的结果tmp左移到左边对应的(31-i)位置
                //结果为
                // 10000000000000000000000000000000
                // 或者
                // 00000000000000000000000000000000
                tmp = tmp << (31-i);
                //初始的rev为
                // 00000000000000000000000000000000
                // 和tmp进行或运算 ,就可以将原来的末位二进制数移动到rev顺序的对应位置
                rev = rev|tmp;
                // 再把n往右无符号右移一位,去除掉了刚刚运算过的末位数
                n = n>>>1;
            }
    
            return rev;
        }

    emm还有一个,可以看下java.lang.Integer#reverse的方法

    /**
         * Returns the value obtained by reversing the order of the bits in the
         * two's complement binary representation of the specified {@code int}
         * value.
         *
         * @param i the value to be reversed
         * @return the value obtained by reversing order of the bits in the
         *     specified {@code int} value.
         * @since 1.5
         */
        public static int reverse(int i) {
            // HD, Figure 7-1
            i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;
            i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333;
            i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f;
            i = (i << 24) | ((i & 0xff00) << 8) |
                ((i >>> 8) & 0xff00) | (i >>> 24);
            return i;
        }

    LeetCode刷题【313】 超级丑数

    超级丑数 是一个正整数,并满足其所有质因数都出现在质数数组 primes 中。

    给你一个整数 n 和一个整数数组 primes ,返回第 n超级丑数

    题目数据保证第 n超级丑数32-bit 带符号整数范围内。

     

    示例 1:

    输入:n = 12, primes = [2,7,13,19]
    输出:32 
    解释:给定长度为 4 的质数数组 primes = [2,7,13,19],前 12 个超级丑数序列为:[1,2,4,7,8,13,14,16,19,26,28,32] 。

    示例 2:

    输入:n = 1, primes = [2,3,5]
    输出:1
    解释:1 不含质因数,因此它的所有质因数都在质数数组 primes = [2,3,5] 中。
    
     

    提示:

    • 1 <= n <= 106
    • 1 <= primes.length <= 100
    • 2 <= primes[i] <= 1000
    • 题目数据 保证 primes[i] 是一个质数
    • primes 中的所有值都 互不相同 ,且按 递增顺序 排列
    Related Topics
  • 数组
  • 哈希表
  • 数学
  • 动态规划
  • 堆(优先队列)
  • \n
  • 👍 193
  • 👎 0
  • 题解

    8月9日每日一题,解法思路同前面的264题LeetCode刷题【264】 丑数 II,考查多指针的用法。原本的264题中是给定了固定的3个丑数2,3,5,本题中的丑数范围为输入的数组范围,那么同样的解法,每个数字维护一个自己对应的指针位置。

    如果说一上来就做这题感觉理不清的话,可以试着先去做下LeetCode刷题【264】 丑数 II题,再回来看这题就非常明朗了

    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        public int nthSuperUglyNumber(int n, int[] primes) {
            int[] point = new int[primes.length];
            int[] res = new int[n];
            res[0] = 1;
            int k = 0;
            while (k<n-1){
                k++;
                int num = Integer.MAX_VALUE;
                for (int i = 0;i < primes.length;i++) {
                    num = Math.min(num,primes[i] * res[point[i]]);
                }
                for (int i = 0;i < primes.length;i++) {
                    if (primes[i] * res[point[i]] == num) {
                        point[i]++;
                    }
                }
                res[k] = num;
            }
            return res[k];
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)

    LeetCode刷题【264】 丑数 II

    给你一个整数 n ,请你找出并返回第 n丑数

    丑数 就是只包含质因数 23 和/或 5 的正整数。

     

    示例 1:

    输入:n = 10
    输出:12
    解释:[1, 2, 3, 4, 5, 6, 8, 9, 10, 12] 是由前 10 个丑数组成的序列。
    

    示例 2:

    输入:n = 1
    输出:1
    解释:1 通常被视为丑数。
    

     

    提示:

    • 1 <= n <= 1690
    Related Topics
  • 哈希表
  • 数学
  • 动态规划
  • 堆(优先队列)
  • \n
  • 👍 710
  • 👎 0
  • 题解

    先说明一个概念

    每个合数都可以写成几个质数(也可称为素数)相乘的形式 ,这几个质数就都叫做这个合数的质因数。如果一个质数是某个数的因数,那么就说这个质数是这个数的质因数;而这个因数一定是一个质数。(来自于百度百科)

    举例

    • 1没有质因子。
    • 5只有1个质因子,5本身。(5是质数)
    • 6的质因子是2和3。(6 = 2 × 3)
    • 2、4、8、16等只有1个质因子:2。(2是质数,4 =2²,8 = 2³,如此类推)
    • 10有2个质因子:2和5。(10 = 2 × 5)

    所以我们知道了,所求数字即为,给定的2、3、5相互组合相乘得到的结果,可以重复相乘,即可以使2x2x2x3x3x5x5x5这样的组合。

    而题中所求的第n位即是将所得结果按照从小到大的顺序有序排列的第n位数字,且根据质因数的概念,第一位一定是数字1。

    那么,我们可以按照题意按部就班的来求解,结果集的第一位是1,那么结果集此时是【1】

    来到第二位。可以是前面的结果集第一位的1×2,也可以是1×3,也可以是1×5,但是按照从小到大的顺序,应该是1×2,此时结果集为【1,2】

    来到第三位,因为前面的1×2已经算过,可以直接忽略,那么我们要比较的就是2×2(此处的2是前面得到1×2的结果的2,不是自然数递增的2),和上次剩下的1×3,1×5,显然结果应该是3,此时的结果集【1,2,3】

    来到第四位需要比较的是2×2,2×3,1×5,显然应该是4,此时的结果集为【1,2,3,4】

    后面依次:

    第五位 3×2,2×3,1×5,结果集【1,2,3,4,5

    第六位 3×2,2×3,2×5,结果集是【1,2,3,4,5,6】,而此时的3×2和2×3都能得到6这个结果,显然是重复的,应该只记一次

    第七位 4×2,3×3,2×5,结果集是【1,2,3,4,5,6,8

    第八位 5×2,3×3,2×5,结果集【1,2,3,4,5,6,8,9

    第九位 5×2,4×3,2×5,结果集【1,2,3,4,5,6,8,9,10

    所以自己手动算了这么多步骤,规律应该可以看出来了,数字2,3,5分别在结果集中维护了一个指针,一开始都指向了索引0。结果集中第一位索引0上的值为1。

    然后每次迭代找出2,3,5与索引位置上的值相乘的乘积中最小的一个,将这个结果放入结果集中的下一位,并将当前对应数字的指针位置沿着索引往后移动一位。如果有多个数与索引位置的乘积相等都为最小值,那么这些数字的指针都往后移动一位。

    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        public int nthUglyNumber(int n) {
            int[] res = new int[n];
            res[0] = 1;
            int p2 = 0, p3 = 0, p5 =0;
            int k = 1;
            while (k < n){
                int num = Math.min(Math.min(res[p2]*2,res[p3]*3),res[p5]*5);
                if (res[p2]*2==num){
                    p2++;
                }
                if (res[p3]*3==num){
                    p3++;
                }
                if (res[p5]*5==num){
                    p5++;
                }
                res[k] = num;
                k++;
            }
            return res[k-1];
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)

    当前题目中固定了丑数范围为2,3,5,更多的非固定丑数范围的题目详见LeetCode刷题【313】 超级丑数

    新更新内容:

    重新做了LeetCode刷题【263】丑数,并再经过了前一段时间动态规划相关题目的练习之后,得到新的理解与启示

    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] = pArr[p]+1;
                    }
                }
            }
            return dp[n-1];
        }
    }

    代码可能大同小异,不过在理解上,

    以一个现有的丑数序列为例[1,2,3,4,5,6,8,9,10,12],一开始其实只有一个初始的索引0位置的1。根据丑数的特性,我们知道丑数F(n)一定能由之前的某个丑数乘以2或者乘以3或者乘以5得到,那么究竟取哪个呢,因为要保持递增性,所以应该是较小的。

    而有了这个理解之后,在去看LeetCode刷题【313】 超级丑数的代码其实很容易从上面的新的代码能修改得到

    LeetCode刷题【847】访问所有节点的最短路径

    存在一个由 n 个节点组成的无向连通图,图中的节点按从 0n - 1 编号。

    给你一个数组 graph 表示这个图。其中,graph[i] 是一个列表,由所有与节点 i 直接相连的节点组成。

    返回能够访问所有节点的最短路径的长度。你可以在任一节点开始和停止,也可以多次重访节点,并且可以重用边。

     

    示例 1:

    输入:graph = [[1,2,3],[0],[0],[0]]
    输出:4
    解释:一种可能的路径为 [1,0,2,0,3]

    示例 2:

    输入:graph = [[1],[0,2,4],[1,3,4],[2],[1,2]]
    输出:4
    解释:一种可能的路径为 [0,1,4,2,3]
    

     

    提示:

    • n == graph.length
    • 1 <= n <= 12
    • 0 <= graph[i].length < n
    • graph[i] 不包含 i
    • 如果 graph[a] 包含 b ,那么 graph[b] 也包含 a
    • 输入的图总是连通图
    Related Topics
  • 位运算
  • 广度优先搜索
  • 动态规划
  • 状态压缩
  • \n
  • 👍 190
  • 👎 0
  • 题解

    
    import java.util.LinkedList;
    import java.util.Queue;
    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        public int shortestPathLength(int[][] graph) {
            int length = graph.length;
    
            Queue<int[]> queue = new LinkedList<>();
            boolean[][] visited = new boolean[length][1<<length];
            for (int i = 0; i < length; i++) {
                //  出发点,经过状态(状态压缩),步数
                queue.offer(new int[]{i,1<<i,0});
                visited[i][1<<i] = true;
            }
            while (!queue.isEmpty()){
                int[] current = queue.poll();
                int index = current[0];
                int status = current[1];
                int steps = current[2];
    
                if (status == (1 << length)-1){
                    //所有点都被访问过了
                    return steps;
                }
                for (int nextIndex : graph[index]) {
                    int nextIndexStatus = status | (1 << nextIndex);
                    if (!visited[nextIndex][nextIndexStatus]) {
                        queue.offer(new int[]{nextIndex, nextIndexStatus, steps + 1});
                        visited[nextIndex][nextIndexStatus] = true;
                    }
    
                }
    
    
    
            }
    
            return 0;
    
    
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)
    

    LeetCode刷题【611】有效三角形的个数

    给定一个包含非负整数的数组,你的任务是统计其中可以组成三角形三条边的三元组个数。

    示例 1:

    输入: [2,2,3,4]
    输出: 3
    解释:
    有效的组合是: 
    2,3,4 (使用第一个 2)
    2,3,4 (使用第二个 2)
    2,2,3
    

    注意:

    1. 数组长度不超过1000。
    2. 数组里整数的范围为 [0, 1000]。
    Related Topics
  • 贪心
  • 数组
  • 双指针
  • 二分查找
  • 排序
  • \n
  • 👍 229
  • 👎 0
  • 题解

    构成三角形的条件:两条较短的边的合大于最长的边长

    所以,先数组排序

    然后按条件求解

    class Solution {
        public int triangleNumber(int[] nums) {
            Arrays.sort(nums);
            int count = 0;
            for (int line1Index = nums.length - 1; line1Index >= 2; line1Index--) {
                for (int line2Index = line1Index - 1; line2Index >= 1; line2Index--) {
                    for (int line3Index = line2Index - 1; line3Index >= 0; line3Index--) {
                        if (nums[line3Index]+nums[line2Index]>nums[line1Index]){
                            count++;
                        }else{
                            break;
                        }
                    }
                }
            }
            return count;
        }
    }
    

    内部查找的方法可以替换成二分法,后面再补充

    LeetCode刷题【743】 网络延迟时间

    n 个网络节点,标记为 1 到 n

    给你一个列表 times,表示信号经过 有向 边的传递时间。 times[i] = (ui, vi, wi),其中 ui 是源节点,vi 是目标节点, wi 是一个信号从源节点传递到目标节点的时间。

    现在,从某个节点 K 发出一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回 -1

     

    示例 1:

    输入:times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2
    输出:2
    

    示例 2:

    输入:times = [[1,2,1]], n = 2, k = 1
    输出:1
    

    示例 3:

    输入:times = [[1,2,1]], n = 2, k = 2
    输出:-1
    

     

    提示:

    • 1 <= k <= n <= 100
    • 1 <= times.length <= 6000
    • times[i].length == 3
    • 1 <= ui, vi <= n
    • ui != vi
    • 0 <= wi <= 100
    • 所有 (ui, vi) 对都 互不相同(即,不含重复边)
    Related Topics
  • 深度优先搜索
  • 广度优先搜索
  • 最短路
  • 堆(优先队列)
  • \n
  • 👍 395
  • 👎 0
  • 题解

    
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Set;
    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
    
        private int totalTime = -1;
        private int[] arriveTime;
        private int[][] map;
    
    
        public int networkDelayTime(int[][] times, int n, int k) {
            map = new int[n+1][n+1];
            for (int i = 0; i < map.length; i++) {
                int[] tmp = new int[n+1];
                Arrays.fill(tmp,-1);
                map[i] =tmp;
            }
            arriveTime = new int[n+1];
            Arrays.fill(arriveTime,-1);
            //到自己的位置需要花费时间为0
            arriveTime[k] = 0;
            for (int[] time : times) {
                //time[0];源节点 time[1];目标节点 time[2];时间
                map[time[0]][time[1]] = time[2];
            }
            dfs(k,0);
            int arrivedCount = 0;
            for (int i = 1; i < arriveTime.length; i++) {
                //i=-1表示没有到达这个点,
                if (arriveTime[i]>=0){
                    arrivedCount++;
                    totalTime = Math.max(totalTime,arriveTime[i]);
                }
            }
            return arrivedCount==n?totalTime:-1;
        }
    
        private void dfs(int startPoint,int time){
            int[] branch = map[startPoint];
            //从出发点开始,寻找可以走的下一步
            for (int i = 0; i < branch.length; i++) {
                //不等于0的 说明这条路是通的,可以访问,。
                if (branch[i]>=0){
                    //走下一步的第二个条件,目标点没有走过 即还没记录过走到下一个点需要多久
                    //或者 到达目标点将要花费的时间比 现在 已知的走到下一个点的时间要短
                    if (arriveTime[i]==-1 || arriveTime[i] > time+branch[i]){
                        //更新 到达下个点的时间  或者是  更短的到达下个点的时间
                        arriveTime[i] = time+branch[i];
                        dfs(i,time+branch[i]);
                    }
                }
            }
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)
    

    LeetCode刷题【581】最短无序连续子数组

    8月3日每日一题,=A=,今天才知道还有每日一题这个东西。

    给你一个整数数组 nums ,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。

    请你找出符合题意的 最短 子数组,并输出它的长度。

     

    示例 1:

    输入:nums = [2,6,4,8,10,9,15]
    输出:5
    解释:你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
    

    示例 2:

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

    示例 3:

    输入:nums = [1]
    输出:0
    

     

    提示:

    • 1 <= nums.length <= 104
    • -105 <= nums[i] <= 105

     

    进阶:你可以设计一个时间复杂度为 O(n) 的解决方案吗?

    Related Topics
  • 贪心
  • 数组
  • 双指针
  • 排序
  • 单调栈
  • \n
  • 👍 612
  • 👎 0
  • 题解

    最直接能想到的解法。

    1,把原数组排序。

    2,从左到右遍历把排序数组和原数组对比,找到需要调整的左边界。

    3,从右向左遍历把排序数组和原数组对比,找到需要调整的右边界。

    4,左右边界中间部分就是需要重新排序的最短区间

    复杂度取决于使用的排序算法。

    那么,从上面的已知的思路可以看出来,需要找到的是左边起顺序的递增的区间,和右边起倒序递减的区间。

    在两边的区间里(不含中间无序部分)第i位都比左边i-n位大,都比右边第i+n位小。

    所以:

    1,从左向右遍历,记录找到的最大值,如果当前值比最大值小,说明当前值应该在已知最大值的左边,当前位置需要调整,记录位置rangeR

    2,同理,从右向左遍历,记录找到的最小值,如果当前的值比已知最小值大,说明当前值应该在已知的最小值右边,当前位置需要调整,记录位置rangeL

    3,最终需要调整的区间为rangeR到rangeL

    这里可以取个巧,用双指针,一次遍历,一个指针从头向尾部遍历,另一个指针从尾部像头部遍历。这样双指针的作法减少另一次遍历循环

    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
    
    
        /**
         * 从左向右找最大值
         * 如果当前值比最大值小,说明当前值应该在已知最大值的左边,当前位置需要调整,记录位置rangeR
         *
         * 同理,从右向左找最小值,
         * 如果当前的值比已知最小值大,说明当前值应该在已知的最小值右边,当前位置需要调整,记录位置rangeL
         *
         * 最终需要调整的区间为rangeR-rangeL
         * @param nums
         * @return
         */
        public int findUnsortedSubarray(int[] nums) {
            int min = Integer.MAX_VALUE;
            int max = Integer.MIN_VALUE;
            int rangeL = nums.length-1;
            int rangeR = 0;
            for (int l = 0; l < nums.length; l++) {
                int r = nums.length - l - 1;
                max = Math.max(nums[l],max);
                min = Math.min(nums[r],min );
                if (nums[l]<max){
                    rangeR = l;
                }
                if (nums[r]>min){
                    rangeL = r;
                }
            }
            return rangeR>rangeL?rangeR-rangeL+1:0;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)