二叉树是面试中容易出现的题目,也是较难的题目,因为二叉树的算法常常涉及递归,而且比较有特点。
所以面对二叉树的技巧是,掌握基本的算法,以及常见的二叉树难题,对于其他问题,在基本算法上修改得到。
在面试中,最简单的题目就是二叉树的遍历问题。
一般包括二叉树的前序,中序,后序,层序遍历。其中前面三个的递归形式是很容易写出来的。
如果面试要求给出非递归实现(一般借助栈即可)
最容易实现是前序遍历,因为前序遍历与标准的DFS是非常像的,只需要使用栈,模仿层序BFS的操作即可实现,后续遍历可以在前序遍历的基础上修改,最后将答案反转一下得到。
比较难的是中序遍历,中序遍历需要用栈模拟中序遍历的过程,一直往左走,走到叶子,然后输出,再往右。
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> st;
TreeNode* now = root;
while(now || !st.empty()){
while(now){
st.push(now);
now = now->left;
}
if(!st.empty()){
now = st.top();
st.pop();
res.push_back(now->val);
now = now->right;
}
}
return res;
}
};
二叉树的层序遍历,BFS,也是一定被下来的模板
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if(root==nullptr){
return res;
}
queue<TreeNode*> q;
q.push(root);
while(!q.empty()){
vector<int> temp;
int n = q.size();
for(int i=0;i<n;i++){
TreeNode* node = q.front();
q.pop();
temp.push_back(node->val);
if(node->left){
q.push(node->left);
}
if(node->right){
q.push(node->right);
}
}
res.push_back(temp);
}
return res;
}
};
笔试(选择题)和面试中,也会涉及由前序遍历和中序遍历,构建二叉树或直接给出后续遍历的题目。
这道题目的难点,是数组起点和中点的判断,需要仔细。出错了不容易debug
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func buildTree(preorder []int, inorder []int) *TreeNode {
preEnd, inEnd := len(preorder)-1, len(inorder)-1
return helper(preorder,0,preEnd,inorder,0,inEnd)
}
func helper(preorder []int, preStart int, preEnd int,
inorder []int, inStart int, inEnd int) *TreeNode{
if preStart>preEnd{
return nil
}
var mid int
for i:=inStart;i<=inEnd;i++{
if preorder[preStart] == inorder[i]{
mid = i
break
}
}
root := &TreeNode{Val:preorder[preStart]}
left := helper(preorder,preStart+1, preStart+mid-inStart, inorder, inStart, mid-1)
right := helper(preorder,preStart+mid-inStart+1,preEnd, inorder, mid+1,inEnd)
root.Left = left
root.Right = right
return root
}
更难一点的题目是序列化一棵二叉树,即把二叉树序列化为字符串,再将二叉树反序列回来。
这里简单的做法是用前序遍历加上空结点标记,递归的进行序列化
下面是序列化函数
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
return rserialize(root,"");
}
public String rserialize(TreeNode root, String str) {
if(root==null) {
str+="null,";
}else{
str += str.valueOf(root.val) + ",";
str = rserialize(root.left, str);
str = rserialize(root.right, str);
}
return str;
}
反序列化的过程涉及到split函数,所以考虑使用Java语言实现,为了更便于递归,将数组转为链表,更容易写
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
String[] data_array = data.split(",");
// 用链表是为了方便递归
List<String> data_list = new LinkedList<String>(Arrays.asList(data_array));
return rdeserialize(data_list);
}
public TreeNode rdeserialize(List<String> list){
if (list.get(0).equals("null")) {
list.remove(0);
return null;
}
TreeNode root = new TreeNode(Integer.valueOf(list.get(0)));
list.remove(0);
root.left = rdeserialize(list);
root.right = rdeserialize(list);
return root;
}
与二叉树升级来的题目就是二叉搜索树,这个题目通常在笔试(选择题)或者面试中考察。
经典的题目有判断一棵二叉树是否是二叉搜索树
注意,二叉搜索树,一定是左子数的所有节点都要小于根节点,右子数的所有节点都要大于根节点,所以不是简单的判断一下,递归就行。必须要记录每次的最大值以及最小值。
还有一种方法是使用中序遍历来做
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isValidBST(TreeNode* root) {
return helper(root,LONG_MIN,LONG_MAX);
}
bool helper(TreeNode* root, long min, long max){
if(root==nullptr) return true;
if(root->val<=min || root->val>=max) return false;
return helper(root->left,min,root->val) && helper(root->right,root->val,max);
}
};
有特点的容易题目
101. Symmetric Tree
Easy
242151FavoriteShare
Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center).
For example, this binary tree [1,2,2,3,4,4,3]
is symmetric:
1 / \ 2 2 / \ / \ 3 4 4 3
But the following [1,2,2,null,3,null,3]
is not:
1 / \ 2 2 \ \ 3 3
Note:
Bonus points if you could solve it both recursively and iteratively
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func isSymmetric(root *TreeNode) bool {
if root == nil{
return true
}
return helper(root.Left,root.Right)
}
func helper(rootA, rootB *TreeNode) bool{
if rootA == nil && rootB == nil{
return true
}else if rootA == nil || rootB == nil{
return false
}else if rootA.Val != rootB.Val{
return false
}else{
return helper(rootA.Left,rootB.Right) && helper(rootA.Right, rootB.Left)
}
}
543. Diameter of Binary Tree
Given a binary tree, you need to compute the length of the diameter of the tree. The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or may not pass through the root.
Example:
Given a binary tree
1 / \ 2 3 / \ 4 5
Return 3, which is the length of the path [4,2,1,3] or [5,2,1,3].
Note: The length of path between two nodes is represented by the number of edges between them.
这道题目可以转会为左子数的高度加上右子数高度的最大值。所以在求树高度的逻辑中,加入全局变量,res,不断判断左子树加右子树的最大值。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int diameterOfBinaryTree(TreeNode* root) {
int res = 0;
helper(root, res);
return res;
}
int helper(TreeNode* root, int &res){
if(root==nullptr) return 0;
int left = helper(root->left,res);
int right = helper(root->right,res);
res = max(left+right,res);
return max(left,right)+1;
}
};