• 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;
        }
    }
  • 相关阅读:
    我的未来。
    我的的第一篇博客
    从软件工程角度回顾本科毕业论文
    从高级软件工程角度分析毕业设计-小结-盛超
    从软件工程视角,回顾分析本科毕业设计软件中存在的不足问题
    从软件工程的角度分析本科毕业设计
    从高级软件工程角度分析本科毕业设计
    从软件工程的视角,回顾本科毕业设计,探视设计中存在的不足
    用软件工程思想看毕业设计
    从软件工程角度分析毕业设计
  • 原文地址:https://www.cnblogs.com/mgdzy/p/14138084.html
Copyright © 2020-2023  润新知