二叉树
二叉树递归
相当于这个的顺序来回调换
java">class Solution {
private List<Integer> res = new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
if(root == null)return res;
inorderTraversal(root.left);
res.add(root.val);
inorderTraversal(root.right);
return res;
}
}
二叉树迭代
前序遍历
迭代法
java">public List<Integer> preOrderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
if (root == null) {
return res;
}
Deque<TreeNode> stack = new LinkedList<TreeNode>();
TreeNode node = root;
while (!stack.isEmpty() || node != null) {
while (node != null) {
res.add(node.val);
stack.push(node);
node = node.left;
}
node = stack.pop();
node = node.right;
}
return res;
}
中序遍历
迭代法
java">public List<Integer> preOrderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
if (root == null) {
return res;
}
Deque<TreeNode> stack = new LinkedList<TreeNode>();
TreeNode node = root;
while (!stack.isEmpty() || node != null) {
while (node != null) {
stack.push(node);
node = node.left;
}
node = stack.pop();
res.add(node.val);
node = node.right;
}
return res;
}
染色法
缺点是要写一个pair的类,优点是只需要更改顺序就可以使三个顺序都能写
java">class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
}
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new LinkedList<>();
Deque<Pair<Integer, TreeNode>> stack = new ArrayDeque<>();
stack.push(new Pair<>(0,root));
while(!stack.isEmpty()){
Pair<Integer,TreeNode> newPair = stack.pop();
int color = newPair.getKey();
TreeNode node = newPair.getValue();
if(node == null)continue;
if(color == 0){
stack.push(new Pair<>(0,node.right));
stack.push(new Pair<>(1,node));
stack.push(new Pair<>(0,node.left));
}else{
res.add(node.val);
}
}
return res;
}
}
Morris法
①第一个循环:是否为空
②判断左子树是否为空,是则记录+进入右子树,不是则进入左子树
③如果最右的最右为空,则链接,进入左子树。如果最右的最右为root,则断联,记录,进入右子树。
java">class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new LinkedList<>();
while(root !=null){
if(root.left == null){
res.add(root.val);
root = root.right;
}else{
//有左子树的情况
TreeNode pre = root.left;
while(pre.right != null && pre.right != root){
pre = pre.right;
}
if(pre.right == null){
pre.right = root;
root = root.left;
}else{
pre.right = null;//断开
res.add(root.val);
root = root.right;
}
}
}
return res;
}
}
后序遍历
反转法
其实就是将递归暗处的栈变成明面
java">public class PostorderTraversal {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) return result;
Stack<TreeNode> stack = new Stack<>();
Stack<Integer> output = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
output.push(node.val);
if (node.left != null) stack.push(node.left);
if (node.right != null) stack.push(node.right);
}
while (!output.isEmpty()) {
result.add(output.pop());
}
return result;
}
访问标记法(染色法)
(使用额外的标记来指示节点是否已经访问)
java">public class PostorderTraversal {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) return result;
Stack<TreeNode> stack = new Stack<>();
TreeNode prev = null;
while (!stack.isEmpty() || root != null) {
while (root != null) {
stack.push(root);
root = root.left;
}
root = stack.peek();
if (root.right == null || root.right == prev) {
result.add(root.val);
stack.pop();
prev = root;
root = null; // We have finished this node
} else {
root = root.right; // Move to the right child
}
}
return result;
}
Morris法(线性时间,常数空间)
Morris 遍历法通过在遍历过程中使用指针而避免了使用栈或递归,从而节省空间。
java">public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) return result;
TreeNode dummy = new TreeNode(0);
dummy.left = root;
TreeNode curr = dummy;
while (curr != null) {
if (curr.left == null) {
curr = curr.right;
} else {
TreeNode prev = curr.left;
while (prev.right != null && prev.right != curr) {
prev = prev.right;
}
if (prev.right == null) {
prev.right = curr;
curr = curr.left;
} else {
reverse(curr.left, prev);
TreeNode temp = prev;
while (temp != null) {
result.add(temp.val);
temp = temp.right;
}
reverse(prev, curr.left);
prev.right = null;
curr = curr.right;
}
}
}
return result;
}
private void reverse(TreeNode from, TreeNode to) {
if (from == to) return;
TreeNode x = from, y = from.right;
while (x != to) {
x.right = y.right;
y.right = x;
x = y;
y = y.right;
}
}
104. 二叉树的最大深度
解法一、递归
java">class Solution {
public int maxDepth(TreeNode root) {
if(root == null)return 0;
int leftMax = maxDepth(root.left);
int rightMax = maxDepth(root.right);
return Math.max(leftMax,rightMax) + 1;
}
}
226. 翻转二叉树
解法一、递归
java">class Solution {
public TreeNode invertTree(TreeNode root) {
if(root == null)return root;
invertTree(root.left);
invertTree(root.right);
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
return root;
}
}
101. 对称二叉树
解法一、递归
java">class Solution {
public boolean isSymmetric(TreeNode root) {
return isS(root.left,root.right);
}
private boolean isS(TreeNode left,TreeNode right){
if(left == null || right == null)return left == right;
return left.val == right.val && isS(left.left,right.right)&&isS(left.right,right.left);
}
}
解法二、迭代
因为List情况下不能add null,所以改换成Queue。不过不改也可以,只需要在null的情况下构建新节点,总之就是改换边界条件
java">class Solution {
public boolean isSymmetric(TreeNode root) {
return isS(root,root);
}
private boolean isS(TreeNode left,TreeNode right){
Queue<TreeNode> res = new LinkedList<>();
res.add(left);
res.add(right);
while (!res.isEmpty()){
TreeNode u = res.poll();
TreeNode v = res.poll();
if (u == null && v == null) {
continue;
}
if ((u == null || v == null) || (u.val != v.val)) {
return false;
}
res.offer(u.left);
res.offer(v.right);
res.offer(u.right);
res.offer(v.left);
}
return true;
}
}
543. 二叉树的直径
解法一、递归
java">class Solution {
private int res = 0;
public int diameterOfBinaryTree(TreeNode root) {
dfs(root);
return res;
}
private int dfs(TreeNode root){
if(root == null)return -1;
int l = dfs(root.left)+1;
int r = dfs(root.right)+1;
res = Math.max(res,l+r);
return Math.max(l,r);
}
}
102. 二叉树的层序遍历
解法一 层序遍历
java">class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> q = new LinkedList<>();
List<List<Integer>> res = new LinkedList<>();
q.offer(root);
while(!q.isEmpty()){
Queue<TreeNode> p = q;
q = new LinkedList<>();
List<Integer> tmp = new LinkedList<>();
while(!p.isEmpty()){
TreeNode node = p.poll();
if(node == null)continue;
q.add(node.left);
q.add(node.right);
tmp.add(node.val);
}
if(!tmp.isEmpty())res.add(tmp);
}
return res;
}
}
108. 将有序数组转换为二叉搜索树
解法一、递归
java">class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
int n = nums.length,mid = n/2;
return create(nums,0,n);
}
private TreeNode create(int[] nums,int x,int y){
if(x==y)return null;
int mid = x + (y-x)/2;
TreeNode node = new TreeNode(nums[mid]);
node.left = create(nums,x,mid);
node.right = create(nums,mid+1,y);
return node;
}
}
98. 验证二叉搜索树
解法一、递归
java">class Solution {
public boolean isValidBST(TreeNode root) {
if(root == null)return true;
return BST(root.left,Long.MIN_VALUE,root.val) && BST(root.right,root.val,Long.MAX_VALUE);
}
public boolean BST(TreeNode root,long x,long y) {
if(root == null)return true;
if(root.val <= x || root.val >= y )return false;
return BST(root.left,x,root.val) && BST(root.right,root.val,y);
}
}
解法二、中序递增
中序出来的数组一定是递增的,同时,递增数组中序构建也一定是BST
java">class Solution {
public boolean isValidBST(TreeNode root) {
List<Integer> tmp = new LinkedList<>();
while(root != null){
if(root.left == null){
tmp.add(root.val);
root = root.right;
}else{
TreeNode node = root.left;
while(node.right != null && node.right != root){
node = node.right;
}
if(node.right == null){
node.right = root;
root = root.left;
}else{
node.right = null;
tmp.add(root.val);
root = root.right;
}
}
}
int n = tmp.size(),num = tmp.get(0);
for(int i = 1;i < n;i++){
if(tmp.get(i) <= num)return false;
num = tmp.get(i);
}
return true;
}
}
230. 二叉搜索树中第 K 小的元素
解法一、中序递增
98的变式。在while里判断一下tmp.size()和k的关系剪枝,可以效率提升一半多。
java">class Solution {
public int kthSmallest(TreeNode root, int k) {
List<Integer> tmp = new LinkedList<>();
while(root != null){
if(root.left == null){
tmp.add(root.val);
root = root.right;
}else{
TreeNode node = new TreeNode();
while(node.right != null && node.right != root){
node = node.right;
}
if(node.right == null){
node.right = root;
}else{
node.right = null;
tmp.add(root.val);
root = root.right;
}
}
}
return tmp.get(k);
}
}
解法二、dfs
第一个if是边界,第二个if是剪枝,第三个是答案赋值判断
java">class Solution {
private int k,res= 0;
public int kthSmallest(TreeNode root, int k) {
this.k = k;
dfs(root);
return res;
}
private void dfs(TreeNode root){
if(root==null)return;
dfs(root.left);
if(k==0)return;
if(--k==0)res = root.val;
dfs(root.right);
}
}
199. 二叉树的右视图
解法一、递归
java">class Solution {
public List<Integer> rightSideView(TreeNode root) {
Deque<TreeNode> a = new LinkedList<>();
a.add(root);
List<Integer> res = new LinkedList<>();
if(root == null)return res;
while(!a.isEmpty()){
Deque<TreeNode> q = a;
a = new LinkedList<>();
res.add(q.getLast().val);
while(!q.isEmpty()){
TreeNode node = q.pollLast();
if(node.right != null)a.addFirst(node.right);
if(node.left != null)a.addFirst(node.left);
}
}
return res;
}
}
105. 从前序与中序遍历序列构造二叉树
解法一、递归
靠前序确定节点 靠中序确定左右树 从顶至下递归进行
两个改进:①搜索改进,甚至使用哈希表O(1)。②更改函数传入,l和r,避免复制数组的开销
java">class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
int n = preorder.length;
if(n==0)return null;
int index = findNum(inorder,preorder[0]);
int [] l = Arrays.copyOfRange(inorder,0,index);
int [] r = Arrays.copyOfRange(inorder,index+1,n);
int [] pre1 = Arrays.copyOfRange(preorder,1,1+index);
int [] pre2 = Arrays.copyOfRange(preorder,1+index,n);
TreeNode left = buildTree(pre1,l);
TreeNode right = buildTree(pre2,r);
return new TreeNode(preorder[0],left,right);
}
public int findNum(int[] nums,int num){
int n = nums.length;
for(int i = 0;i < n;i++){
if(nums[i] == num)return i;
}
return -1;
}
}
437. 路径总和 III
解法一、 递归 哈希
务必恢复现场
java">class Solution {
private int ans;
public int pathSum(TreeNode root, int targetSum) {
Map<Long,Integer>cnt = new HashMap<>();
cnt.put(0L,1);
dfs(root,0,targetSum,cnt);
return ans;
}
private void dfs(TreeNode root,long s,int targetSum,Map<Long,Integer> cnt){
if(root == null)return;
s+= root.val;
ans += cnt.getOrDefault(s - targetSum, 0);
cnt.merge(s,1,Integer::sum);
dfs(root.left,s,targetSum,cnt);
dfs(root.right,s,targetSum,cnt);
cnt.merge(s,-1,Integer::sum);
}
}
236. 二叉树的最近公共祖先
解法一、递归找路径,判断两条路径共同开头
你根本无法理解这个到底有多慢jpg
java">class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
List<TreeNode> l = new LinkedList<>();
List<TreeNode> r = new LinkedList<>();
dfs(root,p,l);
dfs(root,q,r);
int n = l.size(),m = r.size(),x = 0,y = 0;
while(x < n && y < m && l.get(x) == r.get(y)){
x++;
y++;
}
TreeNode res;
if(x>=n)res = l.get(n-1);
else if(y>=m)res = r.get(m-1);
else res = l.get(x-1);
return res;
}
public boolean dfs(TreeNode root,TreeNode p,List<TreeNode> list){
if(root == null)return false;
list.add(root);
if(root == p){
return true;
}
//都是错的才remove
if(dfs(root.left,p,list) || dfs(root.right,p,list))return true;
list.remove(list.size()-1);
return false;
}
}
解法二、递归
进行了一个很大的剪枝。都=null即都没查到,两个都非null即当前节点就公共,p在q下面则返回q即可。
java">class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null || root == p || root == q)return root;
TreeNode left = lowestCommonAncestor(root.left,p,q);
TreeNode right = lowestCommonAncestor(root.right,p,q);
if(left != null && right != null)return root;
return left != null ? left : right;
}
}
124. 二叉树中的最大路径和
解法一、递归
java">class Solution {
private int res = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
dfs(root);
return res;
}
private int dfs(TreeNode root){
if(root == null)return 0;
int left = dfs(root.left);
int right = dfs(root.right);
res = Math.max(left+right+root.val,res);
return Math.max(0,Math.max(left+root.val,right+root.val));
}
}