文章目录
前言
需要开通vip的题目暂时跳过
笔记导航
点击链接可跳转到所有刷题笔记的导航链接
461. 汉明距离
两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。
给出两个整数 x 和 y,计算它们之间的汉明距离。
-
解答
public int hammingDistance(int x, int y) { return Integer.bitCount(x^y); }
-
分析
- 异或 2个数字不一样为1,一样为0.
- Integer.bitCount可以计算2进制中1出现的个数
-
提交结果
462. 最少移动次数使数组元素相等 II
给定一个非空整数数组,找到使所有数组元素相等所需的最小移动数,其中每次移动可将选定的一个元素加1或减1。 您可以假设数组的长度最多为10000。
-
解答
public int minMoves2(int[] nums) { Arrays.sort(nums); int res = 0; for(int i = 0;i < nums.length;i++){ res += Math.abs(nums[nums.length/2] - nums[i]); } return res; }
-
分析
- 所有值去逼近中位数
- 首先对数组排序
- 然后遍历数组,每一位要移动的次数,就是当前数字减去中位数 取绝对值
-
提交结果
463. 岛屿的周长
给定一个包含 0 和 1 的二维网格地图,其中 1 表示陆地 0 表示水域。
网格中的格子水平和垂直方向相连(对角线方向不相连)。整个网格被水完全包围,但其中恰好有一个岛屿(或者说,一个或多个表示陆地的格子相连组成的岛屿)。
岛屿中没有“湖”(“湖” 指水域在岛屿内部且不和岛屿周围的水相连)。格子是边长为 1 的正方形。网格为长方形,且宽度和高度均不超过 100 。计算这个岛屿的周长。
-
解答
int res = 0; public int islandPerimeter(int[][] grid) { int row = grid.length; if(row == 0)return res; int col = grid[0].length; if(col == 0)return res; boolean flag = false; for (int i = 0; i < row; i++) { if (flag) break; for (int j = 0; j < col; j++) { if (grid[i][j] == 1) { int[][] visited = new int[row][col]; dfs(grid, i, j, visited); flag = true; break; } } } return res; } public void dfs(int[][] grid, int i, int j, int[][] visited) { visited[i][j] = 1; res += calEdgeLen(grid, i, j); if (i > 0 && grid[i - 1][j] == 1 && visited[i - 1][j] == 0) dfs(grid, i - 1, j, visited); if (i < grid.length - 1 && grid[i + 1][j] == 1 && visited[i + 1][j] == 0) dfs(grid, i + 1, j, visited); if (j > 0 && grid[i][j - 1] == 1 && visited[i][j - 1] == 0) dfs(grid, i, j - 1, visited); if (j < grid[0].length - 1 && grid[i][j + 1] == 1 && visited[i][j + 1] == 0) dfs(grid, i, j + 1, visited); } public int calEdgeLen(int[][] grid, int i, int j) { int res = 0; if (i == 0) res++; if(i == grid.length - 1) res++; if (j == 0) res++; if(j == grid[0].length - 1) res++; if (i > 0 && grid[i - 1][j] == 0) res++;//上 if (i < grid.length - 1 && grid[i + 1][j] == 0) res++;//下 if (j > 0 && grid[i][j - 1] == 0) res++;//左 if (j < grid[0].length - 1 && grid[i][j + 1] == 0) res++;//右 return res; }
-
分析
- dfs 深度搜索图
- 计算每一个方块的边 累计即可
-
提交结果
464. 我能赢吗
在 “100 game” 这个游戏中,两名玩家轮流选择从 1 到 10 的任意整数,累计整数和,先使得累计整数和达到或超过 100 的玩家,即为胜者。
如果我们将游戏规则改为 “玩家不能重复使用整数” 呢?
例如,两个玩家可以轮流从公共整数池中抽取从 1 到 15 的整数(不放回),直到累计整数和 >= 100。
给定一个整数 maxChoosableInteger (整数池中可选择的最大数)和另一个整数 desiredTotal(累计和),判断先出手的玩家是否能稳赢(假设两位玩家游戏时都表现最佳)?
你可以假设 maxChoosableInteger 不会大于 20, desiredTotal 不会大于 300。
-
解答
public boolean canIWin(int maxChoosableInteger, int desiredTotal) { if (maxChoosableInteger >= desiredTotal) return true; if ((1 + maxChoosableInteger) * maxChoosableInteger / 2 < desiredTotal) return false; //1,..maxChoosable数列总和都比目标和小 int[] state = new int[maxChoosableInteger + 1]; //state[1]=1表示1被选了 return backtraceWitMemo(state, desiredTotal, new HashMap<String, Boolean>()); } private boolean backtraceWitMemo(int[] state, int desiredTotal, HashMap<String, Boolean> map) { String key = Arrays.toString(state); //这里比较关键,如何表示这个唯一的状态,例如[2,3]和[3,2]都是"0011",状态一样 if (map.containsKey(key)) return map.get(key); //如果已经记忆了这样下去的输赢结果,记忆是为了防止如[2,3],[3,2]之后的[1,4,5,..]这个选择区间被重复计算 for (int i = 1; i < state.length; i++){ if (state[i] == 0){ //如果这个数字i还没有被选中 state[i] = 1; //如果当前选了i已经赢了或者选了i还没赢但是后面对方选择输了 if (desiredTotal - i <= 0 || !backtraceWitMemo(state, desiredTotal - i, map)) { map.put(key, true); state[i] = 0; //在返回之前回溯 return true; } //如果不能赢也要记得回溯 state[i] = 0; } } //如果都赢不了 map.put(key, false); return false; }
-
分析
- 回溯实现
- state记录各个数字使用的情况,每次递归根据state得到一个key,作为记录集的标记。
- map记录 state状态下能否获胜
- 遍历数组,若当前数字没有选择,则选择这一位。若选了这一位获得了胜利,或者选择了这一位还没胜利 但是后面对方输了,则获胜。放入记忆集当中,回溯 返回true。
- 若赢不了 回溯,去寻找下一个数字。
- 遍历完了数组也没有获胜,那么记录下当前状态下不可能获胜。返回false。
-
提交结果
467. 环绕字符串中唯一的子字符串
把字符串 s 看作是“abcdefghijklmnopqrstuvwxyz”的无限环绕字符串,所以 s 看起来是这样的:"…zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd…".
现在我们有了另一个字符串 p 。你需要的是找出 s 中有多少个唯一的 p 的非空子串,尤其是当你的输入是字符串 p ,你需要输出字符串 s 中 p 的不同的非空子串的数目。
注意: p 仅由小写的英文字母组成,p 的大小可能超过 10000。
-
解答
//方法1 public int findSubstringInWraproundString(String p) { if(p.equals("") || p == null)return 0; int w = 1; int ans = 0; Map<Character,Integer> map = new HashMap<>(); map.put(p.charAt(0),1); for(int i = 1;i < p.length();i++){ char cur = p.charAt(i); if(cur - p.charAt(i-1) == 1 || cur - p.charAt(i-1) == -25) w++; else w = 1; map.put(cur,Math.max(map.getOrDefault(cur,0) ,w)); } for(Character c : map.keySet()){ ans += map.get(c); } return ans; } //方法2 public int findSubstringInWraproundString(String p){ int[] dp = new int[26]; int cnt = 1, n = p.length(); char[] cs = (" " + p).toCharArray(); for(int i = 1; i <= n; i++){ int idx = cs[i] - 'a'; if(check(cs[i - 1], cs[i])) cnt++; else cnt = 1; dp[idx] = Math.max(dp[idx], cnt); } int ans = 0; for(int i = 0; i < 26; i++) ans += dp[i]; return ans; } public boolean check(char a, char b){ if(a == 'z' && b == 'a') return true; return b - a == 1; }
-
分析
- 前缀和的思路,若满足循环字符串,则w+1.
- 若不满足 w = 1
- map记录下 以key字符结尾的构成的字符串的长度。
- 结果就是将map中所有的value相加即可。
- 方法2 和方法一思路类似,只是使用了数组来代替了HashMap
-
提交结果
方法1
方法2
468. 验证IP地址
编写一个函数来验证输入的字符串是否是有效的 IPv4 或 IPv6 地址。
如果是有效的 IPv4 地址,返回 “IPv4” ;
如果是有效的 IPv6 地址,返回 “IPv6” ;
如果不是上述类型的 IP 地址,返回 “Neither” 。
IPv4 地址由十进制数和点来表示,每个地址包含 4 个十进制数,其范围为 0 - 255, 用(".")分割。比如,172.16.254.1;
同时,IPv4 地址内的数不会以 0 开头。比如,地址 172.16.254.01 是不合法的。
IPv6 地址由 8 组 16 进制的数字来表示,每组表示 16 比特。这些组数字通过 (":")分割。比如, 2001:0db8:85a3:0000:0000:8a2e:0370:7334 是一个有效的地址。而且,我们可以加入一些以 0 开头的数字,字母可以使用大写,也可以是小写。所以, 2001:db8:85a3:0:0:8A2E:0370:7334 也是一个有效的 IPv6 address地址 (即,忽略 0 开头,忽略大小写)。
然而,我们不能因为某个组的值为 0,而使用一个空的组,以至于出现 (:😃 的情况。 比如, 2001:0db8:85a3::8A2E:0370:7334 是无效的 IPv6 地址。
同时,在 IPv6 地址中,多余的 0 也是不被允许的。比如, 02001:0db8:85a3:0000:0000:8a2e:0370:7334 是无效的。
-
解答
//方法1 import java.util.regex.Pattern; class Solution { String chunkIPv4 = "([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"; Pattern pattenIPv4 = Pattern.compile("^(" + chunkIPv4 + "\\.){3}" + chunkIPv4 + "$"); String chunkIPv6 = "([0-9a-fA-F]{1,4})"; Pattern pattenIPv6 = Pattern.compile("^(" + chunkIPv6 + "\\:){7}" + chunkIPv6 + "$"); public String validIPAddress(String IP) { if (IP.contains(".")) { return (pattenIPv4.matcher(IP).matches()) ? "IPv4" : "Neither"; } else if (IP.contains(":")) { return (pattenIPv6.matcher(IP).matches()) ? "IPv6" : "Neither"; } return "Neither"; } } //方法2 public String validateIPv4(String IP) { String[] nums = IP.split("\\.", -1); for (String x : nums) { if (x.length() == 0 || x.length() > 3) return "Neither"; if (x.charAt(0) == '0' && x.length() != 1) return "Neither"; for (char ch : x.toCharArray()) { if (! Character.isDigit(ch)) return "Neither"; } if (Integer.parseInt(x) > 255) return "Neither"; } return "IPv4"; } public String validateIPv6(String IP) { String[] nums = IP.split(":", -1); String hexdigits = "0123456789abcdefABCDEF"; for (String x : nums) { if (x.length() == 0 || x.length() > 4) return "Neither"; for (Character ch : x.toCharArray()) { if (hexdigits.indexOf(ch) == -1) return "Neither"; } } return "IPv6"; } public String validIPAddress(String IP) { if (IP.chars().filter(ch -> ch == '.').count() == 3) { return validateIPv4(IP); } else if (IP.chars().filter(ch -> ch == ':').count() == 7) { return validateIPv6(IP); } else return "Neither"; }
-
分析
- 正则表达式匹配
- 方法2根据两种ip地址的格式进行判断
-
提交结果
方法1
方法2
470. 用 Rand7() 实现 Rand10()
已有方法 rand7 可生成 1 到 7 范围内的均匀随机整数,试写一个方法 rand10 生成 1 到 10 范围内的均匀随机整数。
不要使用系统的 Math.random() 方法。
-
解答
public int rand10() { int row,col,index; do{ row = rand7(); col = rand7(); index = col + (row - 1) * 7; }while(index > 40); return 1 + (index - 1) % 10; }
-
分析
-
拒绝采样,两次rand7的和大于40 则重新随机。返回结果 1 + (co l + (row-1) * 7 - 1) % 10
-
-
提交结果
472. 连接词
给定一个不含重复单词的列表,编写一个程序,返回给定单词列表中所有的连接词。
连接词的定义为:一个字符串完全是由至少两个给定数组中的单词组成的。
-
解答
class Solution { public List<String> findAllConcatenatedWordsInADict(String[] words) { TrieNode root = new TrieNode(); for (String word : words) { if (!word.equals("")) { //非空字符串添加到字典树中 root.add(word); } } List<String> res = new ArrayList<>(); for (String word : words) { if (dfs(word.toCharArray(), 0, root)) { res.add(word); } } return res; } private boolean dfs(char[] str, int start, TrieNode root) { int n = str.length; TrieNode node = root; for (int i = start; i < n; i++) { if (!node.next.containsKey(str[i])) { return false; } node = node.next.get(str[i]); if (node.isWord && dfs(str, i + 1, root)) { return true; } } return node.isWord && start != 0; //start != 0 不能匹配自己 } } class TrieNode { public boolean isWord; public Map<Character, TrieNode> next; public TrieNode() { isWord = false; next = new HashMap<>(); } public void add(String str) { TrieNode node = this; for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); if (!node.next.containsKey(c)) { node.next.put(c, new TrieNode()); } node = node.next.get(c); } node.isWord = true; } }
-
分析
- 构建字典树
- 遍历每个字符串,在字典树中dfs,若能在dfs中找到由2个单词 则返回true
-
提交结果
473. 火柴拼正方形
还记得童话《卖火柴的小女孩》吗?现在,你知道小女孩有多少根火柴,请找出一种能使用所有火柴拼成一个正方形的方法。不能折断火柴,可以把火柴连接起来,并且每根火柴都要用到。
输入为小女孩拥有火柴的数目,每根火柴用其长度表示。输出即为是否能用所有的火柴拼成正方形。
-
解答
public boolean makesquare(int[] nums) { int len = nums.length; int sum = 0; int min = Integer.MAX_VALUE; int max = Integer.MIN_VALUE; for(int i = 0;i < len;i++){ sum += nums[i]; max = Math.max(max,nums[i]); min = Math.min(min,nums[i]); } if(sum % 4 != 0)return false; int edgeLen = sum / 4; if(max > edgeLen || (max < edgeLen && max + min > edgeLen))return false; int[] visited = new int[len]; return dfs(visited,nums,0,edgeLen,0); } public boolean dfs(int[] visited,int[] nums,int temp,int edgeLen,int step){ if(temp > edgeLen) return false; if(step == 4 && temp == 0)return true; for(int i = 0;i < nums.length;i++){ if(visited[i] == 0){ visited[i] = 1; if(temp + nums[i] == edgeLen){ if(dfs(visited,nums,0,edgeLen,step + 1)) return true; }else{ if(dfs(visited,nums,temp + nums[i],edgeLen,step)) return true; } visited[i] = 0; } } return false; }
-
分析
- 若数组的总和不是4的倍数则返回false
- 若最大值大于边长。或者 最大值小于变长的情况下 最大值+最小值大于边长,则返回false
- 之后就是回溯法
- 递归出口,当前计算的边长 大于需要的边长 返回false
- 找到了4个符合的边长组合 返回true
- 遍历数组,若当前火柴没有使用过,分两种情况 第一种是当前计算的边长 + 当前的火柴长度 等于需要的边长 那么计算下一条边长组合,step + 1。第二种是继续计算当前的边长,temp + nums[i]。
-
提交结果
474. 一和零
给你一个二进制字符串数组 strs 和两个整数 m 和 n 。
请你找出并返回 strs 的最大子集的大小,该子集中 最多 有 m 个 0 和 n 个 1 。
如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。
-
解答
public int findMaxForm(String[] strs, int m, int n) { int[][] dp = new int[m + 1][n + 1]; for(String str : strs){ int[] counts = getCounts(str); for(int zeroNumber = m;zeroNumber >= 0;zeroNumber--){ for(int oneNumber = n;oneNumber >= 0;oneNumber--){ if(zeroNumber >= counts[0] && oneNumber >= counts[1]) dp[zeroNumber][oneNumber] = Math.max(dp[zeroNumber][oneNumber],dp[zeroNumber - counts[0]][oneNumber - counts[1]] + 1); } } } return dp[m][n]; } public int[] getCounts(String str){ int[] res = new int[2]; for(int i = 0;i < str.length();i++){ if(str.charAt(i) == '0')res[0]++; else res[1]++; } return res; }
-
分析
-
这是背包问题,只不过这里有两个背包,分别装1和0
-
所以需要2维的dp数组,dp[i] [j] 表示i个0和j个1 最多可以拿多少个字符串
-
动态转移方程
dp[i] [j] = Math.max(dp[i] [j],dp[i - k[0]] [j - k[1]] + 1);
k[0] 表示当前字符串的0的个数,k[1]表示当前字符串1的个数。
-
按顺序遍历字符串,从大到小遍历i和j 就可得到结果
-
-
提交结果
475. 供暖器
冬季已经来临。 你的任务是设计一个有固定加热半径的供暖器向所有房屋供暖。
在加热器的加热半径范围内的每个房屋都可以获得供暖。
现在,给出位于一条水平线上的房屋 houses 和供暖器 heaters 的位置,请你找出并返回可以覆盖所有房屋的最小加热半径。
说明:所有供暖器都遵循你的半径标准,加热的半径也一样。
-
解答
//方法1 public int findRadius(int[] houses, int[] heaters) { Arrays.sort(houses); Arrays.sort(heaters); int res = 0; int i = 0; for (int house : houses) { for (i = 0; i < heaters.length - 1; i++) { if (Math.abs(heaters[i] - house) < Math.abs(heaters[i + 1] - house)) { break; } } res = Math.max(res, Math.abs(heaters[i] - house)); } return res; } //方法2 public int findRadius(int[] houses, int[] heaters) { //二分查找需要对象有序 Arrays.sort(heaters); int maxR = 0; for(int house : houses){ //需所有房屋最小供暖器距离最大值 maxR = Math.max(maxR, search(house, heaters)); } return maxR; } private int search(int house, int[] heaters) { int left = 0, right = heaters.length - 1; while (left <= right) { //折半查找 int mid = left + (right - left) / 2; //当前位置有供暖器那么这个距离就为 0 if(heaters[mid] == house){ return 0; } else if(heaters[mid] > house){ right = mid - 1; } else { left = mid + 1; } } //如果一直找最后发现 left 出了 heaters 右边界,说明当前房屋位置是大于最大供暖器位置的,直接相减 if(left > heaters.length - 1){ return house - heaters[heaters.length - 1]; } //如果一直找最后发现 right 小于 0,说明当前房屋位置是小于最小供暖器位置的,直接相减 if(right < 0){ return heaters[0] - house; } //最后分别在该房屋两边各找到了最近的供暖器,那么取这两个供暖器到当前房屋最近距离即可 return Math.min(heaters[left] - house,house -heaters[right]); }
-
分析
- 方法1
- 找到房屋距离最近的供暖器
- 返回的结果就是所有房屋到最近的供暖器的最大值
- 方法2 在方法1的基础上的改进,使用2分查找,加快搜索的效率
-
提交结果
方法1
方法2
476. 数字的补数
给定一个正整数,输出它的补数。补数是对该数的二进制表示取反。
-
解答
public int findComplement(int num) { String str = Integer.toBinaryString(num); int res = 0; int n = 1; for(int i = str.length() - 1;i >=0 ;i--){ char cur = str.charAt(i); if(cur == '0'){ res += n; } n *= 2; } return res; }
-
分析
- 将数字转2进制,从末尾开始遍历
- 遇到’0’ 则加上这一位表示的10进制的值。
-
提交结果
477. 汉明距离总和
两个整数的 汉明距离 指的是这两个数字的二进制数对应位不同的数量。
计算一个数组中,任意两个数之间汉明距离的总和。
-
解答
public int totalHammingDistance(int[] nums) { int len = nums.length; int res = 0; for(int i = 0;i <= 31;i++){ int t = 0; for(int j = 0;j < len;j++){ int cur = nums[j] & (1 << i); if(cur > 0)t++; } res += t * (len - t); } return res; }
-
分析
- 计算汉明距离可以根据二进制中的每一位分开计算。
- 假设第i位为1的个数为t 一共有n个数字
- 那么这一位构成的汉明距离就是t * (n - t)
- int类型一共32位,所以可以计算所有数字中一个位置为1的数量。就可以得到这一位可以得到的汉明距离
-
提交结果
478. 在圆内随机生成点
给定圆的半径和圆心的 x、y 坐标,写一个在圆中产生均匀随机点的函数 randPoint 。
说明:
- 输入值和输出值都将是浮点数。
- 圆的半径和圆心的 x、y 坐标将作为参数传递给类的构造函数。
- 圆周上的点也认为是在圆中。
- randPoint 返回一个包含随机点的x坐标和y坐标的大小为2的数组。
-
解答
class Solution { double x_center; double y_center; double radius; public Solution(double radius, double x_center, double y_center) { this.x_center = x_center; this.y_center = y_center; this.radius = radius; } public double[] randPoint() { double x_min = x_center - radius; double x_max = x_center + radius; double y_min = y_center - radius; double y_max = y_center + radius; while(true){ double x = Math.random() * (x_max - x_min) + x_min; double y = Math.random() * (y_max - y_min) +y_min; if(Math.pow(x - x_center,2) + Math.pow(y - y_center,2) < Math.pow(radius,2)) return new double[]{x,y}; } } }
-
分析
- 根据圆心和半径,计算出 x和y的最大值和最小值
- 根据x和y的最大值和最小值 生成随机数。
- 若生成的随机数坐标距离圆心的距离小于半径则直接返回,否则继续生成新的随机数
-
提交结果
479. 最大回文数乘积
你需要找到由两个 n 位数的乘积组成的最大回文数。
由于结果会很大,你只需返回最大回文数 mod 1337得到的结果。
示例:
输入: 2
输出: 987
解释: 99 x 91 = 9009, 9009 % 1337 = 987
说明:
n 的取值范围为 [1,8]。
-
解答
public int largestPalindrome(int n) { if (n == 1){ return 9; } String max = ""; String min = "1"; for (int i = 0; i < n; i++) { max += "9"; } for (int i = 0; i < n - 1; i++) { min += "0"; } long maxResultNum = Long.parseLong(max); long minResultNum = Long.parseLong(min); long indexNum = maxResultNum; while (indexNum >= minResultNum) { long thisResult = getHuiwen(indexNum);//构造回文数 long thisNum = maxResultNum; while(thisNum > minResultNum && thisNum * thisNum > thisResult){//要使回文数最大,所以第一位为9,那么最后一位回文数就是9 所以 乘数一定是3、7、9结尾的 if(thisResult%thisNum==0) return (int) (thisResult%1337); if(thisNum%10==9){ thisNum-=2; }else thisNum-=4; } indexNum--; } return 0; } public long getHuiwen(long num) { char[] numCharArray = String.valueOf(num).toCharArray(); int length = numCharArray.length; StringBuilder result = new StringBuilder(num + ""); for (int i = length - 1; i >= 0; i--) { result.append(numCharArray[i]); } return Long.parseLong(result.toString()); }
-
分析
-
最大回文数一定是9开头的
-
那么它的最后一位也必定是9
-
所以限定了乘数最后一位只能是3、7、9
才有可能得到最后一位是9的情况。
-
根据给定的n 得到乘数的最大值和最小值的范围。
-
从大到小遍历乘数
-
根据乘数 构造回文数。
-
若乘数相乘得到的结果大于构造出来的回文数。
-
若回文数可以整除乘数,说明找到了答案。返回结果
-
若乘数%10 ==9。则减2 用7去判断
-
否则减4 用3去判断
-
-
提交结果