删除排序数组中的重复项Ⅱ
双指针
class Solution:def removeDuplicates(self, nums: List[int]) -> int:memoryIndex = index = 0memory = -9999max = 1while index < len(nums):if memory == nums[index]:index += 1continuememory = nums[index]while index < len(nums) - 1 and nums[index + 1] == memory and max > 0:nums[memoryIndex] = nums[index]memoryIndex += 1index += 1max -= 1max = 1nums[memoryIndex] = nums[index]index += 1memoryIndex += 1return memoryIndex
大佬思路
class Solution {public int removeDuplicates(int[] nums) {int i = 0;for (int n : nums) {# 因为限制重复最多为2项 所以在 i>=2 之后 如果 n > nums[i-2] 说明重复项不大于2 否则则多于2if (i < 2 || n > nums[i-2]) nums[i++] = n;}return i;}}
搜索旋转排序数组Ⅱ**
二分查找

class Solution:def search(self, nums: List[int], target: int) -> bool:l = 0r = len(nums) - 1while l<=r:mid = (l+r) // 2if nums[mid] == target:return Trueif nums[mid] == nums[l]: # l和mid重复,l加一l += 1elif nums[mid] == nums[r]: # mid和r重复,r减一r -= 1elif nums[mid] > nums[l]: # l到mid是有序的,判断target是否在其中if nums[l] <= target < nums[mid]: # target在其中,选择l到mid这段r = mid - 1else: # target不在其中,扔掉l到mid这段l = mid + 1elif nums[mid] < nums[r]: # mid到r是有序的,判断target是否在其中if nums[mid] < target <= nums[r]:l = mid + 1else:r = mid - 1return False
删除排序链表中的重复元素
双指针+头结点
class Solution:def deleteDuplicates(self, head: ListNode) -> ListNode:if not head or not head.next: return headheadNode = ListNode(-999)headNode.next = headleft = headNoderight = headNode.nextwhile right != None:if right.next and right.val == right.next.val:temp = right.valleft.next = rightleft = left.nextwhile right != None and right.val == temp:right = right.nextelse:left.next = rightleft = left.nextright = right.nextleft.next = rightreturn headNode.next
删除排序链表中的重复元素Ⅱ*
双指针+头结点
class Solution:def deleteDuplicates(self, head: ListNode) -> ListNode:if head == None or head.next == None:return headdummy = ListNode(-1000)dummy.next = headslow = dummyfast = dummy.nextwhile fast:if fast.next and fast.next.val == fast.val:tmp = fast.valwhile fast and tmp == fast.val:fast = fast.nextelse:slow.next = fastslow = fastfast = fast.nextslow.next = fastreturn dummy.next
柱状图中最大的矩形*
暴力求解



class Solution:def largestRectangleArea(self, heights: List[int]) -> int:res = 0for i in range(len(heights)):left = icur = heights[i]while left > 0 and heights[left - 1] >= cur:left -= 1right = iwhile right < len(heights) - 1 and heights[right + 1] >= cur:right += 1mid = (right - left + 1) * curres = mid if mid > res else resreturn res
空间换时间(单调栈)
class Solution:def largestRectangleArea(self, heights: List[int]) -> int:size = len(heights)res = 0stack = []for i in range(size):while len(stack) > 0 and heights[i] < heights[stack[-1]]:cur_height = heights[stack.pop()]while len(stack) > 0 and cur_height == heights[stack[-1]]:stack.pop()if len(stack) > 0:cur_width = i - stack[-1] - 1else:cur_width = ires = max(res, cur_height * cur_width)stack.append(i)while len(stack) > 0 is not None:cur_height = heights[stack.pop()]while len(stack) > 0 and cur_height == heights[stack[-1]]:stack.pop()if len(stack) > 0:cur_width = size - stack[-1] - 1else:cur_width = sizeres = max(res, cur_height * cur_width)return res
最大矩形
暴力搜索:列优先、按行遍历
class Solution:def maximalRectangle(self, matrix: List[List[str]]) -> int:if not matrix: return 0rows , cols = len(matrix) , len(matrix[0])res = 0for i in range(rows):for j in range(cols):if matrix[i][j] == '1':row = iwhile row < rows and matrix[row][j] == '1': # 按列查找row += 1col = jwhile col < cols and matrix[i][col] == '1': # 按行查找col += 1x , y = i , jy_max = colmid = 0while x < row:while y < y_max:if matrix[x][y] != '1':y_max = ybreaky += 1x += 1mid = max(mid , (x - i) * (y - j))y = j# print("i:{} j:{} col:{} row:{} x:{} y:{} mid:{}".format(i,j,col,row,x,y,mid))res = max(res,row-i,col-j,mid)return res
动态规划 - 使用柱状图的优化暴力方法



class Solution:def maximalRectangle(self, matrix: List[List[str]]) -> int:maxarea = 0dp = [[0] * len(matrix[0]) for _ in range(len(matrix))]for i in range(len(matrix)):for j in range(len(matrix[0])):if matrix[i][j] == '0': continue# compute the maximum width and update dp with itwidth = dp[i][j] = dp[i][j-1] + 1 if j else 1# compute the maximum area rectangle with a lower right corner at [i, j]for k in range(i, -1, -1):width = min(width, dp[k][j])maxarea = max(maxarea, width * (i-k+1))return maxarea

动态规划





class Solution:def maximalRectangle(self, matrix: List[List[str]]) -> int:if not matrix: return 0m = len(matrix)n = len(matrix[0])left = [0] * n # initialize left as the leftmost boundary possibleright = [n] * n # initialize right as the rightmost boundary possibleheight = [0] * nmaxarea = 0for i in range(m):cur_left, cur_right = 0, n# update heightfor j in range(n):if matrix[i][j] == '1': height[j] += 1else: height[j] = 0# update leftfor j in range(n):if matrix[i][j] == '1': left[j] = max(left[j], cur_left)else:left[j] = 0cur_left = j + 1# update rightfor j in range(n-1, -1, -1):if matrix[i][j] == '1': right[j] = min(right[j], cur_right)else:right[j] = ncur_right = j# update the areafor j in range(n):maxarea = max(maxarea, height[j] * (right[j] - left[j]))return maxarea
分隔链表
以空间换时间
class Solution:def partition(self, head: ListNode, x: int) -> ListNode:headLeft , headRight = ListNode(-1) , ListNode(-2)index = headleftIndex , rightIndex = headLeft , headRightwhile index != None:if index.val < x:leftIndex.next = indexleftIndex = leftIndex.nextelse:rightIndex.next = indexrightIndex = rightIndex.nextindex = index.nextrightIndex.next = NoneleftIndex.next = headRight.nextreturn headLeft.next
扰乱字符串**
区间DP

5.最长回文子串
516. 最长回文子序列
312. 戳气球
1246. 删除回文子数组(这个题微软面试问的很多)
递归实现
class Solution {public boolean isScramble(String s1, String s2) {// 长度不等,必定不能变换if (s1.length() != s2.length()) {return false;}// 长度相等,先特判下if (s1.equals(s2)) {return true;}// 看一下字符个数是否一致,不同直接return falseint n = s1.length();HashMap<Character, Integer> map = new HashMap<>();for (int i = 0; i < n; i++) {char c1 = s1.charAt(i);char c2 = s2.charAt(i);map.put(c1, map.getOrDefault(c1, 0) + 1);map.put(c2, map.getOrDefault(c2, 0) - 1);}// out.println(map.size());for (Character key : map.keySet()) {// out.println("key:"+key + " value:"+map.get(key));if (map.get(key) != 0) {return false;}}// 相同的话,开始判断判断,满足一个就能 return truefor (int i = 1; i < n; i++) {boolean flag =// S1 -> T1,S2 -> T2 S:[S1,S2]=[[0,i),[i,n)] T:[T1,T2]=[0,i),[i,n)](isScramble(s1.substring(0, i), s2.substring(0, i)) && isScramble(s1.substring(i), s2.substring(i))) ||// S1 -> T2,S2 -> T1(isScramble(s1.substring(0, i), s2.substring(n - i)) && isScramble(s1.substring(i), s2.substring(0, s2.length() - i)));if (flag) {return true;}}return false;}}
非递归实现
class Solution {public boolean isScramble(String s1, String s2) {char[] chs1 = s1.toCharArray();char[] chs2 = s2.toCharArray();int n = s1.length();int m = s2.length();if (n != m) {return false;}boolean[][][] dp = new boolean[n][n][n + 1];// 初始化单个字符的情况for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {dp[i][j][1] = chs1[i] == chs2[j];}}// 枚举区间长度 2~nfor (int len = 2; len <= n; len++) {// 枚举 S 中的起点位置for (int i = 0; i <= n - len; i++) {// 枚举 T 中的起点位置for (int j = 0; j <= n - len; j++) {// 枚举划分位置for (int k = 1; k <= len - 1; k++) {// 第一种情况:S1 -> T1, S2 -> T2if (dp[i][j][k] && dp[i + k][j + k][len - k]) {dp[i][j][len] = true;break;}// 第二种情况:S1 -> T2, S2 -> T1// S1 起点 i,T2 起点 j + 前面那段长度 len-k ,S2 起点 i + 前面长度kif (dp[i][j + len - k][k] && dp[i + k][j][len - k]) {dp[i][j][len] = true;break;}}}}}return dp[0][0][n];}}
合并两个有序数组
合并后排序

class Solution {public void merge(int[] nums1, int m, int[] nums2, int n) {System.arraycopy(nums2, 0, nums1, m, n);Arrays.sort(nums1);}}//arraycopy 用法://src表示源数组//srcPos表示源数组中拷贝元素的起始位置。//dest表示目标数组//destPos表示拷贝到目标数组的起始位置//length表示拷贝元素的个数
双指针/从前往后

class Solution {public void merge(int[] nums1, int m, int[] nums2, int n) {// Make a copy of nums1.int [] nums1_copy = new int[m];System.arraycopy(nums1, 0, nums1_copy, 0, m);// Two get pointers for nums1_copy and nums2.int p1 = 0;int p2 = 0;// Set pointer for nums1int p = 0;// Compare elements from nums1_copy and nums2// and add the smallest one into nums1.while ((p1 < m) && (p2 < n))nums1[p++] = (nums1_copy[p1] < nums2[p2]) ? nums1_copy[p1++] : nums2[p2++];// if there are still elements to addif (p1 < m)System.arraycopy(nums1_copy, p1, nums1, p1 + p2, m + n - p1 - p2);if (p2 < n)System.arraycopy(nums2, p2, nums1, p1 + p2, m + n - p1 - p2);}}
双指针/从后往前 **


class Solution {public void merge(int[] nums1, int m, int[] nums2, int n) {// two get pointers for nums1 and nums2int p1 = m - 1;int p2 = n - 1;// set pointer for nums1int p = m + n - 1;// while there are still elements to comparewhile ((p1 >= 0) && (p2 >= 0))// compare two elements from nums1 and nums2// and add the largest one in nums1nums1[p--] = (nums1[p1] < nums2[p2]) ? nums2[p2--] : nums1[p1--];// add missing elements from nums2System.arraycopy(nums2, 0, nums1, 0, p2 + 1);}}
格雷编码**
动态规划(真的妙啊)



class Solution {public List<Integer> grayCode(int n) {List<Integer> gray = new ArrayList<Integer>();gray.add(0); //初始化 n = 0 的解for (int i = 0; i < n; i++) {int add = 1 << i; //要加的数//倒序遍历,并且加上一个值添加到结果中for (int j = gray.size() - 1; j >= 0; j--) {gray.add(gray.get(j) + add);}}return gray;}}
直接推导

public List<Integer> grayCode2(int n) {List<Integer> gray = new ArrayList<Integer>();gray.add(0); //初始化第零项for (int i = 1; i < 1 << n; i++) {//得到上一个的值int previous = gray.get(i - 1);//同第一项的情况if (i % 2 == 1) {previous ^= 1; //和 0000001 做异或,使得最右边一位取反gray.add(previous);//同第二项的情况} else {int temp = previous;//寻找右边起第第一个为 1 的位元for (int j = 0; j < n; j++) {if ((temp & 1) == 1) {//和 00001000000 类似这样的数做异或,使得相应位取反previous = previous ^ (1 << (j + 1));gray.add(previous);break;}temp = temp >> 1;}}}return gray;}
公式法

public List<Integer> grayCode(int n) {List<Integer> gray = new ArrayList<Integer>();for(int binary = 0;binary < 1 << n; binary++){gray.add(binary ^ binary >> 1);}return gray;}
子集Ⅱ
回溯法
这个比较好改,我们只需要判断当前数字和上一个数字是否相同,相同的话跳过即可。当然,要把数字首先进行排序。
class Solution {private void getAns(int[] nums, int start, ArrayList<Integer> temp, List<List<Integer>> ans) {ans.add(new ArrayList<>(temp));for (int i = start; i < nums.length; i++) {if (i > start && nums[i] == nums[i - 1]) {continue;}temp.add(nums[i]);getAns(nums, i + 1, temp, ans);temp.remove(temp.size() - 1);}}public List<List<Integer>> subsetsWithDup(int[] nums) {List<List<Integer>> ans =new ArrayList<>();Arrays.sort(nums);getAns(nums,0,new ArrayList<>(),ans);return ans;}}
公式转换
非递归 + 栈
package com.company;import java.util.Stack;public class TString {static public String merge(String[] nums){StringBuilder pushNum = new StringBuilder();for (String num : nums) {pushNum.append("+").append(num);}System.out.println("merge - pushNum: "+pushNum.toString().substring(1));return pushNum.toString().substring(1);}static public String change(String s){if(s.equals("1")){return "1";}return "Y["+s.substring(1)+"]";}public static String transformation(String target){char[] str = target.toCharArray();Stack<Character> symbolStack = new Stack<>();Stack<String> numStack = new Stack<>();StringBuilder midNum = new StringBuilder();for (int i = 0; i < str.length; i++) {switch (str[i]){case '(':symbolStack.push('(');break;case ')':if (midNum.length()>0){numStack.push(change(midNum.toString()));midNum = new StringBuilder();}StringBuilder nums = new StringBuilder();while (symbolStack.peek()!='('){nums.insert(0, symbolStack.pop()+numStack.pop());}nums.insert(0,numStack.pop()); // 获得每个()内的公式 :(x106+x85+x42+x21) -> Y[106]+Y[85]+Y[42]+Y[21]System.out.println(nums.toString());numStack.push(nums.toString());break;case '+':symbolStack.push('+');if (midNum.length()>0){numStack.push(change(midNum.toString())); //将 x116 -> Y[116]midNum = new StringBuilder();}break;case '*':String[] midNums = numStack.pop().split("\\+");int index = i+1;StringBuilder e = new StringBuilder();while (index < str.length && str[index] != '(' && str[index] != ')' && str[index] != '+'){e.append(str[index]);index ++;}i = index - 1;String midS = "";midS = "[" +e.toString().substring(1)+"]"; // 默认只有 *x11 这种情况 读入一个x116 -> [116]for (int j = 0; j < midNums.length; j++) {if (midNums[j].equals("1")){midNums[j] = "Y"+midS;}else {midNums[j] += midS;}}numStack.push(merge(midNums));break;default:midNum.append(str[i]);break;}}return numStack.pop();}public static void main(String[] args) {System.out.println(transformation("((x106+x85+x42+x21)*x116+(x95+x85+x52+x31+1)*x106+x13)*x95"));}}
解码方法**
Date:2020-11-12 周四

我还是想吐槽下,这道题就没说明白。比如”01”算不算。md
DFS(超时)

public int numDecodings(String s) {return getAns(s, 0);}private int getAns(String s, int start) {//划分到了最后返回 1if (start == s.length()) {return 1;}//开头是 0,0 不对应任何字母,直接返回 0if (s.charAt(start) == '0') {return 0;}//得到第一种的划分的解码方式int ans1 = getAns(s, start + 1);int ans2 = 0;//判断前两个数字是不是小于等于 26 的if (start < s.length() - 1) {int ten = (s.charAt(start) - '0') * 10;int one = s.charAt(start + 1) - '0';if (ten + one <= 26) {ans2 = getAns(s, start + 2);}}return ans1 + ans2;}
上边的递归中,走完左子树,再走右子树会把一些已经算过的结果重新算,所以我们可以用 memoization 技术,就是算出一个结果很就保存,第二次算这个的时候直接拿出来就可以了。
public int numDecodings(String s) {HashMap<Integer, Integer> memoization = new HashMap<>();return getAns(s, 0, memoization);}private int getAns(String s, int start, HashMap<Integer, Integer> memoization) {if (start == s.length()) {return 1;}if (s.charAt(start) == '0') {return 0;}//判断之前是否计算过int m = memoization.getOrDefault(start, -1);if (m != -1) {return m;}int ans1 = getAns(s, start + 1, memoization);int ans2 = 0;if (start < s.length() - 1) {int ten = (s.charAt(start) - '0') * 10;int one = s.charAt(start + 1) - '0';if (ten + one <= 26) {ans2 = getAns(s, start + 2, memoization);}}//将结果保存memoization.put(start, ans1 + ans2);return ans1 + ans2;}
动态规划(反向易懂)

public int numDecodings(String s) {int len = s.length();int[] dp = new int[len + 1];dp[len] = 1; //将递归法的结束条件初始化为 1//最后一个数字不等于 0 就初始化为 1if (s.charAt(len - 1) != '0') {dp[len - 1] = 1;}for (int i = len - 2; i >= 0; i--) {//当前数字时 0 ,直接跳过,0 不代表任何字母if (s.charAt(i) == '0') {continue;}int ans1 = dp[i + 1];//判断两个字母组成的数字是否小于等于 26int ans2 = 0;int ten = (s.charAt(i) - '0') * 10;int one = s.charAt(i + 1) - '0';if (ten + one <= 26) {ans2 = dp[i + 2];}dp[i] = ans1 + ans2;}return dp[0];}
反转链表
头插法(一次遍历)
class Solution {public ListNode reverseBetween(ListNode head, int m, int n) {if (m==n) return head;ListNode headNode = new ListNode(-1) , right ,left , last; //left指向第m个节点的前方 right用来遍历headNode.next = head;left = headNode;int index = 0;while (left!=null && index++ < m-1){left = left.next;}right = left.next;last = left.next;index--;while (right!=null && index++ < n){ListNode mid = right.next;right.next=left.next;left.next = right;right = mid;}last.next = right;return headNode.next;}}
复原IP地址**
递归

class Solution {static final int SEG_COUNT = 4;List<String> ans = new ArrayList<String>();int[] segments = new int[SEG_COUNT];public List<String> restoreIpAddresses(String s) {segments = new int[SEG_COUNT];dfs(s, 0, 0);return ans;}public void dfs(String s, int segId, int segStart) {// 如果找到了 4 段 IP 地址并且遍历完了字符串,那么就是一种答案if (segId == SEG_COUNT) {if (segStart == s.length()) {StringBuffer ipAddr = new StringBuffer();for (int i = 0; i < SEG_COUNT; ++i) {ipAddr.append(segments[i]);if (i != SEG_COUNT - 1) {ipAddr.append('.');}}ans.add(ipAddr.toString());}return;}// 如果还没有找到 4 段 IP 地址就已经遍历完了字符串,那么提前回溯if (segStart == s.length()) {return;}// 由于不能有前导零,如果当前数字为 0,那么这一段 IP 地址只能为 0if (s.charAt(segStart) == '0') {segments[segId] = 0;dfs(s, segId + 1, segStart + 1);}// 一般情况,枚举每一种可能性并递归int addr = 0;for (int segEnd = segStart; segEnd < s.length(); ++segEnd) {addr = addr * 10 + (s.charAt(segEnd) - '0');if (addr > 0 && addr <= 0xFF) {segments[segId] = addr;dfs(s, segId + 1, segEnd + 1);} else {break;}}}}
二叉树的中序遍历
递归
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/class Solution {List<Integer> ans = new ArrayList<>();public void dfs(TreeNode node){if (node!=null){dfs(node.left);ans.add(node.val);dfs(node.right);}}public List<Integer> inorderTraversal(TreeNode root) {dfs(root);return ans;}}
不同的二叉搜索树**
动态规划





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

class Solution {public int numTrees(int n) {// 提示:我们在这里需要用 long 类型防止计算过程中的溢出long C = 1;for (int i = 0; i < n; ++i) {C = C * 2 * (2 * i + 1) / (i + 2);}return (int) C;}}
不同的二叉搜索树Ⅱ*
Date:2020-11-17 周二
给定一个整数 n,生成所有由 1 … n 为节点所组成的 二叉搜索树 。
递归

class Solution {public List<TreeNode> generateTrees(int n) {if (n == 0) {return new LinkedList<TreeNode>();}return generateTrees(1, n);}public List<TreeNode> generateTrees(int start, int end) {List<TreeNode> allTrees = new LinkedList<TreeNode>();if (start > end) {allTrees.add(null);return allTrees;}// 枚举可行根节点for (int i = start; i <= end; i++) {// 获得所有可行的左子树集合List<TreeNode> leftTrees = generateTrees(start, i - 1);// 获得所有可行的右子树集合List<TreeNode> rightTrees = generateTrees(i + 1, end);// 从左子树集合中选出一棵左子树,从右子树集合中选出一棵右子树,拼接到根节点上for (TreeNode left : leftTrees) {for (TreeNode right : rightTrees) {TreeNode currTree = new TreeNode(i);currTree.left = left;currTree.right = right;allTrees.add(currTree);}}}return allTrees;}}
构建一棵二叉树的递归
public TreeNode createBinaryTree(int n){return helper(1, n);}public TreeNode helper(int start, int end){if(start > end)return null;// 这里可以选择从start到end的任何一个值做为根结点,// 这里选择它们的中点,实际上,这样构建出来的是一颗平衡二叉搜索树int val = (start + end) / 2;TreeNode root = new TreeNode(val);root.left = helper(start, val - 1);root.right = helper(val + 1, end);return root;}
交错字符串
暴力递归(超时)
class Solution {public boolean dp(String s1,int index_1,String s2,int index_2,String s3,int index){if (index_1 >= s1.length() && index_2 >= s2.length() && index >= s3.length())return true;boolean l = false , r = false;if (index < s3.length() && index_1 < s1.length() && s3.charAt(index) == s1.charAt(index_1)){l = dp(s1,index_1 + 1,s2,index_2,s3,index+1);}if (index < s3.length() && index_2 < s2.length() && s3.charAt(index) == s2.charAt(index_2)){r = dp(s1,index_1,s2,index_2 + 1,s3,index+1);}return l || r;}public boolean isInterleave(String s1, String s2, String s3) {return dp(s1,0,s2,0,s3,0);}}
动态规划

class Solution {public boolean isInterleave(String s1, String s2, String s3) {int n = s1.length(), m = s2.length(), t = s3.length();if (n + m != t) {return false;}boolean[][] f = new boolean[n + 1][m + 1];f[0][0] = true;for (int i = 0; i <= n; ++i) {for (int j = 0; j <= m; ++j) {int p = i + j - 1;if (i > 0) {f[i][j] = f[i][j] || (f[i - 1][j] && s1.charAt(i - 1) == s3.charAt(p));}if (j > 0) {f[i][j] = f[i][j] || (f[i][j - 1] && s2.charAt(j - 1) == s3.charAt(p));}}}return f[n][m];}}
不难看出这个实现的时间复杂度和空间复杂度都是 O(nm)O(nm)。
滚动数组

class Solution {public boolean isInterleave(String s1, String s2, String s3) {int n = s1.length(), m = s2.length(), t = s3.length();if (n + m != t) {return false;}boolean[] f = new boolean[m + 1];f[0] = true;for (int i = 0; i <= n; ++i) {for (int j = 0; j <= m; ++j) {int p = i + j - 1;if (i > 0) {f[j] = f[j] && s1.charAt(i - 1) == s3.charAt(p);}if (j > 0) {f[j] = f[j] || (f[j - 1] && s2.charAt(j - 1) == s3.charAt(p));}}}return f[m];}}
验证二叉搜索树
中序遍历+判断
如果是一棵二叉搜索树的话,那么他的中序遍历是一个有序的集合,那么就可以先进行先序排列,然后在根据遍历出来的数组进行判断是否是一棵二叉搜索树。
class Solution {List<Integer> rootValList = new ArrayList<>();public void lRr(TreeNode root){if (root.left!=null)lRr(root.left);rootValList.add(root.val);if (root.right!=null)lRr(root.right);}public boolean isValidBST(TreeNode root) {if (root==null) return true;lRr(root);for (int i = 1; i < rootValList.size(); i++) {if (rootValList.get(i-1)>=rootValList.get(i))return false;}return true;}}
官方中序
class Solution {public boolean isValidBST(TreeNode root) {Deque<TreeNode> stack = new LinkedList<TreeNode>();double inorder = -Double.MAX_VALUE;while (!stack.isEmpty() || root != null) {while (root != null) {stack.push(root);root = root.left;}root = stack.pop();// 如果中序遍历得到的节点的值小于等于前一个 inorder,说明不是二叉搜索树if (root.val <= inorder) {return false;}inorder = root.val;root = root.right;}return true;}}
递归
最开始做递归没做出来是因为没有设置上下界,导致出错。
class Solution {public boolean isValidBST(TreeNode root) {return helper(root, null, null);}public boolean helper(TreeNode node, Integer lower, Integer upper) {if (node == null) {return true;}int val = node.val;if (lower != null && val <= lower) {return false;}if (upper != null && val >= upper) {return false;}if (!helper(node.right, val, upper)) {return false;}if (!helper(node.left, lower, val)) {return false;}return true;}}
恢复二叉搜索树
显式中序遍历


class Solution {public void recoverTree(TreeNode root) {List<Integer> nums = new ArrayList<Integer>();inorder(root, nums);int[] swapped = findTwoSwapped(nums);recover(root, 2, swapped[0], swapped[1]);}public void inorder(TreeNode root, List<Integer> nums) {if (root == null) {return;}inorder(root.left, nums);nums.add(root.val);inorder(root.right, nums);}public int[] findTwoSwapped(List<Integer> nums) {int n = nums.size();int x = -1, y = -1;for (int i = 0; i < n - 1; ++i) {if (nums.get(i + 1) < nums.get(i)) {y = nums.get(i + 1);if (x == -1) {x = nums.get(i);} else {break;}}}return new int[]{x, y};}public void recover(TreeNode root, int count, int x, int y) {if (root != null) {if (root.val == x || root.val == y) {root.val = root.val == x ? y : x;if (--count == 0) {return;}}recover(root.right, count, x, y);recover(root.left, count, x, y);}}}
隐式中序排序




class Solution {public void recoverTree(TreeNode root) {Deque<TreeNode> stack = new ArrayDeque<TreeNode>();TreeNode x = null, y = null, pred = null;while (!stack.isEmpty() || root != null) {while (root != null) {stack.push(root);root = root.left;}root = stack.pop();if (pred != null && root.val < pred.val) {y = root;if (x == null) {x = pred;} else {break;}}pred = root;root = root.right;}swap(x, y);}public void swap(TreeNode x, TreeNode y) {int tmp = x.val;x.val = y.val;y.val = tmp;}}
Morris 中序遍历


class Solution {public void recoverTree(TreeNode root) {TreeNode x = null, y = null, pred = null, predecessor = null;while (root != null) {if (root.left != null) {// predecessor 节点就是当前 root 节点向左走一步,然后一直向右走至无法走为止predecessor = root.left;while (predecessor.right != null && predecessor.right != root) {predecessor = predecessor.right;}// 让 predecessor 的右指针指向 root,继续遍历左子树if (predecessor.right == null) {predecessor.right = root;root = root.left;}// 说明左子树已经访问完了,我们需要断开链接else {if (pred != null && root.val < pred.val) {y = root;if (x == null) {x = pred;}}pred = root;predecessor.right = null;root = root.right;}}// 如果没有左孩子,则直接访问右孩子else {if (pred != null && root.val < pred.val) {y = root;if (x == null) {x = pred;}}pred = root;root = root.right;}}swap(x, y);}public void swap(TreeNode x, TreeNode y) {int tmp = x.val;x.val = y.val;y.val = tmp;}}
相同的树
递归遍历判断
class Solution {public boolean isSameTree(TreeNode p, TreeNode q) {if (p==null || q==null)return q==p;if (p.val!=q.val)return false;boolean l = true , r = true;if ((p.left==null && q.left !=null)||(p.left!=null && q.left ==null)) return false;if (p.left!=null){l = isSameTree(p.left,q.left);}if ((p.right==null && q.right !=null)||(p.right!=null && q.right ==null)) return false;if (p.right!=null){r = isSameTree(p.right,q.right);}return l && r;}}
对称二叉树
递归
class Solution {public boolean judgeLR(TreeNode left , TreeNode right){if (left.val!=right.val) return false;boolean j1 = true , j2 = true;if ((left.right == null && right.left != null) || left.right != null && right.left == null) return false;if (left.right!=null){j1 = judgeLR(left.right,right.left);}if ((left.left == null && right.right != null) || left.left != null && right.right == null) return false;if (left.left!=null){j2 = judgeLR(left.left,right.right);}return j1 && j2;}public boolean isSymmetric(TreeNode root) {if (root==null) return true;if ((root.right == null && root.left != null) || root.right != null && root.left == null) return false;if (root.left!=null) return judgeLR(root.left,root.right);else return true;}}
迭代**
首先我们引入一个队列,这是把递归程序改写成迭代程序的常用方法。初始化时我们把根节点入队两次。每次提取两个结点并比较它们的值(队列中每两个连续的结点应该是相等的,而且它们的子树互为镜像),然后将两个结点的左右子结点按相反的顺序插入队列中。当队列为空时,或者我们检测到树不对称(即从队列中取出两个不相等的连续结点)时,该算法结束。
class Solution {public boolean isSymmetric(TreeNode root) {return check(root, root);}public boolean check(TreeNode u, TreeNode v) {Queue<TreeNode> q = new LinkedList<TreeNode>();q.offer(u);q.offer(v);while (!q.isEmpty()) {u = q.poll();v = q.poll();if (u == null && v == null) {continue;}if ((u == null || v == null) || (u.val != v.val)) {return false;}q.offer(u.left);q.offer(v.right);q.offer(u.right);q.offer(v.left);}return true;}}
二叉树的层次遍历
队列实现
class Solution {public List<List<Integer>> levelOrder(TreeNode root) {Queue<TreeNode> queue = new LinkedList<>();List<List<Integer>> res = new ArrayList<>();if (root==null) return res;int nodes = 1;queue.offer(root);while (!queue.isEmpty()){TreeNode node;List<Integer> list = new ArrayList<>();int index = nodes;nodes = 0;for (int i = 0; i < index; i++) {node = queue.poll();list.add(node.val);if (node.left!=null){queue.offer(node.left);nodes++;}if (node.right!=null){queue.offer(node.right);nodes++;}}res.add(list);}return res;}}
二叉树的锯齿形层次遍历
队列实现
这道题相较上道题之间的区别就是:需要在奇数层将节点值逆序存放,也就只需要在存入节点值的时候进行一下区分即可。
class Solution {public List<List<Integer>> zigzagLevelOrder(TreeNode root) {Queue<TreeNode> queue = new LinkedList<>();List<List<Integer>> res = new ArrayList<>();if (root==null) return res;int nodes = 1;queue.offer(root);while (!queue.isEmpty()){TreeNode node;List<Integer> list = new ArrayList<>();int index = nodes;nodes = 0;for (int i = 0; i < index; i++) {node = queue.poll();if (res.size()%2!=0){ #区分奇偶层进行存储list.add(0,node.val);}else {list.add(node.val);}if (node.left!=null){queue.offer(node.left);nodes++;}if (node.right!=null){queue.offer(node.right);nodes++;}}res.add(list);}return res;}}
二叉树的最大深度
递归
class Solution {public int findDepth(TreeNode root , int height , int maxH){if (height + 1 > maxH)maxH = height + 1;int l = 0 , r = 0;if (root.left != null)l = findDepth(root.left,height+1,maxH);if (root.right != null)r = findDepth(root.right,height+1,maxH);return Math.max(Math.max(l , r),maxH);}public int maxDepth(TreeNode root) {if (root==null) return 0;return findDepth(root,0,0);}}

**














