leetcode刷题 519~
题目519题
随机翻转矩阵
题中给出一个 n_rows 行 n_cols 列的二维矩阵,且所有值被初始化为 0。要求编写一个 flip 函数,均匀随机的将矩阵中的 0 变为 1,并返回该值的位置下标 [row_id,col_id];同样编写一个 reset 函数,将所有的值都重新置为 0。尽量最少调用随机函数 Math.random(),并且优化时间和空间复杂度。
注意:
1 <= n_rows, n_cols <= 10000
0 <= row.id < n_rows 并且 0 <= col.id < n_cols
当矩阵中没有值为 0 时,不可以调用 flip 函数
调用 flip 和 reset 函数的次数加起来不会超过 1000 次
思路
1.思路非常简单,采用蓄水池抽样,但在实现过程中,我首先采用了一个一位数组来维护抽样结果。但在10000*10000的情况下会遇到超出内存限制的问题。由于题目说调用 flip 和 reset 函数的次数加起来不会超过 1000 次,因此可以采用哈希表的方式来实现。当选择一个随机数时,其在哈希表中默认值为当前值,然后将以此值为键的值换为当前情况下的最大值。
2.分块:
我们可以考虑另一种方法来维护这个一维数组 V。假设我们把这 n_rows * n_cols 个位置放到 k 个桶中,第一个桶对应 V[0 .. a1],第二个桶对应 V[a1 + 1 .. a2],以此类推。我们用 cnt[i] 表示第 i 个桶中还剩下几个 0,并给每个桶分配一个集合(HashSet)存放桶中哪些位置对应的是 1(即被翻转过)。
实现
class Solution { Map<Integer, Integer> res = new HashMap<>(); private int rows; private int cols; private int rand; Random r = new Random(); public Solution(int n_rows, int n_cols) { rows = n_rows; cols = n_cols; rand = rows*cols; } public int[] flip() { int RandIndex = r.nextInt(rand); rand -=1; int[] result = new int[2]; int RandNum = res.getOrDefault(RandIndex, RandIndex); res.put(RandIndex, res.getOrDefault(rand,rand)); result[0] = (int) RandNum/cols; result[1] = RandNum % cols; return result; } public void reset() { res.clear(); rand = rows * cols; } }
题目520题
检测大写字母
给定一个单词,你需要判断单词的大写使用是否正确。
我们定义,在以下情况时,单词的大写用法是正确的:
全部字母都是大写,比如"USA"。
单词中所有字母都不是大写,比如"leetcode"。
如果单词不只含有一个字母,只有首字母大写, 比如 "Google"。
否则,我们定义这个单词没有正确使用大写字母。
思路实现
class Solution { public boolean detectCapitalUse(String word) { char[] words = word.toCharArray(); int upper=0, lower=0, first=0; if (Character.isUpperCase(words[0])){ first =1; } for(char w: words){ if(Character.isUpperCase(w)){ upper += 1; } else{ lower += 1; } if( (upper >1 && lower >=1) || (first==0 && upper > 0) ){ return false; } } return true; } }
题目521题
最长特殊序列I
给你两个字符串,请你从这两个字符串中找出最长的特殊序列。
「最长特殊序列」定义如下:该序列为某字符串独有的最长子序列(即不能是其他字符串的子序列)。
子序列 可以通过删去字符串中的某些字符实现,但不能改变剩余字符的相对顺序。空序列为所有字符串的子序列,任何字符串为其自身的子序列。
输入为两个字符串,输出最长特殊序列的长度。如果不存在,则返回 -1。
思路实现
class Solution { public int findLUSlength(String a, String b) { if (a.equals(b)) return -1; return Math.max(a.length(), b.length()); } }
题目522题
最长特殊字符II
给定字符串列表,你需要从它们中找出最长的特殊序列。最长特殊序列定义如下:该序列为某字符串独有的最长子序列(即不能是其他字符串的子序列)。
子序列可以通过删去字符串中的某些字符实现,但不能改变剩余字符的相对顺序。空序列为所有字符串的子序列,任何字符串为其自身的子序列。
输入将是一个字符串列表,输出是最长特殊序列的长度。如果最长特殊序列不存在,返回 -1 。
思路
1.暴力法
2.检查每个字符串是否是别的字符串的子串
实现
1. class Solution { public int findLUSlength(String[] strs) { HashMap<String, Integer> map = new HashMap<String, Integer>(); int curLen = -1; for(String str: strs){ for (int i = 0; i < (1 << str.length()); i++) { String t = ""; for (int j = 0; j < str.length(); j++) { if ((i & (1 << j)) != 0) t += str.charAt(j); } if (map.containsKey(t)){ map.put(t,-1); } else{ map.put(t,t.length()); } } } curLen = -1; for(String key: map.keySet()){ curLen = Math.max(curLen, map.get(key)); } return curLen; } } 2. class Solution { private boolean isSub(String s1, String s2){ int l =0; for(int i =0; i < s2.length()&& l < s1.length(); i++){ if (s1.charAt(l) == s2.charAt(i)){ l +=1; } } return l == s1.length(); } public int findLUSlength(String[] strs) { int res =-1; for(int i =0; i < strs.length; i++){ int j = 0; for(j=0; j < strs.length; j++){ if (i == j){ continue; } if(isSub(strs[i], strs[j])){ break; } } if(j == strs.length){ res = Math.max(res, strs[i].length()); } } return res; } }
题目523题
连续的子数组和
给定一个包含 非负数 的数组和一个目标 整数 k,编写一个函数来判断该数组是否含有连续的子数组,其大小至少为 2,且总和为 k 的倍数,即总和为 n*k,其中 n 也是一个整数。
示例 1:
输入:[23,2,4,6,7], k = 6
输出:True
解释:[2,4] 是一个大小为 2 的子数组,并且和为 6。
思路
使用 HashMap 来保存到第 i个元素为止的累积和,但我们对这个前缀和除以 k 取余数。当出现相同的余数时,且距离大于1则说明出现了目标数组。
实现
class Solution { public boolean checkSubarraySum(int[] nums, int k) { int sum = 0; HashMap < Integer, Integer > map = new HashMap < > (); map.put(0, -1); for(int i=0; i < nums.length;i++){ sum += nums[i]; if (k!=0) sum = sum % k; if(map.containsKey(sum)){ if(i-map.get(sum)>1){ return true; } } else{ map.put(sum,i); } } return false; } }
题目524题
通过删除字母匹配到字典里最长单词
给定一个字符串和一个字符串字典,找到字典里面最长的字符串,该字符串可以通过删除给定字符串的某些字符来得到。如果答案不止一个,返回长度最长且字典顺序最小的字符串。如果答案不存在,则返回空字符串。
示例 1:
输入:
s = "abpcplea", d = ["ale","apple","monkey","plea"]
输出:
"apple"
思路
判断是否为子串
实现
class Solution { public String findLongestWord(String s, List<String> d) { int maxLen = 0; String maxStr = ""; for(String t: d){ if(isSub(t, s)){ if(t.length() > maxLen || (t.length()== maxLen && t.compareTo(maxStr) < 0)){ maxStr = t; maxLen = t.length(); } } } return maxStr; } private boolean isSub(String s1, String s2){ int l =0; for(int i =0; i < s2.length()&& l < s1.length(); i++){ if (s1.charAt(l) == s2.charAt(i)){ l +=1; } } return l == s1.length(); } }
题目525题
连续数组
给定一个二进制数组, 找到含有相同数量的 0 和 1 的最长连续子数组(的长度)。
示例 1:
输入: [0,1]
输出: 2
说明: [0, 1] 是具有相同数量0和1的最长连续子数组。
思路
遇到0则减1,遇到1则加1,当出现相同值的时候,则是目标数组
实现
class Solution { public int findMaxLength(int[] nums) { HashMap < Integer, Integer > map = new HashMap <>(); map.put(0,-1); int sum = 0; int maxLen =0; for(int index=0; index<nums.length; index++){ sum += (nums[index] == 1 ? 1: -1); if(map.containsKey(sum)){ maxLen = Math.max(maxLen, index - map.get(sum)); }else{ map.put(sum, index); } } return maxLen; } }
题目526题
优美的排列
假设有从 1 到 N 的 N 个整数,如果从这 N 个数字中成功构造出一个数组,使得数组的第 i 位 (1 <= i <= N) 满足如下两个条件中的一个,我们就称这个数组为一个优美的排列。条件:
第 i 位的数字能被 i 整除
i 能被第 i 位上的数字整除
现在给定一个整数 N,请问可以构造多少个优美的排列?
示例1:
输入: 2
输出: 2
解释:
第 1 个优美的排列是 [1, 2]:
第 1 个位置(i=1)上的数字是1,1能被 i(i=1)整除
第 2 个位置(i=2)上的数字是2,2能被 i(i=2)整除
第 2 个优美的排列是 [2, 1]:
第 1 个位置(i=1)上的数字是2,2能被 i(i=1)整除
第 2 个位置(i=2)上的数字是1,i(i=2)能被 1 整除
思路
回溯:注释的代码是去重的,结果更慢了
实现
class Solution { private boolean[] visited; private int res = 0; private int N; //private HashSet<String> error = new HashSet<String>(); public int countArrangement(int N) { this.N = N; visited = new boolean[N]; backtace(1); return res; } private void backtace(int curIndex){ if(curIndex == N+1){ res += 1; return; } for(int numIndex=0; numIndex < N; numIndex++){ if(!visited[numIndex] ){ int num = numIndex +1; /* if(error.contains(num+"!/"+curIndex) || error.contains(curIndex+"!/"+num)){ continue; }*/ if((curIndex % num == 0) || (num % curIndex ==0) ){ visited[numIndex] = true; backtace(curIndex+1); visited[numIndex] = false; }/*else{ error.add(num+ "!/" +curIndex); error.add(curIndex+ "!/" +num); }*/ } } } }
题目528题
按权重随机选择
给定一个正整数数组 w ,其中 w[i] 代表下标 i 的权重(下标从 0 开始),请写一个函数 pickIndex ,它可以随机地获取下标 i,选取下标 i 的概率与 w[i] 成正比。
例如,对于 w = [1, 3],挑选下标 0 的概率为 1 / (1 + 3) = 0.25 (即,25%),而选取下标 1 的概率为 3 / (1 + 3) = 0.75(即,75%)。
也就是说,选取下标 i 的概率为 w[i] / sum(w) 。
思路
二分法
实现
class Solution { private int sum = 0; Random random =new Random(); private int nums[]; public Solution(int[] w) { nums = new int[w.length]; for(int index = 0; index < w.length; index++){ sum += w[index]; nums[index] = sum; } } public int pickIndex() { int randnum = random.nextInt(sum); int left = 0, right = nums.length -1; while(left != right){ int mid = left + (right - left)/2; if(randnum >= nums[mid]) left = mid + 1; else right = mid; } return left; } }
题目529题
扫雷游戏
让我们一起来玩扫雷游戏!
给定一个代表游戏板的二维字符矩阵。 'M' 代表一个未挖出的地雷,'E' 代表一个未挖出的空方块,'B' 代表没有相邻(上,下,左,右,和所有4个对角线)地雷的已挖出的空白方块,数字('1' 到 '8')表示有多少地雷与这块已挖出的方块相邻,'X' 则表示一个已挖出的地雷。
现在给出在所有未挖出的方块中('M'或者'E')的下一个点击位置(行和列索引),根据以下规则,返回相应位置被点击后对应的面板:
如果一个地雷('M')被挖出,游戏就结束了- 把它改为 'X'。
如果一个没有相邻地雷的空方块('E')被挖出,修改它为('B'),并且所有和其相邻的未挖出方块都应该被递归地揭露。
如果一个至少与一个地雷相邻的空方块('E')被挖出,修改它为数字('1'到'8'),表示相邻地雷的数量。
如果在此次点击中,若无更多方块可被揭露,则返回面板。
思路
递归
实现
class Solution { private char[][] board; public char[][] updateBoard(char[][] board, int[] click) { int row = click[0], col = click[1]; this.board = board; updateBoard(row,col); return this.board; } private void updateBoard(int row, int col){ if(board[row][col] != 'M' && board[row][col] != 'E'){ return; } if(board[row][col] == 'M'){ board[row][col] = 'X'; return; } int boom = 0; for(int i = -1; i <=1; i++){ for(int j =-1; j <=1; j++){ int Row = row + i, Col = col + j; if(Row >=0 && Row < board.length && Col >= 0 && Col < board[0].length){ if(board[Row][Col] == 'M'){ boom += 1; } } } } if(boom == 0){ board[row][col] = 'B'; for(int i = -1; i <=1; i++){ for(int j =-1; j <=1; j++){ int Row = row + i, Col = col + j; if(Row >=0 && Row < board.length && Col >= 0 && Col < board[0].length){ updateBoard(Row,Col); } } } }else{ board[row][col] = (char)(boom+48); } return; } }
题目530题
二叉搜索树的最小绝对差
给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。
思路
1.递归,每个节点的最小绝对值等于,这个节点与左子树的最大值的差 和 右子树最小值与节点的差的最小值
2.中序遍历
实现
class Solution { public int getMinimumDifference(TreeNode root) { int[] res = get(root); return res[0]; } private int[] get(TreeNode node){ int[] res = new int[]{Integer.MAX_VALUE, node.val, node.val};//最小绝对值,最小值,最大值 if(node.left == null && node.right == null){ return res; } int[] temp; int resL0 = Integer.MAX_VALUE, resR0 =Integer.MAX_VALUE; int resL1 = node.val; if(node.left != null){ temp = get(node.left); resL0 = Math.min(temp[0], res[2]-temp[2]); resL1 = temp[1]; } if(node.right != null){ temp = get(node.right); resR0 = Math.min(temp[0], temp[1]-res[1]); res[2] = temp[2]; } res[0] = Math.min(resL0, resR0); res[1] = resL1; return res; } }
题目532题
数组中的k-diff数对
给定一个整数数组和一个整数 k,你需要在数组里找到不同的 k-diff 数对,并返回不同的 k-diff 数对 的数目。
这里将 k-diff 数对定义为一个整数对 (nums[i], nums[j]),并满足下述全部条件:
0 <= i, j < nums.length
i != j
|nums[i] - nums[j]| == k
注意,|val| 表示 val 的绝对值。
思路
实现
class Solution { public int findPairs(int[] nums, int k) { HashMap<Integer, Integer> pairs = new HashMap<Integer, Integer>(); int res = 0; for(int num: nums){ if(pairs.containsKey(num)&& k!=0) continue; if(k == 0 && pairs.containsKey(num)){ res += (pairs.get(num) == 1? 1:0 ); } pairs.put(num, pairs.getOrDefault(num,0)+1); if(pairs.containsKey(num-k)&& k!=0){ res += 1; } if(pairs.containsKey(num+k)&& k!= 0){ res += 1; } } return res; } }
题目535题
TinyURL的加密解密
思路实现
public class Codec { private HashMap<String, String> map = new HashMap<>(); private int num = 0; // Encodes a URL to a shortened URL. public String encode(String longUrl) { String shortUrl = "http://tinyurl.com/"+num; map.put(shortUrl, longUrl); num += 1; return shortUrl; } // Decodes a shortened URL to its original URL. public String decode(String shortUrl) { return map.get(shortUrl); } }
题目537题
复数乘法
给定两个表示复数的字符串。
返回表示它们乘积的字符串。注意,根据定义 i2 = -1 。
示例 1:
输入: "1+1i", "1+1i"
输出: "0+2i"
解释: (1 + i) * (1 + i) = 1 + i2 + 2 * i = 2i ,你需要将它转换为 0+2i 的形式。
- 输入字符串不包含额外的空格。
- 输入字符串将以 a+bi 的形式给出,其中整数 a 和 b 的范围均在 [-100, 100] 之间。输出也应当符合这种形式。
思路实现
class Solution { public String complexNumberMultiply(String a, String b) { int[] ac = complexGet(a); int[] bc = complexGet(b); int resa = ac[0]*bc[0] - bc[1]*ac[1]; int resb = ac[0]*bc[1] + ac[1]*bc[0]; return resa + "+" + resb + "i"; } private int[] complexGet(String complex){ int[] res = new int[]{0,0}; char[] nums=complex.toCharArray(); int index = 0; int add = 1; for(char num: nums){ if(num == '-'){ add = -1; } if(num == '+' || num == 'i'){ res[index] *= add; add =1; index +=1; } if(Character.isDigit(num)){ res[index] = res[index]*10 + (num - '0'); } } return res; } }