先来几个推荐的参考博客链接:
LintCode中二叉树与分治法那章有这么些题目:
要求等于target从根节点到叶子节点的路径和的组合,用DFS。
递归需要注意的是,终止情况是什么,也就是遇到叶子节点的时候返回,如果遇到叶子节点的时候恰好满足路径和等于target,就把这条路径加到总结果里面。
然后就是递归的主题,遇到左子节点、遇到右子节点的时候进入子递归。然后记得加入path路径后要remove掉,以方便保持当前路径的唯一性。
public List<List<Integer>> binaryTreePathSum(TreeNode root, int target) {
List<List<Integer>> res = new ArrayList();
if (root == null) {
return res;
}
ArrayList<Integer> path = new ArrayList<Integer>();
path.add(root.val);
dfs(res, path, root, target, root.val);
return res;
}
private void dfs(List<List<Integer>> res, ArrayList<Integer> path, TreeNode root, int target, int sum) {
// 遇到叶子节点的返回条件
if (root.left == null && root.right == null) {
if (sum == target) {
res.add(new ArrayList<Integer>(path));
}
return;
}
if (root.left != null) {
path.add(root.left.val);
dfs(res, path, root.left, target, sum + root.left.val);
path.remove(path.size() - 1);
}
if (root.right != null) {
path.add(root.right.val);
dfs(res, path, root.right, target, sum + root.right.val);
path.remove(path.size() - 1);
}
}
判断两个二叉树是否是完全一样的,连结构也要一样。很明显考的是递归。既然是递归,就注意终结条件,
自然是对不同情况的判断,参数就2个,2个树的root。
1)2个root皆空,则是identical的
2)2个root只有一个空,则非identical
3)2个root皆不空,并且root值不等,则非identical
4)2个root皆不空,并且root值相等,则进行子递归判断左子树和右子树
public boolean isIdentical(TreeNode a, TreeNode b) {
if (a == null && b == null) {
return true;
}
if (a == null || b == null) {
return false;
}
if (a.val != b.val) {
return false;
}
return isIdentical(a.left, b.left) && isIdentical(a.right, b.right);
}
470. Tweaked Identical Binary Tree
也是判断2个二叉树是否完全一样,但是与上道题不同之处在于允许了对称扭曲。同样也是分4种情况判断:
public boolean isTweakedIdentical(TreeNode a, TreeNode b) {
if (a == null && b == null) {
return true;
}
if (a == null || b == null) {
return false;
}
if (a.val == b.val) {
return (isTweakedIdentical(a.left, b.right) && isTweakedIdentical(b.left, a.right)) || (isTweakedIdentical(a.left, b.left) && isTweakedIdentical(b.right, a.right));
}
return false;
}
判断一颗二叉树是不是对称的,跟上道题很像,只要把这颗树的左右子节点作为参数传进去就好。
public boolean isSymmetric(TreeNode root) {
if (root == null) {
return true;
}
if (root.left == null && root.right == null) {
return true;
}
return helper(root.left, root.right);
}
public boolean helper(TreeNode a, TreeNode b) {
if (a == null && b == null) {
return true;
}
if (a == null || b == null) {
return false;
}
if (a.val == b.val) {
return (helper(a.left, b.right) && helper(b.left, a.right));
}
return false;
}
判断一棵树是不是完全二叉树。一个思路就是层次遍历,把每层的节点从左向右依此加入Stack,然后把Stack上层的None弹出,最后检查如果Stack中还有None说明不是Complete Tree 比如上面的不完全二叉树生成的数组为[1, 2, 3, None, 4, None, None],将右侧None弹出后为[1, 2, 3, None, 4],循环查找,发现还有None存在,所以是不完全二叉
public boolean isComplete(TreeNode root) {
if (root == null) {
return true;
}
Queue<TreeNode> q = new LinkedList<TreeNode>();
q.offer(root);
boolean isNull = false;
while (!q.isEmpty()) {
TreeNode node = q.poll();
if (node.left != null) {
if (isNull == true) {
return false;
}
q.offer(node.left);
} else {
isNull = true;
}
if (node.right != null) {
if (isNull == true) {
return false;
}
q.offer(node.right);
} else {
isNull = true;
}
}
return true;
}
还有另外一种方法是递归,比较难理解,可以参考这篇博文:http://www.geeksforgeeks.org/check-whether-binary-tree-complete-not-set-2-recursive-solution/
或者参考这篇博客:http://stackoverflow.com/questions/1442674/how-to-determine-whether-a-binary-tree-is-complete
97. Maximum Depth of Binary Tree
求一个二叉树的最大深度。
public int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
155. Minimum Depth of Binary Tree
求二叉树的最小深度,最小深度,就是从根节点到叶子节点最近的一条路的长度。不递归的话,可以用BFS层级遍历来做。
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
Queue<TreeNode> q = new LinkedList<TreeNode>();
q.offer(root);
int depth = 0;
while (!q.isEmpty()) {
int length = q.size();
depth++;
for (int i = 0; i < length; i++) {
TreeNode n = q.poll();
if (n.left == null && n.right == null) {
return depth;
}
if (n.left != null) {
q.offer(n.left);
}
if (n.right != null) {
q.offer(n.right);
}
}
}
return depth;
}
也可以用递归来做,当当前节点是叶子节点时就返回结果。
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
if (root.left == null && root.right == null) {
return 1;
}
int l = (root.left == null) ? Integer.MAX_VALUE : minDepth(root.left);
int r = (root.right == null) ? Integer.MAX_VALUE : minDepth(root.right);
return 1 + Math.min(l, r);
}
判断一棵树是否为平衡二叉树。如果左右子树的最大高度差超过1的话,那就不是平衡的了。所以这道题是基于求树的最大高度的。
一开始可能会以为直接对左子树和右子树求高度,然后比较两树的高度差。这样的想法是错误的,比如下面这个树就不是平衡的。
所以在递归的过程中,对每一个节点进行高度平衡判断。只要中途有不平衡,那就是不平衡。如果左子树不平衡,那就是不平衡;如果右子树不平衡,那也是不平衡。
private int depth(TreeNode root) {
if (root == null) {
return 0;
}
int l = depth(root.left);
int r = depth(root.right);
if (Math.abs(l-r) > 1 || l == -1 || r == -1) {
return -1;
}
return 1 + Math.max(l, r);
}
public boolean isBalanced(TreeNode root) {
return depth(root) != -1;
}
求最近公共父节点。参数是1个root节点和2个输入节点A、B。求得就是A和B的最近公共父节点。
查找两个node的最早的公共祖先,分三种情况:
1. 如果两个node在root的两边,那么最早的公共祖先就是root。
2. 如果两个node在root的左边,那么把root.left作为root,再递归。
3. 如果两个node在root的右边,那么把root.right作为root,再递归。
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode A, TreeNode B) {
if (root == null || root == A || root == B) {
return root;
}
// divide
TreeNode l = lowestCommonAncestor(root.left, A, B);
TreeNode r = lowestCommonAncestor(root.right, A, B);
// conquer
if (l != null && r != null) {
return root;
}
if (l != null) {
return l;
}
if (r != null) {
return r;
}
return null;
}
474. Lowest Common Ancestor II
跟上道题类似,不过节点之间的关系是parent node的关系了。
如果每个结点都有一个指针指向它的父结点,于是我们可以从任何一个结点出发,得到一个到达树根结点的单向链表。因此这个问题转换为两个单向链表的第一个公共结点。
public ParentTreeNode lowestCommonAncestorII(ParentTreeNode root,
ParentTreeNode A,
ParentTreeNode B) {
Stack<ParentTreeNode> s1 = new Stack();
Stack<ParentTreeNode> s2 = new Stack();
s1.push(A);
s2.push(B);
while (s1.peek().parent != null) {
s1.push(s1.peek().parent);
}
while (s2.peek().parent != null) {
s2.push(s2.peek().parent);
}
if (s1.empty() || s2.empty()) {
return root;
}
ParentTreeNode res = null;
while (!s1.empty() && !s2.empty() && s1.peek() == s2.peek()) {
res = s1.pop();
s2.pop();
}
return res;
}
85. Insert Node in a Binary Search Tree
如果是用非递归的方法,则用一个while循环,找到那个需要插入的地方的父节点。
public TreeNode insertNode(TreeNode root, TreeNode node) {
if (root == null) {
return node;
}
TreeNode res = root;
TreeNode father = null;
while (root != null) {
father = root;
if (node.val > root.val) {
root = root.right;
} else {
root = root.left;
}
}
if (node.val > father.val) {
father.right = node;
} else {
father.left = node;
}
return res;
}
如果是用递归的话,那看起来就比较简单了:
public TreeNode insertNode(TreeNode root, TreeNode node) {
if (root == null) {
return node;
}
if (node.val > root.val) {
root.right = insertNode(root.right, node);
} else {
root.left = insertNode(root.left, node);
}
return root;
}
87. Remove Node in Binary Search Tree
难度为Hard,Acceptance为25%.
首先,该题在《算法导论》一书中是有的。思路也较为容易理解,关键是实现算法时需要注意的各种细节。
那么下面说明该题的思路:
第一步,当然是找到要删除的节点node和它的父节点parent,这里采用普通的二分查找方法,找到了就返回node的父节点parent,找不到就返回null
第二步,就是删除该node节点,按照算法导论上的那个算法,删除node节点分为3种情况,1)node没有左右儿子,那么就直接把node的父节点指向node的儿子,即指向null。2)node只有一个儿子(左儿子或者右儿子),这个时候就直接把node的父节点指向左右儿子。3)node有2个儿子,那么这个时候就从左子树里面找到最小的那个节点,来代替node节点。
为了代码简洁,上述的3种情况可以简化为2种情况:
node没有左右儿子、node只有左儿子,可以合并为一种情况:即node的右子树不存在。此时,直接用parent节点的子节点指向该node节点的左节点。
node只有右儿子、node有左右2各儿子,可以合并为一种情况:即node的右子树存在。此时,需要找到node的右子树里的最小节点,用该节点替换node节点。
技巧:因为要删除的节点可能是根节点,因此为了算法的通用性,可以首先new一个dummy节点,该节点的左节点指向根节点,这样处理起来更为方便。
public TreeNode removeNode(TreeNode root, int key) {
TreeNode dummy = new TreeNode(0);
dummy.left = root;
TreeNode parent = find(dummy, root, key);
TreeNode node = null;
// node 不存在
if (parent == null) {
return root;
}
// node 在左边
if (parent.left != null && parent.left.val == key) {
node = parent.left;
}
// node 在右边
if (parent.right != null && parent.right.val == key) {
node = parent.right;
}
deleteNode(parent, node);
return dummy.left;
}
private TreeNode find(TreeNode parent, TreeNode node, int key) {
if(node == null) {
return null;
}
if (key == node.val) {
return parent;
}
if (key > node.val) {
return find(node, node.right, key);
} else {
return find(node, node.left, key);
}
}
private void deleteNode(TreeNode parent, TreeNode node) {
// node的右子树不存在, 处理了两种情况,即只有左子树或者没有子树
if (node.right == null) {
if (parent.right == node) {
parent.right = node.left;
} else {
parent.left = node.left;
}
} else {
// node的右子树存在, 且左子树不存在
if (node.left == null) {
if (parent.right == node) {
parent.right = node.right;
} else {
parent.left = node.right;
}
} else {
// node的左右子树都存在,则寻找node右子树的最小节点,用于取代node
TreeNode father = node;
TreeNode t = node.right;
while (t.left != null) {
father = t;
t = t.left;
}
if (father.left == t) {
father.left = t.right;
} else {
father.right = t.right;
}
node.val = t.val;
}
}
}
11. Search Range in Binary Search Tree
给定一个区间,要求在一个二叉查找树中,找到所有处在这个区间中的节点。最简单的思路就是先用dfs遍历,遍历过程中发现符合条件就加入ArrayList中。最后再对数组排序,返回。但是其实细细一想,是否可以免去这个排序的过程呢?是否可以在遍历的时候就能保持有序呢?考虑到二叉查找树的性质,如果用中序遍历,是可以保证这个的。
public void dfs(TreeNode root, ArrayList<Integer> res, int k1, int k2) {
if (root != null) {
dfs(root.left, res, k1, k2);
if (root.val >= k1 && root.val <= k2) {
res.add(root.val);
}
dfs(root.right, res, k1, k2);
}
}
public ArrayList<Integer> searchRange(TreeNode root, int k1, int k2) {
ArrayList<Integer> res = new ArrayList();
dfs(root, res, k1, k2);
return res;
}
66. Binary Tree Preorder Traversal
67. Binary Tree Inorder Traversal
68. Binary Tree Postorder Traversal
二叉树的前中后序遍历,递归的非常简单,就不再赘述了,非递归的可以参考这篇博客:http://zisong.me/post/suan-fa/geng-jian-dan-de-bian-li-er-cha-shu-de-fang-fa
69. Binary Tree Level Order Traversal
二叉树层次遍历,这个算法已经烂大街了,就不多说了
public ArrayList<ArrayList<Integer>> levelOrder(TreeNode root) {
ArrayList<ArrayList<Integer>> res = new ArrayList();
if (root == null) {
return res;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(root);
while(!queue.isEmpty()) {
ArrayList<Integer> level = new ArrayList();
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode tmp = queue.poll();
level.add(tmp.val);
if (tmp.left != null) {
queue.offer(tmp.left);
}
if (tmp.right != null) {
queue.offer(tmp.right);
}
}
res.add(level);
}
return res;
}
70. Binary Tree Level Order Traversal II
也是二叉树的层次遍历,不过是从叶子节点到根节点了,跟上道题类似,把最后的数组reverse一下就得到了。
就在上面的代码的最后加上这么一句就行:
Collections.reverse(res);
71. Binary Tree Zigzag Level Order Traversal
“之”字型的层次遍历,加个变量判断一下奇偶数就好。
public ArrayList<ArrayList<Integer>> zigzagLevelOrder(TreeNode root) {
ArrayList<ArrayList<Integer>> res = new ArrayList();
if (root == null) {
return res;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(root);
int level = 1;
while(!queue.isEmpty()) {
ArrayList<Integer> path = new ArrayList();
int size = queue.size();
level++;
for (int i = 0; i < size; i++) {
TreeNode tmp = queue.poll();
path.add(tmp.val);
if (tmp.left != null) {
queue.offer(tmp.left);
}
if (tmp.right != null) {
queue.offer(tmp.right);
}
}
if (level % 2 == 1) {
Collections.reverse(path);
}
res.add(path);
}
return res;
}
72. Construct Binary Tree from Inorder and Postorder Traversal
根据中序遍历和后续遍历结果,构建出那颗二叉树。要知道如何构建二叉树,首先我们需要知道二叉树的几种遍历方式,譬如有如下的二叉树:
1
--------|-------
2 3
----|---- ----|----
4 5 6 7
前序遍历 1245367
中序遍历 4251637
后续遍历 4526731
仍然以上面那棵二叉树为例,我们可以发现,对于后序遍历来说,最后一个元素一定是根节点,也就是1。然后我们在中序遍历的结果里面找到1所在的位置,那么它的左半部分就是其左子树,有半部分就是其右子树。
我们将中序遍历左半部分425取出,同时发现后序遍历的结果也在相应的位置上面,只是顺序稍微不一样,也就是452。我们可以发现,后序遍历中的2就是该子树的根节点。
上面说到了左子树,对于右子树,我们取出637,同时发现后序遍历中对应的数据偏移了一格,并且顺序也不一样,为673。而3就是这颗右子树的根节点。
重复上述过程,通过后续遍历找到根节点,然后在中序遍历数据中根据根节点拆分成两个部分,同时将对应的后序遍历的数据也拆分成两个部分,重复递归,就可以得到整个二叉树了。
private TreeNode build(int[] in, int instart, int inend, int[] post, int poststart, int postend) {
if (instart > inend) {
return null;
}
TreeNode root = new TreeNode(post[postend]);
int pos = -1;
for (int i = 0; i <= inend; i++) {
if (in[i] == post[postend]) {
pos = i;
}
}
root.left = build(in, instart, pos - 1, post, poststart, poststart + pos - instart - 1);
root.right = build(in, pos + 1, inend, post, poststart + pos - instart, postend - 1);
return root;
}
public TreeNode buildTree(int[] in, int[] post) {
return build(in, 0, in.length - 1, post, 0, post.length - 1);
}
73. Construct Binary Tree from Preorder and Inorder Traversal
根据后续和中序遍历结果,构建出那颗二叉树。参考:https://siddontang.gitbooks.io/leetcode-solution/content/tree/construct_binary_tree.html
private TreeNode build(int[] pre, int prestart, int preend, int[] in, int instart, int inend) {
if (instart > inend) {
return null;
}
TreeNode root = new TreeNode(pre[prestart]);
int pos = -1;
for (int i = 0; i <= inend; i++) {
if (in[i] == pre[prestart]) {
pos = i;
}
}
root.left = build(pre, prestart + 1, prestart + pos - instart, in, instart, pos - 1);
root.right = build(pre, prestart + pos - instart + 1, preend, in, pos + 1, inend);
return root;
}
public TreeNode buildTree(int[] pre, int[] in) {
return build(pre, 0, pre.length - 1, in, 0, in.length - 1);
}
可以把二叉树序列化String,也可以把String版本的二叉树还原成节点版本的二叉树。可参见这篇博客:http://blog.csdn.net/ljiabin/article/details/49474445
public String serialize(TreeNode root) {
String res = "";
if (root == null) {
return res;
}
Queue<TreeNode> q = new LinkedList<TreeNode>();
q.offer(root);
res += root.val;
res += ',';
while (!q.isEmpty()) {
int length = q.size();
for (int i = 0; i < length; i++) {
TreeNode node = q.poll();
if (node.left != null) {
q.offer(node.left);
res += node.left.val;
res += ',';
} else {
res += "#,";
}
if (node.right != null) {
q.offer(node.right);
res += node.right.val;
res += ',';
} else {
res += "#,";
}
}
}
return res;
}
public TreeNode deserialize(String data) {
if (data == null || data == "") {
return null;
}
String[] res = data.split(",");
// 节点i之前null节点的个数
int[] nums = new int[res.length];
TreeNode[] arr = new TreeNode[res.length];
for (int i = 0; i < res.length; i++) {
if (i > 0) {
nums[i] = nums[i - 1];
}
if (res[i].equals("#")) {
arr[i] = null;
nums[i]++;
} else {
arr[i] = new TreeNode(Integer.parseInt(res[i]));
}
}
for (int i = 0; i < res.length; i++) {
if (arr[i] != null) {
arr[i].left = arr[(i - nums[i]) * 2 + 1];
arr[i].right = arr[(i - nums[i]) * 2 + 2];
}
}
return arr[0];
}
475. Binary Tree Maximum Path Sum II
找到一个二叉树从root到叶子节点的最大的一个路径和,非常简单的递归,但是需要考虑的问题是,节点可能是负数。所以每次返回的时候,是选择(左节点,右节点,0)的最大值:
public int maxPathSum2(TreeNode root) {
if (root == null) {
return 0;
}
return root.val + Math.max(0, Math.max(maxPathSum2(root.left), maxPathSum2(root.right)));
}
94. Binary Tree Maximum Path Sum
求一个二叉树的最大路径和,可以从任意节点到任意节点。
最优路径上的节点一定是连续的,不能中断
最优路径中一定包含某个子树的根节点
写一个递归函数,实现计算根节点到任意点的最大路径和,以及穿过根节点的最大路径和,用一个全局变量保存最优解。
需要注意的是,节点可能会包含负数,所以计算的时候,要判断一下负数。
private int res = Integer.MIN_VALUE;
private int helper(TreeNode root) {
if (root == null) {
return Integer.MIN_VALUE;
}
// divide
int left = helper(root.left);
int right = helper(root.right);
// conquer
int root2any = Math.max(Math.max(left, right), 0) + root.val;
int any2any = Math.max(0, left) + Math.max(0, right) + root.val;
res = Math.max(res, Math.max(root2any, any2any));
return root2any;
}
public int maxPathSum(TreeNode root) {
if (root == null) {
return Integer.MIN_VALUE;
}
helper(root);
return res;
}
448. Inorder Successor in Binary Search Tree
找到一个节点的中序后继节点。我的思路是先通过中序遍历得到一个有序的数组,然后再在这个数组中二分查找,找到那个节点。这个算法比标准答案还要快些
private void inorder(TreeNode root, ArrayList<TreeNode> res) {
if (root != null) {
inorder(root.left, res);
res.add(root);
inorder(root.right, res);
}
}
private int binarySearch(ArrayList<TreeNode> res, int val) {
int start = 0, end = res.size() - 1;
while (start + 1 < end) {
int mid = start + (end - start) / 2;
if (res.get(mid).val == val) {
if (mid + 1 < res.size()) {
return mid + 1;
} else {
return -1;
}
} else if (val > res.get(mid).val) {
start = mid;
} else {
end = mid;
}
}
if (res.get(start).val == val && start + 1 < res.size()) {
return start + 1;
}
if (res.get(end).val == val && end + 1 < res.size()) {
return end + 1;
}
return -1;
}
public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
ArrayList<TreeNode> res = new ArrayList();
if (root == null || p == null) {
return null;
}
inorder(root, res);
int pos = binarySearch(res, p.val);
if (pos != -1) {
return res.get(pos);
}
return null;
}
当然还有种方法,也是在CC150上看到的,伪代码如下:
Node inorderSucc(Node n) {
if (n has a right subtree) {
return leftmost child of right subtree
} else {
while ( n is a right son of n.parent) {
n = n.parent;
}
return n.parent;
}
}
由于题目未提供指向父亲的指针,所以这种情况需要从根往节点n进行搜索了: 把从根往Node n一路搜索遇到的节点都存入stack,然后再逐个从stack里往外pop,遇到的第一个比Node n的value大的值便是答案了。如果一路pop到root还没有的话,那就返回null:
class Solution {
public TreeNode leftMost(TreeNode root) {
while (root.left != null) {
root = root.left;
}
return root;
}
public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
if (root == null || p == null) {
return null;
}
if (p.right != null) {
return leftMost(p.right);
} else {
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.push(root);
while (root.val != p.val) {
if (p.val > root.val) {
stack.push(root.right);
root = root.right;
} else if (p.val < root.val) {
stack.push(root.left);
root = root.left;
} else {
break;
}
}
while (!stack.isEmpty()) {
TreeNode head = stack.pop();
if (head.val > p.val) {
return head;
}
}
}
return null;
}
}
95. Validate Binary Search Tree
判断一颗二叉树是不是合法的二分查找树,这时可以用到一个性质,如果它的中序遍历的结果是非递减的,拿它就是合法的:
private void inorder(TreeNode root, ArrayList<Integer> list) {
if (root != null) {
inorder(root.left, list);
list.add(root.val);
inorder(root.right, list);
}
}
public boolean isValidBST(TreeNode root) {
ArrayList<Integer> list = new ArrayList();
inorder(root, list);
for (int i = 1; i < list.size(); i++) {
if (list.get(i) <= list.get(i - 1)) {
return false;
}
}
return true;
}
当然我们可以优化一下,在遍历的过程中就比较两个相邻的数是否满足递增序列的要求,这样就能把空间复杂度降低:
public Integer last = null;
public boolean inorder(TreeNode root) {
if (root == null) {
return true;
}
boolean l = inorder(root.left);
if (last != null && root.val <= last) {
return false;
}
last = root.val;
boolean r = inorder(root.right);
return l && r;
}
还有一种方法是min max方法,我在CC150里看到的:
public boolean checkBST(TreeNode root, Integer min, Integer max) {
if (root == null) {
return true;
}
if (max != null && root.val >= max || min != null && root.val <= min) {
return false;
}
boolean l = checkBST(root.left, min, root.val);
boolean r = checkBST(root.right, root.val, max);
return l && r;
}
86. Binary Search Tree Iterator
算是一道系统设计题,给定一些函数接口,要你按照要求实现。实现一个iterator可以遍历二叉树。我用的是中序遍历,把整个树保存在一个ArrayList中,然后用一个pos变量记录当前位置。比九章的标准答案还要快一点:
/**
* Definition of TreeNode:
* public class TreeNode {
* public int val;
* public TreeNode left, right;
* public TreeNode(int val) {
* this.val = val;
* this.left = this.right = null;
* }
* }
* Example of iterate a tree:
* BSTIterator iterator = new BSTIterator(root);
* while (iterator.hasNext()) {
* TreeNode node = iterator.next();
* do something for node
* }
*/
public class BSTIterator {
private ArrayList<TreeNode> arr = new ArrayList<TreeNode>();
private int pos = -1;
//@param root: The root of binary tree.
private void inorder(TreeNode root) {
if (root != null) {
inorder(root.left);
arr.add(root);
inorder(root.right);
}
}
public BSTIterator(TreeNode root) {
inorder(root);
if (arr.size() > 0) {
pos = 0;
}
}
//@return: True if there has next node, or false
public boolean hasNext() {
return pos >= 0 && pos < arr.size();
}
//@return: return next node
public TreeNode next() {
return arr.get(pos++);
}
}
此外,Chapter之外的二叉树还有这么一些题目:
反转二叉树,HomeBrew的创始人就是倒在了这道题下,用递归做很简单,先把根节点的左右子树对调,然后再递归得对左右子树调用递归。递归返回条件就是当前节点为空。
public void invertBinaryTree(TreeNode root) {
if (root == null) {
return;
}
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
invertBinaryTree(root.left);
invertBinaryTree(root.right);
}
要求返回所有从根节点到叶子节点的路径。这种要求所有路径的,一看就基本知道八九成是用DFS来做了:
private void dfs(List<String> res, String path, TreeNode root) {
if (root.left == null && root.right == null) {
path += root.val;
res.add(path);
return;
}
if (root.left != null) {
dfs(res, path + root.val + "->", root.left);
}
if (root.right != null) {
dfs(res, path + root.val + "->", root.right);
}
}
public List<String> binaryTreePaths(TreeNode root) {
List<String> res = new ArrayList();
if (root == null) {
return res;
}
dfs(res, "", root);
return res;
}
判断一棵树A是否包含另一棵树B,用DFS遍历A的每个节点,对于A的每个节点,调用一个equal函数判断A的这个节点的子树和B树是否完全相等。
private void dfs(TreeNode T1, TreeNode T2) {
if (T1 != null) {
if (equal(T1, T2)) {
flag = true;
return;
}
dfs(T1.left, T2);
dfs(T1.right, T2);
}
}
public boolean isSubtree(TreeNode T1, TreeNode T2) {
if (T2 == null) {
return true;
}
dfs(T1, T2);
return flag;
}
private boolean equal(TreeNode T1, TreeNode T2) {
// 两个都为空
if (T1 == null && T2 == null) {
return true;
}
// 只有一个为空
if (T1 == null || T2 == null) {
return false;
}
// 都不空,且不相等的时候,就返回false
if (T1.val != T2.val) {
return false;
}
// 相等,则进一步判断
return equal(T1.left, T2.left) && equal(T1.right, T2.right);
}
453. Flatten Binary Tree to Linked List
要求把一棵树按照先根遍历的规则,转换为一个只有右子树的数:
1
\
1 2
/ \ \
2 5 => 3
/ \ \ \
3 4 6 4
\
5
\
6
private TreeNode p = null;
private void inorder(TreeNode root) {
if (root != null) {
if (p != null) {
p.right = root;
p.left = null; <span style="font-family: Arial, Helvetica, sans-serif;">// 这一行代码不能少!</span>
}
p = root;
TreeNode r = root.right; // 这一行代码不能少!
inorder(root.left);
inorder(r);
}
}
public void flatten(TreeNode root) {
inorder(root);
}
注意每次遍历是把上个节点和当前节点建立联系。如果上个节点存在,则直接把上个节点的右子树指向当前节点。然后把p指向当前节点,并把右子树先拿出来(这点很重要,不拿出来基本就是stackoverflow了),再对左右子树分别进行子递归。
177. Convert Sorted Array to Binary Search Tree With Minimal Height
把一个有序数组转换为一颗高度最小的二叉搜索树。思路很简单,递归建树,每次递归都选择数组的中点建树。
private TreeNode build(int[] arr, int start, int end, TreeNode root) {
if (start <= end) {
int mid = start + (end - start) / 2;
root = new TreeNode(arr[mid]);
root.left = build(arr, start, mid - 1, root.left);
root.right = build(arr, mid + 1, end, root.right);
}
return root;
}
public TreeNode sortedArrayToBST(int[] A) {
TreeNode root = null;
root = build(A, 0, A.length - 1, root);
return root;
}
Calculate the an % b where a, b and n are all 32bit integers.
For 231 % 3 = 2
For 1001000 % 1000 = 0
首先可以参考下百度百科里对于取模运算的定义:http://baike.baidu.com/view/4887065.htm
我们发现,取模运算有这么些规律:
(a * b) % p = (a % p * b % p) % p
public int fastPower(int a, int b, int n) {
if (n == 1) {
return a % b;
}
if (n == 0) {
return 1 % b;
}
long res = fastPower(a, b, n / 2);
res = res * res % b;
if (n % 2 == 1) {
res = res * a % b % b;
}
return (int)res;
}
这道题与上面那道题有异曲同工之妙,不过推导公式没有上面那么复杂了。也是分治法,需要考虑n小于0的情况,还有分奇偶的情况。如果用分治法则时间复杂度是O(logn),如果是直接用从n递归到n-1再递归到n-2···这种方法,则时间复杂度是O(N)
public double myPow(double x, int n) {
if (n == 0) {
return 1.0;
}
if (n < 0) {
return 1.0 / myPow(x, -1 * n);
}
double half = myPow(x, n / 2);
if (n % 2 == 1) {
return x * half * half;
}
return half * half;
}