随笔 - 81  文章 - 0  评论 - 0  阅读 - 7175 

一、化繁为简的分治法

1、为运算表达式设计优先级

问题:

给你一个由数字和运算符组成的字符串 expression ,按不同优先级组合数字和运算符,计算并返回所有可能组合的结果。你可以按任意顺序返回答案。

示例 1:

输入:expression = "2-1-1"
输出:[0,2]
解释:
((2-1)-1) = 0
(2-(1-1)) = 2
示例 2:

输入:expression = "2*3-4*5"
输出:[-34,-14,-10,-10,10]
解释:
(2*(3-(4*5))) = -34
((2*3)-(4*5)) = -14
((2*(3-4))*5) = -10
(2*((3-4)*5)) = -10
(((2*3)-4)*5) = 10

复制代码
 1 package LeetCode.test7_fenzhi_shuxue_weiyunsuan;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 public class ques_241_为运算表达式设计优先级 {
 7     public List<Integer> diffWaysToCompute(String expression) {
 8         List<Integer> ways = new ArrayList<>();
 9         for (int i = 0; i < expression.length(); i++) {
10             char c = expression.charAt(i);
11             if (c == '+' || c == '-' || c == '*') {
12                 List<Integer> left = diffWaysToCompute(expression.substring(0, i));   // left = 2
13                 List<Integer> right = diffWaysToCompute(expression.substring(i + 1));  // right = 1
14                 for (Integer l : left) {
15                     for (Integer r : right) {
16                         if (c == '+') {
17                             ways.add(l + r);
18                         } else if (c == '-') {
19                             ways.add(l - r);
20                         } else {
21                             ways.add(l * r);
22                         }
23                     }
24                 }
25             }
26         }
27         if (ways.size() == 0) {
28             ways.add(Integer.valueOf(expression));
29         }
30         return ways;
31     }
32 }
33 
34 class Test_241 {
35     public static void main(String[] args) {
36         String expression = "2-1";
37         ques_241_为运算表达式设计优先级 s = new ques_241_为运算表达式设计优先级();
38         System.out.println(s.diffWaysToCompute(expression));
39     }
40 }
View Code
复制代码

2、漂亮数组

问题:

对于某些固定的 N,如果数组 A 是整数 1, 2, ..., N 组成的排列,使得:

对于每个 i < j,都不存在 k 满足 i < k < j 使得 A[k] * 2 = A[i] + A[j]。

那么数组 A 是漂亮数组。

给定 N,返回任意漂亮数组 A(保证存在一个)。

示例 1:

输入:4
输出:[2,1,4,3]
示例 2:

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

复制代码
 1 package LeetCode.test7_fenzhi_shuxue_weiyunsuan;
 2 
 3 import java.util.Arrays;
 4 import java.util.HashMap;
 5 import java.util.Map;
 6 
 7 /**
 8  * 思路:A[k]*2=A[i]+A[j],i<k<j,可知等式左边必为偶数,只要右边和为奇数即可保证不成立,让A[i]和A[j]一个为奇数一个为偶数即可。
 9  *      对于从1到N的所有整数,奇数个数为 (N + 1) / 2,偶数个数为 N / 2
10  *      1、对于从1到(N + 1)/2的所有整数x,得出其漂亮数组,并映射成1到N范围的所有奇数 2 * x - 1
11  *      2、对于从1到N/2的所有整数x,得出其漂亮数组,并映射成1到N范围的所有偶数 2 * x
12  * 示例:N = 1    [1]
13  *      N = 2    [1,2]
14  *      N = 3    奇N=2  1*2-1  2*2-1  [1,3]
15  *               偶N=1  1*2           [2]
16  *               合并后为[1,3,2]
17  *      N = 4    奇N=2  1*2-1  2*2-1  [1,3]
18  *               偶N=2  1*2    2*2    [2,4]
19  *               合并后为[1,3,2,4]
20  *      N = 5    奇N=3  1*2-1  3*2-1  2*2-1  [1,5,3]
21  *               偶N=2  1*2    2*2    [2,4]
22  *               合并后为[1,5,3,2,4]
23  *      ......
24  */
25 public class ques_932_漂亮数组 {
26     Map<Integer, int[]> map = new HashMap<>();
27     public int[] beautifulArray(int n) {
28         map.put(1, new int[]{1});
29         return f(n);
30     }
31 
32     public int[] f(int n) {
33         if (!map.containsKey(n)) {
34             int[] res = new int[n];
35             int index = 0;
36             for (int x : f((n + 1) / 2)) {
37                 res[index++] = 2 * x - 1;
38             }
39             for (int x : f(n / 2)) {
40                 res[index++] = 2 * x;
41             }
42             map.put(n,res);
43         }
44         return map.get(n);
45     }
46 }
47 
48 class Test_932 {
49     public static void main(String[] args) {
50         int n = 4;
51         ques_932_漂亮数组 s = new ques_932_漂亮数组();
52         System.out.println(Arrays.toString(s.beautifulArray(n)));
53     }
54 }
View Code
复制代码

3、戳气球

问题:

有 n 个气球,编号为0 到 n - 1,每个气球上都标有一个数字,这些数字存在数组 nums 中。

现在要求你戳破所有的气球。戳破第 i 个气球,你可以获得 nums[i - 1] * nums[i] * nums[i + 1] 枚硬币。 这里的 i - 1 和 i + 1 代表和 i 相邻的两个气球的序号。如果 i - 1或 i + 1 超出了数组的边界,那么就当它是一个数字为 1 的气球。

求所能获得硬币的最大数量。

示例 1:
输入:nums = [3,1,5,8]
输出:167
解释:
nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> []
coins = 3*1*5 + 3*5*8 + 1*3*8 + 1*8*1 = 167
示例 2:

输入:nums = [1,5]
输出:10

复制代码
 1 package LeetCode.test7_fenzhi_shuxue_weiyunsuan;
 2 
 3 import java.util.Arrays;
 4 
 5 /**
 6  * 思路:令solve(i,j)表示将开区间(i,j)内的位置全部填满气球能够得到的最多硬币数。由于是开区间,因此区间两端的气球的编号就是i和j,
 7  *      对应着val[i]和val[j]。
 8  *      当i≥j−1时,开区间中没有气球,solve(i,j)的值为0;
 9  *      当i<j−1时,枚举开区间(i,j)内的全部位置mid,令mid为当前区间第一个添加的气球,该操作能得硬币数为val[j]val[i]×val[mid]×val[j]。
10  *                同时递归地计算分割出的两区间对solve(i,j)的贡献,这三项之和的最大值,即为solve(i,j) 的值。
11  *                这样问题就转化为求solve(i,mid)和solve(mid,j)。
12  */
13 public class ques_312_戳气球 {
14     int[] val;
15     int[][] res;
16 
17     public int maxCoins(int[] nums) {
18         int n = nums.length;
19         val = new int[n + 2];
20         val[0] = 1;
21         for (int i = 1; i <= n; i++) {
22             val[i] = nums[i - 1];
23         }
24         val[n + 1] = 1;  // val = [1, 3, 1, 5, 8, 1]
25         res = new int[n + 2][n + 2];
26         for (int i = 0; i < n + 2; i++) {
27             Arrays.fill(res[i], -1);
28         }
29         return solve(0, n + 1);
30     }
31 
32     private int solve(int left, int right) {
33         if (left >= right - 1) {   // nums = []  val = [1,1]这种情况
34             return 0;
35         }
36         if (res[left][right] != -1) {  // 没有这个会超出时间限制
37             return res[left][right];
38         }
39         for (int k = left + 1; k < right; k++) {  // [1, 0, 0, 0, 0, 1]     其他:nums = [8]  val = [1,8,1] res[0][2]
40             int sum = val[left] * val[k] * val[right];    // [1, 8, 1]  k = 4
41             sum += solve(left, k) + solve(k, right);
42             res[left][right] = Math.max(res[left][right], sum);
43         }
44         return res[left][right];
45     }
46 }
47 
48 class Test_312 {
49     public static void main(String[] args) {
50         int[] nums = {3, 1, 5, 8};
51         ques_312_戳气球 s = new ques_312_戳气球();
52         System.out.println(s.maxCoins(nums));
53     }
54 }
View Code
复制代码

二、 巧解数学问题

Demo、公倍数与公因数

复制代码
 1 package LeetCode.test7_fenzhi_shuxue_weiyunsuan;
 2 
 3 public class demo {
 4     /**
 5      * 辗转相除法求最大公因数
 6      *
 7      * @param a
 8      * @param b
 9      * @return
10      */
11     int gcd(int a, int b) {
12         return b == 0 ? a : gcd(b, a % b);
13     }
14 
15     /**
16      * 求最小公倍数
17      *
18      * @param a
19      * @param b
20      * @return
21      */
22     int lcm(int a, int b) {
23         return a * b / gcd(a, b);
24     }
25 }
26 
27 class Test_demo {
28     public static void main(String[] args) {
29         demo s = new demo();
30         int a = 9;
31         int b = 12;
32         System.out.println(s.gcd(a, b));
33         System.out.println(s.lcm(a, b));
34     }
35 }
View Code
复制代码

4、计数质数

问题:

给定整数 n ,返回 所有小于非负整数 n 的质数的数量 。

示例 1:

输入:n = 10
输出:4
解释:小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。
示例 2:

输入:n = 0
输出:0
示例 3:

输入:n = 1
输出:0

复制代码
 1 package LeetCode.test7_fenzhi_shuxue_weiyunsuan;
 2 
 3 
 4 import java.util.Arrays;
 5 
 6 /**
 7  * 思路:从1到n遍历,假设当前遍历到 m,则把所有小于n的、且是m的倍数的整数标为和数;遍历完成后,没有被标为和数的数字即为质数。
 8  */
 9 public class ques_204_计数质数 {
10     public int countPrimes(int n) {
11         if (n <= 2) {
12             return 0;
13         }
14         Integer[] prime = new Integer[n];  // eg: [_,_,2,3,4,5,6,7,8,9]  10个
15         Arrays.fill(prime, 1);
16         prime[0] = 0;
17         prime[1] = 0;
18         for (int i = 2; i < Math.sqrt(n); i++) {  // eg: 数字2-9  Math.sqrt(n)优化
19             if (prime[i] == 1) {
20                 for (int j = i * 2; j < n; j += i) {  // 2_4_6_8  3_6_9  4_8  5
21                     if (prime[j] == 1) {
22                         prime[j] = 0;
23                     }
24                 }
25             }
26         }
27         // [0, 0, 1(2), 1(3), 0(4), 1(5), 0(6), 1(7), 0(8), 0(9)]
28 //        return Arrays.asList(prime).strem().reduce(0, Integer::sum);
29         int sum = 0;
30         for (Integer p : prime) {
31             sum += p;
32         }
33         return sum;
34     }
35 }
36 
37 class Test_204 {
38     public static void main(String[] args) {
39         int n = 10;
40         ques_204_计数质数 s = new ques_204_计数质数();
41         System.out.println(s.countPrimes(n));
42     }
43 }
View Code
复制代码

5、七进制数

问题:

给定一个整数 num,将其转化为 7 进制,并以字符串形式输出。

示例 1:

输入: num = 100
输出: "202"
示例 2:

输入: num = -7
输出: "-10"

复制代码
 1 package LeetCode.test7_fenzhi_shuxue_weiyunsuan;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 /**
 7  * 思路:2753
 8  * 2753 = 393*7 +2
 9  * 393 = 56*7 +1
10  * 56 = 8*7 + 0
11  * 8 = 1*7 +1
12  * 1 = 0*7 +1
13  * (2753)|10 = ( 11012) |7
14  */
15 public class ques_504_七进制数 {
16     public String convertToBase7(int num) {
17         if (num == 0) {
18             return "0";
19         }
20         boolean flag = false;
21         if (num < 0) {
22             num = -num;
23             flag = true;
24         }
25         List<Integer> res = new ArrayList<>();  // [2, 1, 0, 1, 1]
26         while (num != 0) {
27             res.add(num % 7);
28             num = num / 7;
29         }
30         StringBuilder ans = new StringBuilder();
31         for (int i = res.size() - 1; i >= 0; i--) {
32             ans.append(res.get(i));
33         }
34         return flag ? "-" + ans.toString() : ans.toString();
35     }
36 }
37 
38 class Test_504 {
39     public static void main(String[] args) {
40         int num = -8;
41         ques_504_七进制数 s = new ques_504_七进制数();
42         System.out.println(s.convertToBase7(num));
43     }
44 }
View Code
复制代码

6、阶乘后的零

问题:

给定一个整数 n ,返回 n! 结果中尾随零的数量。

提示 n! = n * (n - 1) * (n - 2) * ... * 3 * 2 * 1

示例 1:

输入:n = 3
输出:0
解释:3! = 6 ,不含尾随 0
示例 2:

输入:n = 5
输出:1
解释:5! = 120 ,有一个尾随 0
示例 3:

输入:n = 0
输出:0

复制代码
 1 package LeetCode.test7_fenzhi_shuxue_weiyunsuan;
 2 
 3 /**
 4  * 思路:每个尾部的0由2×5=10而来,因此可以把阶乘的每一个元素拆成质数相乘,统计有多少个2和5。
 5  * 明显的,质因子2的数量远多于质因子5的数量,因此可以只统计阶乘结果里有多少个质因子5。
 6  * (在一个阶乘中,把所有1和n之间的数相乘,这和把所有1和n之间所有数字的因子相乘是一样的。)
 7  * 如果n=16,需要查看1到16之间所有数字的因子。我们只对2和5有兴趣。包含5因子的数字是5,10,15,
 8  * 包含因子2的数字是2、4、6、8、10、12、14、16。因为只有三个完整的对,因此16!后有三个零。
 9  */
10 public class ques_172_阶乘后的零 {
11     public int trailingZeroes(int n) {
12 //        return n/5; // 错误的做法 eg:30
13         return n == 0 ? 0 : n / 5 + trailingZeroes(n / 5);
14     }
15 }
16 
17 class Test_172 {
18     public static void main(String[] args) {
19         int n = 30;
20         ques_172_阶乘后的零 s = new ques_172_阶乘后的零();
21         System.out.println(s.trailingZeroes(n));
22     }
23 }
View Code
复制代码

7、字符串相加

问题:

给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。

你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。

示例 1:

输入:num1 = "11", num2 = "123"
输出:"134"
示例 2:

输入:num1 = "456", num2 = "77"
输出:"533"
示例 3:

输入:num1 = "0", num2 = "0"
输出:"0"

复制代码
 1 package LeetCode.test7_fenzhi_shuxue_weiyunsuan;
 2 
 3 public class ques_415_字符串相加 {
 4     public String addStrings(String num1, String num2) {
 5         int len = num1.length() - num2.length();
 6         StringBuilder res = new StringBuilder();
 7         for (int i = 0; i < Math.abs(len); i++) {
 8             res.append('0');
 9         }
10         if (len > 0) {
11             num2 = res.toString() + num2;
12         } else {
13             num1 = res.toString() + num1;
14         }
15         char[] chars1 = num1.toCharArray();
16         char[] chars2 = num2.toCharArray();
17         int addbit = 0;
18         for (int i = chars1.length - 1; i >= 0; i--) {
19             int cur_sum = (chars1[i] - '0') + (chars2[i] - '0') + addbit;
20             addbit = cur_sum < 10 ? 0 : 1;
21             chars1[i] = (char) (cur_sum % 10 + '0');
22         }
23         StringBuilder output = new StringBuilder();
24         if (addbit == 1) {
25             output.append("1");
26         }
27         for (int i = 0; i < chars1.length; i++) {
28             output.append(chars1[i]);
29         }
30         return output.toString();
31     }
32 }
33 
34 class Test_415 {
35     public static void main(String[] args) {
36         String num1 = "19";
37         String num2 = "123";
38         ques_415_字符串相加 s = new ques_415_字符串相加();
39         System.out.println(s.addStrings(num1, num2));
40     }
41 }
View Code
复制代码

8、3的幂

问题:

给定一个整数,写一个函数来判断它是否是 3 的幂次方。如果是,返回 true ;否则,返回 false 。

整数 n 是 3 的幂次方需满足:存在整数 x 使得 n == 3x

示例 1:

输入:n = 27
输出:true
示例 2:

输入:n = 0
输出:false
示例 3:

输入:n = 9
输出:true
示例 4:

输入:n = 45
输出:false

复制代码
 1 package LeetCode.test7_fenzhi_shuxue_weiyunsuan;
 2 
 3 public class ques_326_3的幂 { 
 4     public boolean isPowerOfThree(int n) {
 5         // 不断地将n除以3,直到n=1。如果此过程中n无法被3整除,就说明n不是3的幂。
 6         if (n == 0) {
 7             return false;
 8         }
 9         while (n % 3 == 0) {
10             n = n / 3;
11         }
12         return n == 1;
13     }
14 }
15 
16 class Test_326 {
17     public static void main(String[] args) {
18         int n = 26;
19         ques_326_3的幂 s = new ques_326_3的幂();
20         System.out.println(s.isPowerOfThree(n));
21     }
22 }
View Code
复制代码

9、打乱数组

问题:

给你一个整数数组 nums ,设计算法来打乱一个没有重复元素的数组。打乱后,数组的所有排列应该是 等可能 的。

实现 Solution class:

Solution(int[] nums) 使用整数数组 nums 初始化对象
int[] reset() 重设数组到它的初始状态并返回
int[] shuffle() 返回数组随机打乱后的结果

示例 1:

输入
["Solution", "shuffle", "reset", "shuffle"]
[[[1, 2, 3]], [], [], []]
输出
[null, [3, 1, 2], [1, 2, 3], [1, 3, 2]]

解释
Solution solution = new Solution([1, 2, 3]);
solution.shuffle(); // 打乱数组 [1,2,3] 并返回结果。任何 [1,2,3]的排列返回的概率应该相同。例如,返回 [3, 1, 2]
solution.reset(); // 重设数组到它的初始状态 [1, 2, 3] 。返回 [1, 2, 3]
solution.shuffle(); // 随机返回数组 [1, 2, 3] 打乱后的结果。例如,返回 [1, 3, 2]

复制代码
 1 package LeetCode.test7_fenzhi_shuxue_weiyunsuan;
 2 
 3 import java.util.Arrays;
 4 import java.util.Random;
 5 
 6 /**
 7  * 思路:设待原地乱序的数组nums。
 8  *      循环n次,在第i次循环中(0≤i<n):
 9  *      1.在[i,n)中随机抽取一个下标j;2.将第i个元素与第j个元素交换。
10  *      其中数组中的nums[i..n−1]的部分为待乱序的数组,其长度为n−i;nums[0..i−1]的部分为乱序后的数组,其长度为i。
11  */
12 public class ques_384_打乱数组 {
13     int[] nums;
14     int[] original;
15 
16     public ques_384_打乱数组(int[] nums) {
17         this.nums = nums;
18         this.original = new int[nums.length];
19         System.arraycopy(nums, 0, original, 0, nums.length);
20     }
21 
22     public int[] reset() {
23         System.arraycopy(original, 0, nums, 0, nums.length);
24         return nums;
25     }
26 
27     public int[] shuffle() {
28         Random random = new Random();
29         for (int i = 0; i < nums.length; i++) {
30             int j = i + random.nextInt(nums.length - i);
31             int temp = nums[i];
32             nums[i] = nums[j];
33             nums[j] = temp;
34         }
35         return nums;
36     }
37 }
38 
39 class Test_384 {
40     public static void main(String[] args) {
41         String[] actions = {"Solution", "shuffle", "reset", "shuffle"};
42         int[] nums = {1, 2, 3};
43         ques_384_打乱数组 s = new ques_384_打乱数组(nums);
44         System.out.println(Arrays.toString(s.shuffle()));
45         System.out.println(Arrays.toString(s.reset()));
46         System.out.println(Arrays.toString(s.shuffle()));
47     }
48 }
View Code
复制代码

10、按权重随机选择

问题:

给你一个 下标从 0 开始 的正整数数组 w ,其中 w[i] 代表第 i 个下标的权重。

请你实现一个函数 pickIndex ,它可以 随机地 从范围 [0, w.length - 1] 内(含 0 和 w.length - 1)选出并返回一个下标。选取下标 i 的 概率 为 w[i] / sum(w) 。

例如,对于 w = [1, 3],挑选下标 0 的概率为 1 / (1 + 3) = 0.25 (即,25%),而选取下标 1 的概率为 3 / (1 + 3) = 0.75(即,75%)。

示例 1:

输入:
["Solution","pickIndex"]
[[[1]],[]]
输出:
[null,0]
解释:
Solution solution = new Solution([1]);
solution.pickIndex(); // 返回 0,因为数组中只有一个元素,所以唯一的选择是返回下标 0。
示例 2:

输入:
["Solution","pickIndex","pickIndex","pickIndex","pickIndex","pickIndex"]
[[[1,3]],[],[],[],[],[]]
输出:
[null,1,1,1,1,0]
解释:
Solution solution = new Solution([1, 3]);
solution.pickIndex(); // 返回 1,返回下标 1,返回该下标概率为 3/4 。
solution.pickIndex(); // 返回 1
solution.pickIndex(); // 返回 1
solution.pickIndex(); // 返回 1
solution.pickIndex(); // 返回 0,返回下标 0,返回该下标概率为 1/4 。

由于这是一个随机问题,允许多个答案,因此下列输出都可以被认为是正确的:
[null,1,1,1,1,0]
[null,1,1,1,1,1]
[null,1,1,1,0,0]
[null,1,1,1,0,1]
[null,1,0,1,0,0]
......
诸若此类。

复制代码
 1 package LeetCode.test7_fenzhi_shuxue_weiyunsuan;
 2 
 3 import java.util.Arrays;
 4 
 5 /**
 6  * 思路:w=[3,1,2,4]
 7  * 权重之和total=10,按照[1,3],[4,4],[5,6],[7,10]对[1,10]进行划分,使得它们的长度恰好依次为3,1,2,4。
 8  * 用pre[i]表示数组w的前缀和,第i个区间的左边界就是pre[i]−w[i]+1,右边界就是pre[i]。
 9  * pre[0] = 3
10  * pre[1] = pre[0] + 1 = 4
11  * pre[2] = pre[1] + 2 = 6
12  * pre[3] = pre[2] + 4 = 10
13  */
14 public class ques_528_按权重随机选择 {
15     int[] pre;
16     int total;
17 
18     public ques_528_按权重随机选择(int[] w) {
19         pre = new int[w.length];
20         pre[0] = w[0];
21         for (int i = 1; i < w.length; i++) {
22             pre[i] = pre[i - 1] + w[i];
23         }
24         total = Arrays.stream(w).sum();
25     }
26 
27     public int pickIndex() {
28         int x = (int) (Math.random() * total) + 1;
29         System.out.println(x);
30         return binarySearch(x);
31     }
32 
33     private int binarySearch(int x) {
34         int left = 0;
35         int right = pre.length - 1;
36         while (left < right) {
37             int mid = (right - left) / 2 + left;
38             if (pre[mid] < x) {
39                 left = mid + 1;
40             } else {
41                 right = mid;
42             }
43         }
44         return left;
45     }
46 }
47 
48 class Test_528 {
49     public static void main(String[] args) {
50         int[] weights = {3, 1, 2, 4};
51         ques_528_按权重随机选择 s = new ques_528_按权重随机选择(weights);
52         System.out.println(s.pickIndex());
53     }
54 }
View Code
复制代码

11、链表随机节点

问题:

给你一个单链表,随机选择链表的一个节点,并返回相应的节点值。每个节点 被选中的概率一样 。

实现 Solution 类:

Solution(ListNode head) 使用整数数组初始化对象。
int getRandom() 从链表中随机选择一个节点并返回该节点的值。链表中所有节点被选中的概率相等。

示例:

输入
["Solution", "getRandom", "getRandom", "getRandom", "getRandom", "getRandom"]
[[[1, 2, 3]], [], [], [], [], []]
输出
[null, 1, 3, 2, 2, 3]

解释
Solution solution = new Solution([1, 2, 3]);
solution.getRandom(); // 返回 1
solution.getRandom(); // 返回 3
solution.getRandom(); // 返回 2
solution.getRandom(); // 返回 2
solution.getRandom(); // 返回 3
// getRandom() 方法应随机返回 1、2、3中的一个,每个元素被返回的概率相等。

复制代码
 1 package LeetCode.test7_fenzhi_shuxue_weiyunsuan;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 import java.util.Random;
 6 
 7 class ListNode {
 8     int val;
 9     ListNode next;
10 
11     ListNode() {
12     }
13 
14     ListNode(int val) {
15         this.val = val;
16     }
17 
18     ListNode(int val, ListNode next) {
19         this.val = val;
20         this.next = next;
21     }
22 }
23 
24 public class ques_382_链表随机节点 {
25     List<Integer> list;
26     Random random;
27 
28     public ques_382_链表随机节点(ListNode head) {
29         random = new Random();
30         list = new ArrayList<>();
31         PrintOrder(head);
32     }
33 
34     public int getRandom() {
35         return list.get(random.nextInt(list.size()));
36     }
37 
38     public void PrintOrder(ListNode root) {
39         if (root == null)
40             return;
41         list.add(root.val);
42         PrintOrder(root.next);
43     }
44 }
45 
46 class Test_382 {
47     public static void main(String[] args) {
48         String[] root = {"1", "2", "3"};
49         int index = 0;
50         ListNode bt = createBTree(root, index);
51 //        PrintOrder(bt);
52         ques_382_链表随机节点 s = new ques_382_链表随机节点(bt);
53         System.out.println(s.getRandom());
54     }
55 
56     public static ListNode createBTree(String[] root, int index) {
57         ListNode rootNode;   // 定义根节点
58         if (index >= root.length || root[index] == null) {
59             return null;
60         }
61         rootNode = new ListNode(Integer.parseInt(root[index]));
62         rootNode.next = createBTree(root, index + 1);
63         return rootNode;
64     }
65 
66     public static void PrintOrder(ListNode root) {
67         if (root == null)
68             return;
69         System.out.print(root.val + " ");
70         PrintOrder(root.next);
71     }
72 }
View Code
复制代码

练习

基础难度
168. Excel Sheet Column Title (Easy)
7 进制转换的变种题,需要注意的一点是从 1 开始而不是 0。
67. Add Binary (Easy)
字符串加法的变种题。
238. Product of Array Except Self (Medium)
你可以不使用除法做这道题吗?我们很早之前讲过的题目 135 或许能给你一些思路。
进阶难度
462. Minimum Moves to Equal Array Elements II (Medium)
这道题是笔者最喜欢的 LeetCode 题目之一,需要先推理出怎么样移动是最优的,再考虑如
何进行移动。你或许需要一些前些章节讲过的算法。
169. Majority Element (Easy)
如果想不出简单的解决方法,搜索一下 Boyer-Moore Majority Vote 算法吧。

470. Implement Rand10() Using Rand7() (Medium)
如何用一个随机数生成器生成另一个随机数生成器?你可能需要利用原来的生成器多次。
202. Happy Number (Easy)
你可以简单的用一个 while 循环解决这道题,但是有没有更好的解决办法?如果我们把每个
数字想象成一个节点,是否可以转化为环路检测?

三、神奇的位运算

12、汉明距离

问题:

两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。

给你两个整数 x 和 y,计算并返回它们之间的汉明距离。

示例 1:

输入:x = 1, y = 4
输出:2
解释:
1 (0 0 0 1)
4 (0 1 0 0)
↑ ↑
上面的箭头指出了对应二进制位不同的位置。
示例 2:

输入:x = 3, y = 1
输出:1

复制代码
 1 package LeetCode.test7_fenzhi_shuxue_weiyunsuan;
 2 
 3 /**
 4  * 思路:异或操作,相同为0,不同为1.
 5  * 不断地检查s的最低位,如果最低位为1,那么令计数器加一,然后令s整体右移一位,这样s的最低位将被舍去,
 6  * 原本的次低位就变成了新的最低位。重复这个过程直到s=0为止。
 7  * System.out.println(diff); //0101  5
 8  * System.out.println("###");
 9  * System.out.println(diff&1);  // 0101&0001 = 1
10  * System.out.println(diff >>= 1);  //0010  2
11  * System.out.println("###");
12  * System.out.println(diff&1);   // 0010&0001 = 0
13  * System.out.println(diff >>= 1);  // 0001  1
14  * System.out.println("###");
15  * System.out.println(diff&1);   // 0001&0001 = 1
16  * System.out.println(diff >>= 1);  // 0000  0
17  */
18 public class ques_461_汉明距离 {
19     public int hammingDistance(int x, int y) { //对两个数进行按位异或操作,统计有多少个1即可。
20         int diff = x ^ y;  // 0001^0100-> 0101 = 1*2^2+1*2^0=5
21         int ans = 0;
22         while (diff != 0) {
23             ans += diff & 1;
24             // 1的二进制除了最后一位为1前面全为0,这样不管前面数字如何都为0,只比较最低位与1,最低位为0则为0,最低位为1则为1。
25             diff >>= 1;
26             // eg: a=a>>2 将a的二进制位右移2位,左补0或者左补1得看被移数是正还是负(正数左补0,负数左补1)。
27         }
28         return ans;
29     }
30 }
31 
32 class Test_461 {
33     public static void main(String[] args) {
34         int x = 1;
35         int y = 4;
36         ques_461_汉明距离 s = new ques_461_汉明距离();
37         System.out.println(s.hammingDistance(x, y));
38     }
39 }
View Code
复制代码

13、颠倒二进制位

问题:

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

提示:

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

示例 1:

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

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

复制代码
 1 package LeetCode.test7_fenzhi_shuxue_weiyunsuan;
 2 
 3 public class ques_190_颠倒二进制位 { //使用算术左移和右移,实现二进制的翻转。
 4     public int reverseBits(int n) {  // eg: 0101
 5         int ans = 0;  // ans左移腾位置,n右移腾最低位。
 6         for (int i = 0; i < 32; i++) {
 7             ans <<= 1;
 8             ans += n & 1;  // 0101 & 0001 = 0001 -> 001_0(腾位置)
 9             n >>= 1;   // 0101 -> 0_010
10         }
11         return ans;
12     }
13 }
14 
15 class Test_190 {
16     public static void main(String[] args) {
17         int n = 43261596;
18         ques_190_颠倒二进制位 s = new ques_190_颠倒二进制位();
19         System.out.println(s.reverseBits(n));
20     }
21 }
View Code
复制代码

14、只出现一次的数字

问题:

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,1]
输出: 1
示例 2:

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

复制代码
 1 package LeetCode.test7_fenzhi_shuxue_weiyunsuan;
 2 
 3 /**
 4  * 思路:利用x∧x=0和x∧0=x的特点,将数组内所有的数字进行按位异或。
 5  * 出现两次的所有数字按位异或的结果是0,0与出现1次的数字异或可以得到这个数字本身。
 6  * 0000∧0001∧0001=0001∧0001=0(2次)
 7  * 0000∧0001=0001
 8  */
 9 public class ques_136_只出现一次的数字 {
10     public int singleNumber(int[] nums) {
11         int ans = 0;
12         for (int num : nums) {
13             ans ^= num;
14         }
15         return ans;
16     }
17 }
18 
19 class Test_136 {
20     public static void main(String[] args) {
21         int[] nums = {4, 1, 2, 1, 2};
22         ques_136_只出现一次的数字 s = new ques_136_只出现一次的数字();
23         System.out.println(s.singleNumber(nums));
24     }
25 }
View Code
复制代码

15、4的幂

问题:

给定一个整数,写一个函数来判断它是否是 4 的幂次方。如果是,返回 true ;否则,返回 false 。

整数 n 是 4 的幂次方需满足:存在整数 x 使得 n == 4x

示例 1:

输入:n = 16
输出:true
示例 2:

输入:n = 5
输出:false
示例 3:

输入:n = 1
输出:true

复制代码
 1 package LeetCode.test7_fenzhi_shuxue_weiyunsuan;
 2 
 3 /**
 4  * 如果一个数字n是2的整数次方,那么它的二进制一定是0...010...0这样的形式;考虑到n−1的二进制是0...001...1,
 5  * 这两个数求按位与的结果一定是0。因此如果n&(n - 1)为0,那么这个数是2的次方。
 6  * 如果这个数也是4的次方,那二进制表示中1的位置必须为奇数位。我们可以把n和二进制的10101...101
 7  * (即十进制下的 1431655765)做按位与,如果结果不为0,那么说明这个数是4的次方。
 8  * 000100&000101=000100(不为0)   000010&000101=000000(为0)
 9  */
10 public class ques_342_4的幂 {
11     public boolean isPowerOfFour(int n) {
12         return n > 0 && (n & (n - 1)) == 0 && (n & 1431655765) != 0;
13     }
14 }
15 
16 class Test_342 {
17     public static void main(String[] args) {
18         int n = 16;
19         ques_342_4的幂 s = new ques_342_4的幂();
20         System.out.println(s.isPowerOfFour(n));
21     }
22 }
View Code
复制代码

16、最大单词长度乘积

问题:

给你一个字符串数组 words ,找出并返回 length(words[i]) * length(words[j]) 的最大值,并且这两个单词不含有公共字母。如果不存在这样的两个单词,返回 0 。

示例 1:

输入:words = ["abcw","baz","foo","bar","xtfn","abcdef"]
输出:16
解释:这两个单词为 "abcw", "xtfn"。
示例 2:

输入:words = ["a","ab","abc","d","cd","bcd","abcd"]
输出:4
解释:这两个单词为 "ab", "cd"。
示例 3:

输入:words = ["a","aa","aaa","aaaa"]
输出:0
解释:不存在这样的两个单词。

复制代码
 1 package LeetCode.test7_fenzhi_shuxue_weiyunsuan;
 2 
 3 /**
 4  * 思路:当且仅当masks[i]&masks[j]=0时第i个单词和第j个单词没有公共字母,此时使用这两个单词的长度乘积更新最大单词长度乘积。
 5  */
 6 public class ques_318_最大单词长度乘积 {
 7     public int maxProduct(String[] words) {
 8         int length = words.length;
 9         int[] masks = new int[length];
10         for (int i = 0; i < length; i++) {
11             String word = words[i];
12             for (int j = 0; j < word.length(); j++) {
13                 masks[i] = masks[i] | (1 << (word.charAt(j) - 'a'));
14                 // eg: "ab"(a左移0位,b左移1位) masks[0]=000|001=001 masks[0]=001|010=011 011就是"ab"的表示
15             }
16         }
17         int maxLength = 0;
18         for (int i = 0; i < masks.length; i++) {
19             for (int j = i + 1; j < masks.length; j++) {
20                 if ((masks[i] & masks[j]) == 0) {
21                     maxLength = Math.max(maxLength, words[i].length() * words[j].length());
22                 }
23             }
24         }
25         return maxLength;
26     }
27 }
28 
29 class Test_318 {
30     public static void main(String[] args) {
31         String[] words = {"abcw", "baz", "foo", "bar", "xtfn", "abcdef"};
32         ques_318_最大单词长度乘积 s = new ques_318_最大单词长度乘积();
33         System.out.println(s.maxProduct(words));
34     }
35 }
View Code
复制代码

17、比特位计数

问题:

给你一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1 的数组 ans 作为答案。

示例 1:

输入:n = 2
输出:[0,1,1]
解释:
0 --> 0
1 --> 1
2 --> 10
示例 2:

输入:n = 5
输出:[0,1,1,2,1,2]
解释:
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101

复制代码
 1 package LeetCode.test7_fenzhi_shuxue_weiyunsuan;
 2 
 3 import java.util.Arrays;
 4 
 5 /**
 6  * 思路: dp[i]表示数字i的二进制含有1的个数。
 7  * 对于第i个数字,如果它二进制的最后一位为1,那么它含有1的个数则为dp[i-1]+1;
 8  * 如果它二进制的最后一位为0,那么它含有1的个数和其算术右移结果相同,即dp[i>>1]。
 9  * 01000&00001=00000   01001&00001=00001
10  * (111) = (110) + 1;  (110) = (011)
11  */
12 public class ques_338_比特位计数 {
13     public int[] countBits(int n) {
14         int[] dp = new int[n + 1];
15         for (int i = 1; i <= n; i++) {
16             dp[i] = (i & 1) == 1 ? dp[i - 1] + 1 : dp[i >> 1];
17             // dp[1] = dp[0] + 1 = 1
18         }
19         return dp;
20     }
21 }
22 
23 class Test_338 {
24     public static void main(String[] args) {
25         int n = 5;
26         ques_338_比特位计数 s = new ques_338_比特位计数();
27         System.out.println(Arrays.toString(s.countBits(n)));
28     }
29 }
View Code
复制代码

练习

基础难度
268. Missing Number (Easy)
Single Number 的变种题。除了利用二进制,也可以使用高斯求和公式。
693. Binary Number with Alternating Bits (Easy)
利用位运算判断一个数的二进制是否会出现连续的 0 和 1。
476. Number Complement (Easy)
二进制翻转的变种题。
进阶难度
260. Single Number III (Medium)
Single Number 的 follow-up,需要认真思考如何运用位运算求解。

posted on   晨曦生辉耀匕尖  阅读(146)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· 地球OL攻略 —— 某应届生求职总结
点击右上角即可分享
微信分享提示