- lee75 颜色分类 patition 三个指针
- lee76 最小覆盖字串 滑动窗口
- lee78 子集 无重复数字 回溯
- lee79 单词搜索 回溯
- lee84 柱状图中最大的矩形 单调栈
- lee85 最大矩形 (用84的方法,在矩阵中)
- lee95 二叉树中序遍历
- lee96 不同的二叉搜索树 总结规律 动态规划
- lee98 验证二叉搜索树
- 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);
}
}