leetcode必刷题(四)lee75 颜色分类、lee76 最小覆盖字串、lee78 子集、lee79 单词搜索、lee84 柱状图中最大的矩形、lee85 最大矩形、lee96 不同的二叉搜索树、

本文精选LeetCode上的经典算法题目,包括颜色分类、最小覆盖子串、子集生成、单词搜索、柱状图最大矩形、最大矩形、二叉树遍历与构造、验证二叉搜索树、对称二叉树等,详细解析了每道题目的解题思路与代码实现,涵盖回溯、滑动窗口、单调栈、动态规划等多种算法。
摘要由CSDN通过智能技术生成
  1. lee75 颜色分类 patition 三个指针
  2. lee76 最小覆盖字串 滑动窗口
  3. lee78 子集 无重复数字 回溯
  4. lee79 单词搜索 回溯
  5. lee84 柱状图中最大的矩形 单调栈
  6. lee85 最大矩形 (用84的方法,在矩阵中)
  7. lee95 二叉树中序遍历
  8. lee96 不同的二叉搜索树 总结规律 动态规划
  9. lee98 验证二叉搜索树
  10. lee101 对称二叉树

lee75 颜色分类

同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
注意:
不能使用代码库中的排序函数来解决这道题。
示例:
输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]

public void sortColors(int[] nums) {
    int len = nums.length;
    if (len < 2) {
        return;
    }
    // all in [0, zero] = 0
    // all in (zero, i) = 1
    // all in (two, len - 1] = 2

    // 为了保证初始化的时候 [0, zero] 为空,设置 zero = -1,
    // 所以下面遍历到 0 的时候,先加,再交换
    int zero = -1;

    // 为了保证初始化的时候 (two, len - 1] 为空,设置 two = len - 1
    // 所以下面遍历到 2 的时候,先交换,再减
    int two = len - 1;
    int i = 0;
    // 当 i == two 的时候,还有一个元素还没有看,
    // 因此,循环可以继续的条件是 i <= two
    while (i <= two) {
        if (nums[i] == 0) {
            zero++;
            swap(nums, i, zero);
            i++;
        } else if (nums[i] == 1) {
            i++;
        } else {
            swap(nums, i, two);
            two--;
        }
    }
}

    private void swap(int[] nums, int index1, int index2) {
        int temp = nums[index1];
        nums[index1] = nums[index2];
        nums[index2] = temp;
    }

lee76 最小覆盖子串

给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。
示例:
输入: S = “ADOBECODEBANC”, T = “ABC”
输出: “BANC”
说明:
如果 S 中不存这样的子串,则返回空字符串 “”。

class Solution {
    public String minWindow(String s, String t) {
        HashMap<Character, Integer> need = new HashMap<>();
        HashMap<Character, Integer> window = new HashMap<>();
        // 存需要的字母,可能会有重复
        for (int i = 0; i < t.length(); i++) {
            char curChar = t.charAt(i);
            need.put(curChar, need.getOrDefault(curChar, 0) + 1);
        }
        int left = 0, right = 0;
        int valid = 0;
        int start = 0, minLen = Integer.MAX_VALUE;

        while (right < s.length()) {
            char c = s.charAt(right);
            right++;
            if (need.containsKey(c)) {
                window.put(c, window.getOrDefault(c, 0) + 1);
                if ((int)window.get(c) ==(int) need.get(c)) {// 强转
                    valid++;
                }
            }
            // 判断左侧窗口
            while (valid == need.size()) {
                if (right - left < minLen) {
                    start = left;
                    minLen = right - left;
                }
                char d = s.charAt(left);
                left++;
                if (need.containsKey(d)) {
                    if ((int)window.get(d) ==(int) need.get(d)) {// 强转
                        valid--;
                    }
                    window.put(d, window.get(d) - 1);
                }
            }
        }
        return minLen == Integer.MAX_VALUE ? "" : s.substring(start, start + minLen);
    }
}

lee78 子集

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]

solution1 回溯

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        backtrack(0, nums, res, new ArrayList<Integer>());
        return res;

    }

    private void backtrack(int i, int[] nums, List<List<Integer>> res, ArrayList<Integer> tmp) {
        res.add(new ArrayList<>(tmp));
        for (int j = i; j < nums.length; j++) {
            tmp.add(nums[j]);
            backtrack(j + 1, nums, res, tmp);
            tmp.remove(tmp.size() - 1);
        }
    }
}

solution2 迭代

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        res.add(new ArrayList<>());
        for (int i = 0; i < nums.length; i++) {
           int all = res.size();
            for (int j = 0; j < all; j++) {
                List<Integer> tmp = new ArrayList<>(res.get(j));
                tmp.add(nums[i]);
                res.add(tmp);
            }
        }
        return res;
    }
}

类lee90 子集2

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

输入: [1,2,2]
输出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]

class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>> res=new ArrayList<>();
        if(nums==null || nums.length==0 ) return res;
        Arrays.sort(nums);
        helper(nums, res, new ArrayList<>(), 0);
        return res;
    }
    private void helper(int[] nums, List<List<Integer>> res, List<Integer> list,int start){
        res.add(new ArrayList<>(list));
        for(int i=start;i<nums.length;i++){
            if(i>start && nums[i] ==nums[i-1]) continue;
            list.add(nums[i]);
            helper(nums, res, list, i+1);
            list.remove(list.size()-1);
        }
    }
}

lee79 单词搜索

给定一个二维网格和一个单词,找出该单词是否存在于网格中。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

示例:
board =
[
[‘A’,‘B’,‘C’,‘E’],
[‘S’,‘F’,‘C’,‘S’],
[‘A’,‘D’,‘E’,‘E’]
]
给定 word = “ABCCED”, 返回 true
给定 word = “SEE”, 返回 true
给定 word = “ABCB”, 返回 false

提示:
board 和 word 中只包含大写和小写英文字母。
1 <= board.length <= 200
1 <= board[i].length <= 200
1 <= word.length <= 10^3

class Solution {
    public boolean exist(char[][] board, String word) {
        char[] s=word.toCharArray();
        int[][] flag=new int[board.length][board[0].length];
        for(int i=0;i<board.length;i++){
            for(int j=0;j<board[0].length;j++){
                if(board[i][j]==s[0]){
                    if( existHelper(board, 0, s, i, j, flag ) ){//必须有个判断,因为不止搜到的第一个为开头遍历
                        return true;
                    }
                }
                
            }
        }
        return false;
    }

    public boolean existHelper(char[][] board, int index,char[] s ,int startx, int starty, int[][] flag){
        if(index==s.length) return true;
        if( startx<0|| startx>=board.length || starty<0 || starty>=board[0].length || flag[startx][starty]==1 ||board[startx][starty]!= s[index]){
            return false;
        }

        flag[startx][starty]=1;

        if( 
            existHelper(board,index+1,s, startx+1,starty,flag)||
            existHelper(board,index+1,s, startx,starty+1,flag)||
            existHelper(board,index+1,s, startx-1,starty,flag)||
            existHelper(board,index+1,s, startx,starty-1,flag)
        ){
            return true;
        }
        flag[startx][starty]=0;
        return false;
    }
}

lee84 柱状图中的最大矩形 单调栈

class Solution {
    public int largestRectangleArea(int[] heights) {
        // 这里为了代码简便,在柱体数组的头和尾加了两个高度为 0 的柱体。
        int[] tmp = new int[heights.length + 2];
        System.arraycopy(heights, 0, tmp, 1, heights.length); 
        
        Deque<Integer> stack = new ArrayDeque<>();
        int area = 0;
        for (int i = 0; i < tmp.length; i++) {
            // 对栈中柱体来说,栈中的下一个柱体就是其「左边第一个小于自身的柱体」;
            // 若当前柱体 i 的高度小于栈顶柱体的高度,说明 i 是栈顶柱体的「右边第一个小于栈顶柱体的柱体」。
            // 因此以栈顶柱体为高的矩形的左右宽度边界就确定了,可以计算面积🌶️ ~
            while (!stack.isEmpty() && tmp[i] < tmp[stack.peek()]) {
                int h = tmp[stack.pop()];
                area = Math.max(area, (i - stack.peek() - 1) * h);   
            }
            stack.push(i);
        }

        return area;
    }
}

需要考虑两种特殊的情况:

弹栈的时候,栈为空;
遍历完成以后,栈中还有元素;
为此可以我们可以在输入数组的两端加上两个高度为 0 (或者是 0.5,只要比 1 严格小都行)的柱形,可以回避上面这两种分类讨论。

这两个站在两边的柱形有一个很形象的名词,叫做哨兵(Sentinel)。

有了这两个柱形:

左边的柱形(第 1 个柱形)由于它一定比输入数组里任何一个元素小,它肯定不会出栈,因此栈一定不会为空;

右边的柱形(第 2 个柱形)也正是因为它一定比输入数组里任何一个元素小,它会让所有输入数组里的元素出栈(第 1 个哨兵元素除外)。

类lee42 接雨水

public class Solution {
    public int trap(int[] height) {
        if (height == null) {
            return 0;
        }
        Stack<Integer> stack = new Stack<>();
        int ans = 0;
        for (int i = 0; i < height.length; i++) {
            while(!stack.isEmpty() && height[stack.peek()] < height[i]) {
                int curIdx = stack.pop();
                // 如果栈顶元素一直相等,那么全都pop出去,只留第一个。
                while (!stack.isEmpty() && height[stack.peek()] == height[curIdx]) {
                    stack.pop();
                }
                if (!stack.isEmpty()) {
                    int stackTop = stack.peek();
                    // stackTop此时指向的是此次接住的雨水的左边界的位置。右边界是当前的柱体,即i。
                    // Math.min(height[stackTop], height[i]) 是左右柱子高度的min,减去height[curIdx]就是雨水的高度。
                    // i - stackTop - 1 是雨水的宽度。
                    ans += (Math.min(height[stackTop], height[i]) - height[curIdx]) * (i - stackTop - 1);
                }
            }
            stack.add(i);
        }
        return ans;
    }
}

类 lee85

给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。

示例:

输入:
[
[“1”,“0”,“1”,“0”,“0”],
[“1”,“0”,“1”,“1”,“1”],
[“1”,“1”,“1”,“1”,“1”],
[“1”,“0”,“0”,“1”,“0”]
]
输出: 6

class Solution {
    // 单调栈, 84题
    public int largestRectangleArea(int[] heights) {
        Stack<Integer> stack = new Stack<>();
        int res = 0;        int[] copy = new int[heights.length + 2];
        for (int i = 1; i < heights.length + 1; i++) {
            copy[i] = heights[i - 1];
        }
        for (int i = 0; i < copy.length; i++) {
            //System.out.println(stack.toString());
            while (!stack.isEmpty() && copy[stack.peek()] > copy[i]) {
                int idx = stack.pop();
                res = Math.max(res, (i - stack.peek() - 1) * copy[idx]);
            }
            stack.push(i);
        }
        return res;
    }

    public int maximalRectangle(char[][] matrix) {
        if (matrix.length == 0) {
            return 0;
        }
        int res = 0;
        // dp[]数组存储84题中的柱状图高度(矩阵每列)
        int[] dp = new int[matrix[0].length];
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                if (matrix[i][j] == '1') {
                    dp[j] += 1;
                } else {
                    dp[j] = 0;
                }
            }
            res = Math.max(res, largestRectangleArea(dp));
        }
        return res;
    }
}


lee96 不同的二叉搜索树

给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种?

示例:

输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:

1 3 3 2 1
\ / / / \
3 2 1 1 3 2
/ / \
2 1 2 3

思路
标签:动态规划
假设n个节点存在二叉排序树的个数是G(n),令f(i)为以i为根的二叉搜索树的个数,则
G(n) = f(1) + f(2) + f(3) + f(4) + … + f(n)G(n)=f(1)+f(2)+f(3)+f(4)+…+f(n)

当i为根节点时,其左子树节点个数为i-1个,右子树节点为n-i,则
f(i) = G(i-1)*G(n-i)f(i)=G(i−1)∗G(n−i)

综合两个公式可以得到 卡特兰数 公式
G(n) = G(0)G(n-1)+G(1)(n-2)+…+G(n-1)*G(0)G(n)=G(0)∗G(n−1)+G(1)∗(n−2)+…+G(n−1)∗G(0)

class Solution {
    public int numTrees(int n) {
        int[] dp = new int[n+1];
        dp[0] = 1;
        dp[1] = 1;
        
        for(int i = 2; i < n + 1; i++)
            for(int j = 1; j < i + 1; j++) 
                dp[i] += dp[j-1] * dp[i-j];
        
        return dp[n];
    }
}


lee101 对称二叉树

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null) return true;
        return helper(root,root);
        
    }
    public boolean helper(TreeNode left,TreeNode right){
        if(left == null && right == null) return true;
        if(left == null || right == null) return false;
        if(left.val != right.val) return false;
        return helper(left.left,right.right) && helper(left.right,right.left);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值