面试精选:二叉树问题集锦
2017-06-24 10:26
543 查看
0.本文目录
本文目录开篇明志
定义二叉树数据结构
二叉树的序列化和反序列化
前序遍历
中序遍历
后序遍历
二叉树层次广度优先遍历
二叉树深度优先遍历
二叉树最大深度
二叉树的最近公共祖先
二叉树的镜像
非递归实现
1.开篇明志
在上一篇《面试精选:链表问题集锦》中, 我总结了一些常见的有关链表的题目; 而本文将对有关二叉树的常见问题进行总结。2.定义二叉树数据结构
public class TreeNode{ public int val; public TreeNode left; public TreeNode right; public TreeNode(int val){ this.val = val; } }
3.二叉树的序列化和反序列化
设计一个算法,并编写代码来序列化和反序列化二叉树。将树写入一个文件被称为“序列化”,读取文件后重建同样的二叉树被称为“反序列化”。如何反序列化或序列化二叉树是没有限制的,你只需要确保可以将二叉树序列化为一个字符串,并且可以将字符串反序列化为原来的树结构。
样例
给出一个测试数据样例, 二叉树{3,9,20,#,#,15,7},表示如下的树结构:
3 / \ 9 20 / \ 15 5
//序列化 public String serialize(TreeNode root) { if (root == null) { return "#,"; } String mid = root.val + ","; String left = serialize(root.left); String right = serialize(root.right); mid += left + right; return mid; } private String data = ""; //反序列化 public TreeNode deserialize(String data) { this.data = data; return desHelper(); } public TreeNode desHelper() { if (this.data.indexOf("#,") == 0) { this.data = this.data.substring(this.data.indexOf(",") + 1); return null; } String midVal = this.data.substring(0, this.data.indexOf(",")); TreeNode mid = new TreeNode(Integer.parseInt(midVal)); this.data = this.data.substring(this.data.indexOf(",") + 1); TreeNode left = desHelper(); TreeNode right = desHelper(); mid.left = left; mid.right = right; return mid; }
4.前序遍历
问题: 给出一棵二叉树,返回其节点值的前序遍历。样例
给出一棵二叉树 {1,#,2,3},
1 \ 2 / 3
返回 [1,2,3].
import java.util.ArrayList; import java.util.Iterator; public class PreOrderTraversal{ //二叉树的前序遍历 public static ArrayList<Integer> preOrderTraversal(TreeNode root){ ArrayList<Integer> result = new ArrayList<Integer>(15); if(root == null) return result; result.add(root.val);//根 ArrayList<Integer> left = preOrderTraversal(root.left);//左 ArrayList<Integer> right = preOrderTraversal(root.right);//右 result.addAll(left); result.addAll(right); return result; } }
5.中序遍历
//二叉树中序遍历 public static ArrayList<Integer> inorderTraversal(TreeNode root){ ArrayList<Integer> result = new ArrayList<Integer>(15); if(root == null) return result; ArrayList<Integer> left = inorderTraversal(root.left); result.addAll(left); result.add(root.val); ArrayList<Integer> right = inorderTraversal(root.right); result.addAll(right); return result; }
6.后序遍历
//二叉树后续遍历 public static ArrayList<Integer> postOrderTraversal(TreeNode root){ ArrayList<Integer> result = new ArrayList<Integer>(15); if(root == null) return result; ArrayList<Integer> left = postOrderTraversal(root.left); result.addAll(left); ArrayList<Integer> right = postOrderTraversal(root.right); result.addAll(right); result. add(root.val); return result; }
7.二叉树层次[广度优先]遍历
广度优先搜索(Breadth First Search),又叫宽度优先搜索或横向优先搜索,是从根结点开始沿着树的宽度搜索遍历。宽度搜索遍历, 其实就是所谓的层次遍历, 分层遍历。
public ArrayList<ArrayList<Integer>> levelOrder(TreeNode root) { ArrayList result = new ArrayList(); if (root == null) { return result; } Queue<TreeNode> queue = new LinkedList<TreeNode>(); queue.offer(root); while (!queue.isEmpty()) { ArrayList<Integer> level = new ArrayList<Integer>(); int size = queue.size(); for (int i = 0; i < size; i++) { TreeNode head = queue.poll(); level.add(head.val); if (head.left != null) { queue.offer(head.left); } if (head.right != null) { queue.offer(head.right); } } result.add(level); } return result; }
8.二叉树深度优先遍历
深度优先搜索(Depth First Search)是沿着树的深度遍历树的节点,尽可能深的搜索树的分支。深度优先搜索二叉树是先访问根结点,然后遍历左子树接着是遍历右子树,因此我们可以利用堆栈的先进后出的特点,
先将右子树压栈,再将左子树压栈,这样左子树就位于栈顶,可以保证结点的左子树先与右子树被遍历。
//深度优先搜索 //利用栈, 先将右子树压入栈, 再将左子树压入栈 public static void DFS(TreeNode root){ Stack<TreeNode> stack = new Stack<TreeNode>(); stack.push(root); while(!stack.empty()){ TreeNode node = stack.peek(); System.out.println(node.val); stack.pop(); if(node.right != null){ stack.push(node.right); } if(node.left != null){ stack.push(node.left); } } }
9.二叉树最大深度
给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的距离。
样例
给出一棵如下的二叉树:
1 / \ 2 3 / \ 4 5
这个二叉树的最大深度为3.
public static int maxDepth(TreeNode root){ if(root == null){ return 0; } //最大深度 + 1 return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; }
10.二叉树的最近公共祖先
给定一棵二叉树,找到两个节点的最近公共父节点(LCA)。最近公共祖先是两个节点的公共的祖先节点且具有最大深度。
注意事项
假设给出的两个节点都在树中存在
样例
对于下面这棵二叉树
4 / \ 3 7 / \ 5 6 LCA(3, 5) = 4 LCA(5, 6) = 7 LCA(6, 7) = 7
// 在root为根的二叉树中找A,B的LCA: // 如果找到了就返回这个LCA // 如果只碰到A,就返回A // 如果只碰到B,就返回B // 如果都没有,就返回null public TreeNode lowestCommonAncestor(TreeNode root, TreeNode node1, TreeNode node2) { if (root == null || root == node1 || root == node2) { return root; } // Divide TreeNode left = lowestCommonAncestor(root.left, node1, node2); TreeNode right = lowestCommonAncestor(root.right, node1, node2); // Conquer if (left != null && right != null) { return root; } if (left != null) { return left; } if (right != null) { return right; } return null; }
11.二叉树的镜像
非递归实现
即使用循环实现public static void mirrorNonRecurively(TreeNode root){ if(root == null) return; java.util.Stack<TreeNode> stack = new java.util.Stack<TreeNode>(); stack.push(root); while(stack.size() > 0){ TreeNode node = stack.pop(); if(null != node.left || null != node.right){ TreeNode temp = node.left; node.left = node.right; node.right = temp; } if(null != node.left){ stack.push(node.left); } if(null != node.right){ stack.push(node.right); } } }