Loading

LeetCode 精选 41-55 until 10-10

41. 缺失的第一个正数

哈希的本质

🤔哈希其实并没有直接完成键到值的映射,实际上是完成了键->索引的映射

至于索引(循秩访问)当中的内容

这个元素曾经出现过

if(nums[i - 1] == FLAG)

这个题目有特殊的限制:[1,N+1]固定区间出现答案,因此,不需要额外空间

如何实现可见区间之内的计数?

  • 合法区间之内,在指定位置映射上一个标记

    • 如果该位置存在一个没有被处理的数字

    • 那么进行如下操作

      // 注意防止死循环
      // nums[x - 1] != x 即未做标记
      while(valid(nums[i]) && nums[i] != nums[nums[i] - 1])swap(nums[i],nums[nums[i] - 1]);
      
  • 合法区间之外,不做标记

class Solution {
    public int firstMissingPositive(int[] nums) {
        int len = nums.length;
        for(int i = 0;i<len;i++){
            // for each element
            while(nums[i]>0 && nums[i] <=len && nums[i] != nums[nums[i] - 1])swap(nums,i,nums[i] - 1);
        }
        for(int i = 0;i<len;i++){
            if(nums[i] != i + 1)return i + 1;
        }
        return len + 1;
    }
    void swap(int[] nums,int i,int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

通过多重属性解决覆盖 / 后效性问题

42. 接雨水

动态规划 ≈黎曼积分

\[f(i) = \min(\max(L[i],R[i])) - h[i] \]

❗注意:L[0] = height[0] 报错💣
height[0]也不一定存在

class Solution {
    int[] L;
    int[] R;
    public int trap(int[] height) {
        if(height.length == 0)return 0;
        L = new int[height.length];
        R = new int[height.length];
        L[0] = height[0];
        for(int i = 1;i<height.length;i++){
            L[i] = Math.max(height[i],L[i-1]);
        }
        R[height.length - 1] = height[height.length - 1];
        for(int i = height.length - 2;i>=0;i--){
            R[i] = Math.max(height[i],R[i+1]);
        }
        int ans = 0;
        for(int i = 0;i<height.length;i++){
            // assess the final condition
            // there is a column of water supported by the brick beneath
            ans += Math.min(L[i],R[i]) - height[i];
        }
        return ans;
    }
}

单调栈

这个方法感觉普遍性不强

class Solution {
    Stack<Integer> s;
    public int trap(int[] height) {
        s = new Stack<Integer>();
        int ans = 0;
        int cur = 0;
        while(cur < height.length){
            while(!s.empty() && height[s.peek()] < height[cur]){
                int i = s.pop();
                if(s.empty())break;
                int d = cur - s.peek() - 1;
                int h = Math.min(height[s.peek()],height[cur]) - height[i];
                ans += h * d;
            }
            s.push(cur++);
        }
        return ans;
    }
}

44. 通配符匹配

输入:

"ho" "**ho"

输出:

false

预期结果:

true

# h o

# * * h o

考察必要条件:如果需要一个true的结果,必须保证dp[0][2] = true

dp[0][2] = dp[0][1] = dp[0][0] = true; 
class Solution {
    boolean [][] dp;
    public boolean isMatch(String s, String p) {
        // null 
        if(s.length()== 0 && p.length()==0)return true;
        if(s.length()!= 0 && p.length()==0)return false;
        int len1 = s.length();
        int len2 = p.length();
        // bounding flags
        dp = new boolean[len1+10][len2+10];
        dp[0][0] = true;
        if(p.charAt(0) == '*'){
            for(int i = 0;i<=len1;i++){
                dp[i][1] = true;
            }
        }
        for(int i = 0;i<=len1;i++){
            for(int j = 1;j<=len2;j++){
                if(p.charAt(j-1) == '?'){
                    if(i>=1)dp[i][j] = dp[i-1][j-1];
                }else if(p.charAt(j-1) == '*'){
                    // neglect or use it as '?'
                    dp[i][j] = dp[i][j-1];
                    if(i>=1)dp[i][j] |= dp[i-1][j-1] | dp[i-1][j];
                }else{
                    if(i>=1 && p.charAt(j-1) == s.charAt(i-1)){
                        dp[i][j] = dp[i-1][j-1];
                    }else{
                        dp[i][j] = false;
                    }
                }
            }
        }
        return dp[len1][len2];
    }
}

代码简化

 if(p.charAt(0) == '*'){
     for(int i = 0;i<=len1;i++){
         dp[i][1] = true;
     }
 }

下面的这段代码很好地实现了对连续*号的标记

for & break

for (int i = 1; i <= n; ++i) {
    if (p.charAt(i - 1) == '*') {
        dp[0][i] = true;
    } else {
        break;
    }
}

else if(p.charAt(j-1) == '*'){
    // neglect or use it as '?'
    dp[i][j] = dp[i][j-1];
    if(i>=1)dp[i][j] |= dp[i-1][j-1] | dp[i-1][j];
}

这段代码实际上和 dp[i][j] = dp[i-1][j] | dp[i][j-1]; 等价

dp[i-1][j-1](匹配一个并且跳过) = dp[i][j-1] & dp[i-1][j]

46. 全排列

回溯法

class Solution {
    List<List<Integer>> ret;
    List<Integer> out;
    int n;
    public List<List<Integer>> permute(int[] nums) {
        ret = new ArrayList<List<Integer>>();
        out = new ArrayList<Integer>();
        n = nums.length;
        for(int i : nums){
            out.add(i);
        }
        bp(0);
        return ret;
    }
    public void bp(int cur){
        if(cur == n){
            ret.add(new ArrayList<Integer>(out));
            return;
        }else{
            for(int i = cur;i<n;i++){
                // 其实 i = cur没有用
                Collections.swap(out,i,cur);
                // 但是需要保证 bp(cur + 1)可以进入
                bp(cur+1);
                Collections.swap(out,i,cur);
            }
        }
    }
}

48. 旋转图像

遍历每一个元素,需要开辟O(N^2)的辅助空间

群论——变换的连续作用

对角线反转 + 对于垂直中线的反射 = 顺指针旋转90°

工程思想——单变量辅助,就地反转

You’re just one step away from the answer:
Give an Example!

🔑BUFFER

考虑数组删除元素后的移动操作

class Solution {
    public void rotate(int[][] matrix) {
        int n = matrix.length;
        // layer
        for(int l = 0;l<n/2;l++){
            // no.
            for(int i = l;i<n-l-1;i++){
                int r = l;
                int c = i;
                int temp = matrix[r][c];
                // r,c
                // c,n-1-r
                // n-1-r,n-1-c
                // n-1-c,r
                // inv. order
                matrix[r][c] = matrix[n-1-c][r];
                matrix[n-1-c][r] = matrix[n-1-r][n-1-c];
                matrix[n-1-r][n-1-c] = matrix[c][n-1-r];
                matrix[c][n-1-r] = temp;
            }
        }
    }
}

49. 字母异位词分组

一遍过!

class Solution {
    Map<Map<Character,Integer>,List<String>> map;
    void gen(String word){
        Map<Character,Integer> count = new HashMap<Character,Integer>();
        for(int i = 0;i<word.length();i++){
            char c = word.charAt(i);
            count.put(c,count.getOrDefault(c,0)+1);
        }
        if(!map.containsKey(count)){
            List<String> list = new ArrayList<String>();
            list.add(word);
            map.put(count,list);
        }else{
            map.get(count).add(word);
        }
    }
    public List<List<String>> groupAnagrams(String[] strs) {
        map = new HashMap<Map<Character,Integer>,List<String>>();
        for(String s : strs){
            gen(s);
        }
        List<List<String>> ans = new ArrayList<List<String>>();
        for(List<String> list: map.values()){
            ans.add(list);
        }
        return ans;
    }
}

排序字符串:同素异形体😂的充要条件,可以直接作为键

53. 最大子序和

这个自己写还真是写不出来……

PS:举例子的时候一定要举得全面一点,看似正确的方法其实不一定对

软件测试的重要性 hhh

class Solution {
    public int maxSubArray(int[] nums) {
        int[] dp = new int[nums.length];
        int ans = Integer.MIN_VALUE,sum = 0;
        for(int i = 0;i<nums.length;i++){
            // max contiguous subarray with nums[i-1] as tail
            sum = Math.max(sum + nums[i],nums[i]);
            // then you choose whether to accept it
            ans = Math.max(ans,sum);
        }
        return ans;
    }
}

分治——线段树思想

50. Pow(x, n)

💃 AC!

class Solution {
    public double myPow(double x, int n) {
        double ans = 1.0;
        if(n == Integer.MIN_VALUE){
            n++;
            ans = x;
        }
        boolean neg = n<0;
        if(n<0)n = -n;
        double base = x;
        while(n!=0){
            if((n&1) == 1){
                ans *= base;
            }
            base *= base;
            n >>= 1;
        }
        return neg?1/ans:ans;
    }
}

54. Spiral Matrix

动态Bound

class Solution {
    int m,n;
    public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> ans = new ArrayList<Integer>();
        int m = matrix.length;
        if(m == 0)return ans;
        int n = matrix[0].length;   
        int tar = m*n;
        int l = 0,r = n - 1,u = 0,b = m - 1;
        int count = 0;
        while(count < tar){
            for(int i = l;i<=r;i++){
                ans.add(matrix[u][i]);
                count++;
            }
            u++;
            if(count >= tar)break;
            for(int i = u;i<=b;i++){
                ans.add(matrix[i][r]);
                count++;
            }
            r--;
            if(count >= tar)break;
            for(int i = r;i>=l;i--){
                ans.add(matrix[b][i]);
                count++;
            }
            b--;
            if(count >= tar)break;
            for(int i = b;i>=u;i--){
                ans.add(matrix[i][l]);
                count++;
            }
            l++;
            if(count >= tar)break;
        }
        return ans;
    }
}

55. Jump Game

做一个小证明

nums[len - 1]可达

等效于 bound <= len-1

  • 必要性显然,既bound的含义

  • 充分性

    如果可达nums[len - 1],必然有nums[k]可达,并且nums[k] + k >= len - 1;

??这话咋说?

就这?😂

class Solution {
    public boolean canJump(int[] nums) {
        int b = 0;
        for(int i = 0;i<nums.length;i++) b = i<=b?Math.max(b,i+nums[i]):b;
        return b >= (nums.length - 1);
    }
}

posted @ 2020-11-06 20:38  ZXYFrank  阅读(109)  评论(0编辑  收藏  举报