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;
    }
}

class Solution {
    private int res = Integer.MAX_VALUE;
    private int val;
    private boolean first = true;
    public int getMinimumDifference(TreeNode root) {
        dfs(root);
        return res;
    }

    private void dfs(TreeNode node){
        if(node == null){
            return;
        }
        dfs(node.left);
        
        if(first){
            first = false;
        }else{
            res = Math.min(node.val - val, res);
        }
        val = node.val;
        dfs(node.right);
    }
}

题目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 的形式。

  1. 输入字符串不包含额外的空格。
  2. 输入字符串将以 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;
    }
}
posted @ 2020-12-15 13:47  maoguai  阅读(79)  评论(0编辑  收藏  举报