1 2 3 4 5 6 7 8 9 10 11 12 13 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; } }
二叉树的遍历 前序非递归:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public ArrayList<Integer> preorderTraversal (TreeNode root) { ArrayList<Integer> arr = new ArrayList<Integer>(); if (root == null ) return arr; LinkedList<TreeNode> stack = new LinkedList<>(); stack.push(root); while (!stack.isEmpty()) { TreeNode node = stack.pop(); arr.add(node.val); if (node.right != null ) { stack.push(node.right); } if (node.left != null ) { stack.push(node.left); } } return arr; }
中序非递归,把所有的左结点入栈,再挨个弹出来:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public ArrayList<Integer> inorderTraversal (TreeNode root) { ArrayList<Integer> arr = new ArrayList<Integer>(); if (root == null ) return arr; LinkedList<TreeNode> stack = new LinkedList<>(); TreeNode node = root; while (!stack.isEmpty() || node != null ) { if (node != null ) { stack.push(node); node = node.left; } else { node = stack.pop(); arr.add(node.val); node = node.right; } } return arr; }
230. 二叉搜索树中第K小的元素 给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素。
说明: 你可以假设 k 总是有效的,1 ≤ k ≤ 二叉搜索树元素个数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 示例 1 : 输入: root = [3 ,1 ,4 ,null ,2 ], k = 1 3 / \ 1 4 \ 2 输出: 1 示例 2 : 输入: root = [5 ,3 ,6 ,2 ,4 ,null ,null ,1 ], k = 3 5 / \ 3 6 / \ 2 4 / 1 输出: 3
思路:中序遍历,top k的k只能已成员变量的形式计数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public int kthSmallest (TreeNode root, int k) { mK = k; val = 0 ; inOrder(root); return val; } private int val;private int mK = 0 ;void inOrder (TreeNode node) { if (node == null ) { return ; } inOrder(node.left); if (--mK == 0 ){ val = node.val; return ; } inOrder(node.right); }
98. 验证二叉搜索树 给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。 节点的右子树只包含 大于 当前节点的数。 所有左子树和右子树自身必须也是二叉搜索树。
思路:二叉搜索树的特点是每个结点都在一个区间内,利用这个区间判断,root在Int.MAX到Int.MIN区间,其余结点类推
1 2 3 4 5 6 7 8 public boolean isValidBST (TreeNode root) { return isBST(root,Integer.MIN_VALUE,Integer.MAX_VALUE); } private boolean isBST (TreeNode root, int lo, int hi) { if (root == null ) return true ; return root.val > lo && root.val < hi && isBST(root.left, lo, root.val) && isBST(root.right, root.val, hi); }
103. 二叉树的锯齿形层序遍历 给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。
思路:交替使用两个栈,第一个栈保存从左向右的,第二栈保存从右向左的结点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public ArrayList<ArrayList<Integer>> zigzagLevelOrder(TreeNode root) { Stack<TreeNode> s1 = new Stack<>(); Stack<TreeNode> s2 = new Stack<>(); ArrayList<ArrayList<Integer>> arr = new ArrayList<>(); if (root == null ) return arr; ArrayList<Integer> list = new ArrayList<>(); s1.add(root); while (!s1.isEmpty() || !s2.isEmpty()) { while (!s1.isEmpty()) { TreeNode node = s1.pop(); list.add(node.val); if (node.left != null ) { s2.add(node.left); } if (node.right != null ) { s2.add(node.right); } } if (!list.isEmpty()) { arr.add(list); list = new ArrayList<>(); } while (!s2.isEmpty()) { TreeNode node = s2.pop(); list.add(node.val); if (node.right != null ) { s1.add(node.right); } if (node.left != null ) { s1.add(node.left); } } if (!list.isEmpty()) { arr.add(list); list = new ArrayList<>(); } } return arr; }
109. 有序链表转换二叉搜索树 给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
1 2 3 4 5 6 7 8 9 10 11 12 示例: 给定的有序链表: [-10 , -3 , 0 , 5 , 9 ], 一个可能的答案是:[0 , -3 , 9 , -10 , null , 5 ], 它可以表示下面这个高度平衡二叉搜索树: 0 / \ -3 9 / / -10 5
思路:找到链表中间结点,然后构建treenode 结点,依次遍历左边链表、右边链表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public TreeNode sortedListToBST (ListNode head) { return sortedListToBST(head,null ); } public TreeNode sortedListToBST (ListNode start,ListNode end) { if (start == end) return null ; ListNode middle = middleNode(start,end); TreeNode root = new TreeNode(middle.val); root.left = sortedListToBST(start,middle); root.right = sortedListToBST(middle.next,end); return root; } ListNode middleNode (ListNode start,ListNode end) { ListNode fast = start,slow = start; while (fast != end && fast.next != end ){ fast = fast.next.next; slow = slow.next; } return slow; } public TreeNode sortedListToBST (ListNode head) { ArrayList<Integer> list = new ArrayList<>(); while (head != null ) { list.add(head.val); head = head.next; } return generate(list, 0 , list.size() - 1 ); } private TreeNode generate (List<Integer> list, int lo, int hi) { if (lo > hi) return null ; int mid = (lo + hi+1 ) / 2 ; TreeNode r = new TreeNode(list.get(mid)); r.left = generate(list, lo, mid - 1 ); r.right = generate(list, mid + 1 , hi); return r; }
102. 二叉树的层序遍历 给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。思路:广度优先遍历,使用队列模拟这个过程: 访问一个结点时把它的子结点加入队列,并用一个tail指针指向最后入队的那个结点,它代表下一排的最后一个,如果正在访问的结点当前行的最后一个,那就代表要换行了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public ArrayList<ArrayList<Integer>> levelOrder(TreeNode root) { Queue<TreeNode> queue = new LinkedList<TreeNode>(); ArrayList<ArrayList<Integer>> arr = new ArrayList<ArrayList<Integer>>(); ArrayList<Integer> arr2 = new ArrayList<Integer>(); if (root == null ) return arr; TreeNode last = root, nlast = root; queue.offer(root); while (!queue.isEmpty()) { TreeNode node = queue.poll(); arr2.add(node.val); if (node.left != null ) { nlast = node.left; queue.offer(node.left); } if (node.right != null ){ nlast = node.right; queue.offer(node.right); } if (node == last) { last = nlast; arr.add(arr2); arr2 = new ArrayList<Integer>(); } } return arr; }
108. 将有序数组转换为二叉搜索树 给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。
高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。
**思路: 二叉搜索树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
二叉平衡搜索树(AVL): 左子树与右子树高度之差的绝对值不超过1
[1,2]数组:mid = (lo + hi+1) / 2 这样取出来就是两个中间大的那个作为父结点。mid = (lo + hi) / 2 取的小的那个 **
1 2 3 4 5 6 7 8 9 10 11 12 13 public TreeNode sortedArrayToBST (int [] num) { if (num == null || num.length == 0 ) return null ; return gen(num, 0 , num.length - 1 ); } private TreeNode gen (int [] num, int lo, int hi) { if (lo > hi) return null ; int mid = (lo + hi+1 ) / 2 ; TreeNode node = new TreeNode(num[mid]); node.left = gen(num, lo, mid - 1 ); node.right = gen(num, mid + 1 , hi); return node; }
112. 路径总和 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
叶子节点 是指没有子节点的节点。思路:从上向下分摊sum,只要有一个子结点的值等于最后剩余的值都ok
1 2 3 4 5 6 7 public boolean hasPathSum (TreeNode root, int sum) { if (root == null ) return false ; if (root.right == null && root.left == null ) { return sum - root.val == 0 ; } return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val); }
113. 路径总和 II 给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
思路:深度优先遍历,add与remove成对,记得remove的参数是index
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public ArrayList<ArrayList<Integer>> pathSum(TreeNode root, int sum) { if (root == null ) { return new ArrayList<>(); } ArrayList<ArrayList<Integer>> arrs = new ArrayList<>(); ArrayList<Integer> arr = new ArrayList<>(); pathSumIn(arrs, arr, root, sum); return arrs; } public void pathSumIn (ArrayList<ArrayList<Integer>> arrs, ArrayList<Integer> arr, TreeNode root, int sum) { if (root.left == null && root.right == null ) { int v = sum - root.val; if (v == 0 ) { arr.add(root.val); arrs.add((ArrayList<Integer>) arr.clone()); arr.remove(arr.size() - 1 ); } return ; } arr.add(root.val); if (root.left != null ) { pathSumIn(arrs, arr, root.left, sum - root.val); } if (root.right != null ) { pathSumIn(arrs, arr, root.right, sum - root.val); } arr.remove(arr.size() - 1 ); }
104. 二叉树的最大深度 给定一个二叉树,找出其最大深度。 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
1 2 3 4 5 6 public int maxDepth (TreeNode root) { if (root == null ) return 0 ; int left = maxDepth(root.left); int right = maxDepth(root.right); return left > right ? left + 1 : right + 1 ; }
111. 二叉树的最小深度 给定一个二叉树,找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
思路:求左右子树的最小高度,最后加1即可,但当发现结点的左子树或右子树的高度为0时要排除它,因为此时就证明这个结点一定不是一个叶子结点
1 2 3 4 5 6 7 8 9 10 11 12 13 public int run (TreeNode root) { if (root == null ) return 0 ; int left = run(root.left); int right = run(root.right); if (left == 0 ) { return right + 1 ; } else if (right == 0 ) { return left + 1 ; } return left > right ? right + 1 : left + 1 ; }
538. 把二叉搜索树转换为累加树 给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。
1 2 3 4 5 6 7 8 9 Input: The root of a Binary Search Tree like this : 5 / \ 2 13 Output: The root of a Greater Tree like this : 18 / \ 20 13
思路:从右子树开始的中序遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private static void order (TreeNode root, int [] s) { if (root == null ) return ; order(root.right, s); root.val += s[0 ]; s[0 ] = root.val; order(root.left, s); } public TreeNode convertBST (TreeNode root) { int [] s = {0 }; order(root, s); return root; }
129. 求根节点到叶节点数字之和 给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。 每条从根节点到叶节点的路径都代表一个数字:
1 2 3 4 5 6 7 8 1 / \ 2 3 The root-to-leaf path1->2represents the number12. The root-to-leaf path1->3represents the number13. Return the sum = 12 + 13 =25.
思路:深度优先遍历,前序遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public int sumNumbers (TreeNode root) { return dfs(root, 0 ); } public int dfs (TreeNode root, int prevSum) { if (root == null ) { return 0 ; } int sum = prevSum * 10 + root.val; if (root.left == null && root.right == null ) { return sum; } else { return dfs(root.left, sum) + dfs(root.right, sum); } }
96. 不同的二叉搜索树 给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。思路:动态规划,0个结点1种、1个结点1种、2个结点等于左边的情况乘以右边的情况,要记住第n个结点,不能把n算进来,n-1的元素中两两组合
1 2 3 4 5 6 7 8 9 10 11 public int numTrees (int n) { int [] result = new int [n + 1 ]; result[0 ] = 1 ; result[1 ] = 1 ; for (int i = 2 ; i < result.length; i++) { for (int j = 0 ; j < i; j++) { result[i] += result[j] * result[i - j-1 ]; } } return result[n]; }
116. 填充每个节点的下一个右侧节点指针 给定一个 完全二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。
1 2 3 4 5 6 struct Node { int val; Node *left; Node *right; Node *next; }
思路:前序遍历,再次回到遍历的这个结点时它的next已经设置了,所以可以直接访问到next指针,方便设置right的next
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public void connect (Node parent,Node root,boolean isRight) { if (root == null ) return ; if (parent != null ){ if (!isRight){ root.next = parent.right; }else { Node next = parent; next = next.next; if (next != null ){ root.next = next.left; } } } connect(root,root.left,false ); connect(root,root.right,true ); } public Node connect (Node root) { connect(null ,root,false ); return root; }
117. 填充每个节点的下一个右侧节点指针 II 填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。 初始状态下,所有 next 指针都被设置为 NULL。
思路:广度优先遍历,与填完整二叉树不同的是,我们不能用递归的方式完成广度优先遍历,必须使用队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public Node connect (Node root) { if (root == null ) return null ; Queue<Node> queue = new LinkedList<Node>(); Node tail = root; Node nextTail = root; Node preNode = null ; queue.offer(root); while (!queue.isEmpty()){ Node curNode = queue.remove(); if (curNode.left != null ){ queue.offer(curNode.left); nextTail = curNode.left; } if (curNode.right != null ){ queue.offer(curNode.right); nextTail = curNode.right; } if (preNode != null ){ preNode.next = curNode; } preNode = curNode; if (curNode == tail){ tail = nextTail; nextTail = null ; preNode = null ; } } return root; }
114. 二叉树展开为链表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 For example, given the following tree: 1 / \ 2 5 / \ \ 3 4 6 The flattened tree should look like: 1 \ 2 \ 3 \ 4 \ 5 \ 6
**思路,后序遍历,遍历到一个root结点时,只有它存在左子结点才处理: 1、遍历找到左子结点的最右结点 2、该最右结点指向root结点的右结点 3、再把左子结点插入到右结点中 **
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public void flatten (TreeNode root) { if (root == null ) return ; flatten(root.left); flatten(root.right); if (root.left != null ) { TreeNode tail = root.left; while (tail.right != null ) { tail = tail.right; } TreeNode temp = root.right; root.right = root.left; tail.right = temp; root.left = null ; } }
110. 平衡二叉树 给定一个二叉树,判断它是否是高度平衡的二叉树。 本题中,一棵高度平衡二叉树定义为: 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
思路:求出每个结点的左右子树的最大高度,然后比较
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 static private int maxDepth (TreeNode root) { if (root == null ) return 0 ; if (root.left == null && root.right == null ) return 1 ; int left = maxDepth(root.left); int right = maxDepth(root.right); return left > right ? left + 1 : right + 1 ; } public boolean isBalanced (TreeNode root) { if (root == null ) return true ; int left = maxDepth(root.left); int right = maxDepth(root.right); if (Math.abs(left - right) > 1 ) return false ; return isBalanced(root.left) && isBalanced(root.right); }
100. 相同的树 给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
1 2 3 4 5 6 public boolean isSameTree (TreeNode p, TreeNode q) { if (q == null && p == null ) return true ; if (q== null || p == null ) return false ; if (q.val== p.val) return isSameTree(p.left,q.left) && isSameTree(p.right,q.right); return false ; }
101. 对称二叉树 给你一个二叉树的根节点 root , 检查它是否轴对称。
1 2 3 4 5 6 7 8 9 10 11 public boolean isSymmetric (TreeNode root) { if (root == null ) return true ; return isSame(root.right, root.left); } private boolean isSame (TreeNode p, TreeNode q) { if (p == null && q == null ) return true ; if (p == null || q == null ) return false ; if (p.val == q.val) return isSame(p.left, q.right) && isSame(p.right, q.left); return false ; }
非递归,用一个栈就行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public boolean isSymmetric (TreeNode root) { if (root == null ) return true ; return isSame2(root.right, root.left); } public boolean isSame2 (TreeNode p, TreeNode q) { if (p == null && q == null ) return true ; if (p == null || q == null ) return false ; LinkedList<TreeNode> stack = new LinkedList<>(); stack.push(p); stack.push(q); while (!stack.isEmpty()) { TreeNode n1 = stack.pop(); TreeNode n2 = stack.pop(); if (n1.val != n2.val) return false ; if (n1.left != null && n2.right != null ) { stack.push(n1.left); stack.push(n2.right); } else if (n1.left != null || n2.right != null ) { return false ; } if (n1.right != null && n2.left != null ) { stack.push(n1.right); stack.push(n2.left); } else if (n1.right != null || n2.left != null ) { return false ; } } return true ; }
99. 恢复二叉搜索树 给你二叉搜索树的根节点 root ,该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下,恢复这棵树 。
思路:中序遍历得到升序数组,然后找到其中的逆序的两个结点的位置,找到第一对逆序结点时取第一个结点的位置,第二对逆序结点取第二个结点的位置,如果没有第二对逆序结点,那说明是相邻的两个结点被交换了。最后再次执行遍历二叉树,对两个结点重新赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public void recoverTree (TreeNode root) { List<Integer> nodes = new LinkedList(); inOrder(root,nodes); List<Integer> two = findTowNode(nodes); if (two.size() > 1 ){ recover(root,nodes.get(two.get(0 )),nodes.get(two.get(1 ))); }else { recover(root,nodes.get(two.get(0 )),nodes.get(two.get(0 )+1 )); } } void recover (TreeNode root,int x,int y) { if (root == null ) return ; if (root.val == x){ root.val = y; }else if (root.val == y){ root.val = x; } recover(root.left,x,y); recover(root.right,x,y); } List<Integer> findTowNode (List<Integer> nodes) { List<Integer> two = new ArrayList(); boolean isAddOne = false ; for (int i=0 ;i< nodes.size()-1 ;i++){ if (nodes.get(i) > nodes.get(i+1 )){ if (!isAddOne){ isAddOne = true ; two.add(i); } else { two.add(i+1 ); } } } return two; } void inOrder (TreeNode root,List<Integer> nodes) { if (root == null ) return ; inOrder(root.left,nodes); nodes.add(root.val); inOrder(root.right,nodes); }
方法二:中序遍历,同时保存结点和与之对应的值到两个list,对保存值数组排序,然后再依次重新赋值给保存结点的List,完事。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public void recoverTree (TreeNode root) { inOrder(root); Collections.sort(vals); for (int i = 0 ; i < vals.size(); i++) { nodes.get(i).val = vals.get(i); } } LinkedList<TreeNode> nodes = new LinkedList<>(); ArrayList<Integer> vals = new ArrayList<>(); private void inOrder (TreeNode root) { if (root == null ) return ; inOrder(root.left); nodes.add(root); vals.add(root.val); inOrder(root.right); }
107. 二叉树的层序遍历 II 给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
思路:得到的每行添加到返回的List的第一个就行了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public List<List<Integer>> levelOrderBottom(TreeNode root) { List<List<Integer>> arrs = new ArrayList<>(); if (root == null ) return arrs; ArrayList<Integer> arr = new ArrayList<>(); LinkedList<TreeNode> queue = new LinkedList<>(); queue.offer(root); TreeNode tail = root; TreeNode nextTail = root; while (!queue.isEmpty()) { TreeNode node = queue.remove(); if (node.left != null ) { queue.offer(node.left); nextTail = node.left; } if (node.right != null ) { queue.offer(node.right); nextTail = node.right; } arr.add(node.val); if (node == tail) { arrs.add(0 ,arr); arr = new ArrayList<>(); tail = nextTail; } } return arrs; }