二叉树中常见的面试题
1 用一个函数判断一棵树是否平衡
题目:实现一个函数检查一棵树是否平衡。对于这个问题而言, 平衡指的是这棵树任意两个叶子结点到根结点的距离之差不大于1。
注意,对于这道题,要审清题意。它并不是让你判断一棵树是否为平衡二叉树。平衡二叉树的定义为:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1, 并且左右两个子树都是一棵平衡二叉树。 而本题的平衡指的是这棵树任意两个叶子结点到根结点的距离之差不大于1。(程序员面试金典)
思路:
对于本题,只需要求出离根结点最近和最远的叶子结点, 然后看它们到根结点的距离之差是否大于1即可。
C++实现代码:
//求最大高度
int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
//求最小高度
int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
return 1 + Math.min( minDepth(root.left), minDepth(root.right));
}
public static boolean isBalanced(TreeNode root){
return (maxDepth(root) - minDepth(root) <= 1);
}
2 求二叉树的深度(剑指offer)
1)递归版本
int TreeDepth(BinaryTreeNode* pRoot)
{
if(pRoot==NULL)
return;
int nLeft=TreeDepth(pRoot->m_pLeft);
int nRight=TreeDepth(pRoot->m_pRight);
return (nLeft>nRight)?(nLeft+1):(nRight+1);
}
2)非递归版本
int maxDepth(TreeNode *root) {
if(root==NULL)
return 0;
queue<TreeNode*> q;
q.push(root);
int cur=1;
int node=0;
int level=0;
while(!q.empty())
{
while(cur)
{
TreeNode* tmp=q.front();
cur--;
q.pop();
if(tmp->left)
{
node++;
q.push(tmp->left);
}
if(tmp->right)
{
node++;
q.push(tmp->right);
}
}
level++; //每次遍历完一层之后,层数加1
cur=node;
node=0;
}
return level;
}
求二叉树的叶子节点到根的最小高度
int minDepth(BinaryTreeNode *root)
{
if(root==NULL)
return 0;
int left=minDepth(root->left);
int right=minDepth(root->right);
if(left==0&&right==0)
return 1;
else if(left==0)
return right+1;
else if(right==0)
return left+1;
else
return min(left,right)+1;
}
3 判断一棵树是否为平衡二叉树。(剑指offer)
方法一:
bool IsBalanced(BinaryTreeNode* root)
{
if(root==NULL)
return true;
int left=TreeDepth(root->left);
int right=TreeDepth(root->right);
int diff=left-right;
if(diff>1||diff<-1)
return false;
return IsBalanced(root->left)&&IsBalanced(root->right);
}
方法二:
如果我们用后序遍历的方式遍历二叉树的每一个节点,在遍历到一个结点之前我们就已经遍历了它的左右子树。只要在遍历每个结点的时候记录它的深度(某一结点的深度等于它到叶节点的路径的长度),我们就可以一边遍历一边判断每个结点是不是平衡的。下面是参考代码:
bool IsBalanced(BinaryTreeNode* root,int *pDepth)
{
if(root==NULL)
{
*pDepth=0;
return true;
}
int left,right;
if(IsBalanced(root->left,&left)&&IsBalanced(root->right,&right)
{
int diff=left-right;
if(diff<=1&&diff>=-1)
{
*pDepth=1+(left>right?left:right);
return true;
}
}
return false;
}
方法三:
从根节点递归向下检查每颗子树的高度,我们会通过checkHeight方法,以递归方式获取每个结点左右子树的高度。若子树是平衡的,则返回孩子树的实际高度,若子树不平衡,则返回-1.
int checkHeight(TreeNode *root)
{
if(root==NULL)
return 0;
int leftHeight=checkHeight(root->left);
if(leftHeight==-1)
return -1;
int rightHeight=checkHeight(root->right);
if(rightHeight==-1)
return -1;
int diff=abs(leftHeight-rightHeight);
if(diff>1)
return -1;
else
return max(leftHeight,rightHeight)+1;
}
bool isAVL(TreeNode* root)
{
if(checkHeight(root)==-1)
return false;
else
return true;
}
4 将一颗二叉树按照先序遍历顺序展开成一个单链表。(leetcode)
思路一:
将所有的树节点按照先序遍历,并将结果存放在vector中,然后将vector中的结点使用right指针链接起来。
C++实现代码:
#include<iostream>
#include<new>
#include<vector>
#include<stack>
using namespace std;
//Definition for binary tree
struct TreeNode
{
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution
{
public:
void flatten(TreeNode *root)
{
if(root==NULL)
return;
vector<TreeNode*> vec;
preorder(root,vec);
TreeNode *tmp=root;
size_t i;
for(i=1; i<vec.size(); i++)
{
//一定要记得将左指针置为NULL
tmp->left=NULL;
tmp->right=vec[i];
tmp=tmp->right;
}
tmp->left=tmp->right=NULL;
}
void preorder(TreeNode *root,vector<TreeNode*> &vec)
{
stack<TreeNode*> s;
while(root||!s.empty())
{
while(root)
{
vec.push_back(root);
s.push(root);
root=root->left;
}
if(!s.empty())
{
root=s.top();
s.pop();
root=root->right;
}
}
}
void createTree(TreeNode *&root)
{
int i;
cin>>i;
if(i!=0)
{
root=new TreeNode(i);
if(root==NULL)
return;
createTree(root->left);
createTree(root->right);
}
}
};
int main()
{
Solution s;
TreeNode *root;
s.createTree(root);
s.flatten(root);
while(root)
{
cout<<root->val<<" ";
root=root->right;
}
}
思路二:
将root的右子树中的链接到左子树的最后一个结点,然后将整个左子树作为根的右子树,此时,根只有一颗右子树,root->left=NULL。然后将root移到右子树第一个节点,此时是以右子树为根节点的子树,重复上面的过程。直到为NULL。
C++实现代码:
void flatten(TreeNode *root)
{
if(root==NULL)
return;
while(root)
{
if(root->left)
{
TreeNode* pre=root->left;
while(pre->right)
pre=pre->right;
pre->right=root->right;
root->right=root->left;
root->left=NULL;
}
root=root->right;
}
}
5 将二叉搜索树转换成双向链表。(剑指offer)
思路:
我们可以中序遍历整棵树。按照这个方式遍历,比较小的结点先访问。如果我们没访问一个结点,假设之前访问过的结点已经调整成一个排序双向链表,我们再把调整当前结点的指针将其连接到链表的末尾。当所有结点都访问过之后,整棵树也就转换成一个排序双向链表了。
BinaryTreeNode* Convert(BinaryTreeNode* pRootOfTree)
{
BinaryTreeNode* pLastNodeInList=NULL;
ConvertNode(pRootOfTree,&pLastNodeInList);
BinaryTreeNode* pHeadOfList=pLastNodeInList;
while(pHeadOfList!=NULL&&pHeadOfList->left)
pHeadOfList=pHeadOfList->left;
return pHeadOfList;
}
void ConvertNode(BinaryTreeNode* pNode,BinaryTreeNode*&pLastNodeInList)
{
if(pNode==NULL)
return;
BinaryTreeNode *pCurrent=pNode;
if(pCurrent->left)
ConvertNode(pNode->left,pLastNodeInList);
pCurrent->left=pLastNodeInList;;
if(pLastNodeInList!=NULL)
pLastNodeInList->right=pCurrent;
//pLastNodeInList始终指向链表中存在的最后一个元素。
pLastNodeInList=pCurrent;
if(pCurrent->right)
ConvertNode(pCurrent->right,pLastNodeInList);
}
6 普通二叉树的创建和排序二叉树的创建
//一般二叉树
void createTree(TreeNode *&root)
{
int i;
cin>>i;
if(i==0)
return;
TreeNode *tmp=new TreeNode(i);
if(root==NULL)
root=tmp;
createTree(root->left);
createTree(root->right);
}
//插入
void Insert(TreeNode *&root,int val)
{
TreeNode* tmp=new TreeNode(val);
if(root==NULL)
root=tmp;
else if(val<root->val)
Insert(root->left,val);
else if(val>root->val)
Insert(root->right,val);
else
return;
}
//二叉排序树
void createSort(TreeNode *&root)
{
int arr[10]= {8,3,5,1,9,2,6,7,10,4};
for(auto a:arr)
Insert(root,a);
}
7 二叉树的递归遍历(前序、中序和后序)
//二叉树的遍历(递归)
void recursivePreorder(TreeNode *root)
{
if(root)
{
cout<<root->val<<' ';
recursivePreorder(root->left);
recursivePreorder(root->right);
}
}
void recursiveInorder(TreeNode *root)
{
if(root)
{
recursiveInorder(root->left);
cout<<root->val<<' ';
recursiveInorder(root->right);
}
}
void recursivePostorder(TreeNode *root)
{
if(root)
{
recursivePostorder(root->left);
recursivePostorder(root->right);
cout<<root->val<<' ';
}
}
8 二叉树的非递归遍历(前序、中序、后序和层次遍历)
//二叉树遍历(非递归)
void preOrder(TreeNode *root)
{
stack<TreeNode*> st;
while(root||!st.empty())
{
if(root)
{
st.push(root);
cout<<root->val<<' ';
root=root->left;
}
else
{
root=st.top();
st.pop();
if(root)
root=root->right;
}
}
}
void inOrder(TreeNode *root)
{
stack<TreeNode*> st;
while(root||!st.empty())
{
while(root)
{
st.push(root);
root=root->left;
}
if(!st.empty())
{
root=st.top();
cout<<root->val<<' ';
st.pop();
if(root)
root=root->right;
}
}
}
void postOrder(TreeNode *root)
{
TreeNode *cur;
TreeNode *pre=NULL;
stack<TreeNode*> st;
if(root)
st.push(root);
while(!st.empty())
{
cur=st.top();
if(cur->left==NULL&&cur->right==NULL||pre&&(cur->left==pre||cur->right==pre))
{
cout<<cur->val<<' ';
pre=cur;
st.pop();
}
else
{
if(cur->right)
{
st.push(cur->right);
}
if(cur->left)
{
st.push(cur->left);
}
}
}
}
//层次遍历
void levelOrder(TreeNode *root)
{
queue<TreeNode*> q;
if(root)
q.push(root);
int cur=1;
while(!q.empty())
{
int count=0;
while(cur)
{
TreeNode* tmp=q.front();
cout<<tmp->val<<' ';
cur--;
q.pop();
if(tmp->left)
{
q.push(tmp->left);
count++;
}
if(tmp->right)
{
q.push(tmp->right);
count++;
}
}
cout<<endl;
cur=count;
}
}
9 二叉树叶子节点的数目
//叶子节点的个数
int leafCounts(TreeNode *root)
{
if(root==NULL)
return 0;
if(root->left==NULL&&root->right==NULL)
return 1;
return leafCounts(root->left)+leafCounts(root->right);
}
直接求所有节点的总数
//节点总数
int counts(TreeNode* root)
{
if(root==NULL)
return 0;
int leftCounts=counts(root->left);
int rightCounts=counts(root->right);
return leftCounts+rightCounts+1;
}
10 判断是否为二叉树的子结构(剑指offer)
bool isSub(TreeNode* root,TreeNode *sroot)
{
if(sroot==NULL)
return true;
if(root==NULL)
return false;
if(root->val!=sroot->val)
return false;
return isSub(root->left,sroot->left)&&isSub(root->right,sroot->right);
}
//二叉树的子结构
bool isSubStruct(TreeNode *root,TreeNode *sroot)
{
if(root==NULL||sroot==NULL)
return false;
bool result=false;
if(root->val==sroot->val)
result=isSub(root,sroot);
if(!result)
result=isSubStruct(root->left,sroot);
if(!result)
result=isSubStruct(root->right,sroot);
return result;
}
11 判断是否为镜像
//二叉树的镜像
bool isMirror(TreeNode *rootA,TreeNode *rootB)
{
if(rootA==NULL&&rootB==NULL)
return true;
if(rootA==NULL||rootB==NULL)
return false;
if(rootA->val!=rootB->val)
return false;
return isMirror(rootA->left,rootB->right)&&isMirror(rootA->right,rootB->left);
}
12 判断是否为完全二叉树
//判断是否为完全二叉树
bool isCompleteTree(TreeNode* root)
{
if(root==NULL)
return true;
//tag=0表示还有节点,否则tag=1表示后面不能有节点了
int tag=0;
queue<TreeNode*> q;
q.push(root);
while(!q.empty())
{
TreeNode *tmp=q.front();
q.pop();
if(tmp->left&&!tag)
q.push(tmp->left);
else if(tmp->left&&tag)
return false;
else if(!tmp->left)
tag=1;
if(tmp->right&&!tag)
q.push(tmp->right);
else if(tmp->right&&tag)
return false;
else if(!tmp->right)
tag=1;
}
return true;
}
13 重建二叉树(剑指offer)
//重建二叉树
TreeNode* rebulidTree(int *pre,int *in,int len)
{
if(pre==NULL||in==NULL||len<=0)
return NULL;
int i;
int val=*pre;
TreeNode *root=new TreeNode(val);
i=0;
while(i<len&&in[i]!=val) i++;
if(i==len)
return NULL;
root->left=rebulidTree(pre+1,in,i);
root->right=rebulidTree(pre+1+i,in+i+1,len-i-1);
return root;
}
14 判断是否为后序遍历(剑指offer)
//判断是否为后序遍历
bool isPostOrder(int *post,int len)
{
if(post==NULL||len<=0)
return false;
int val=post[len-1];
int i=0,j;
while(i<len-1&&post[i]<val) i++;
j=i;
while(i<len-1)
{
if(post[i]<val)
return false;
i++;
}
bool left=true;
if(j>0)
left=isPostOrder(post,j);
bool right=true;
if(j<len-1)
right=isPostOrder(post+j,len-j-1);
return left&&right;
}
15 二叉树结点间的最大距离(只是算路径的长度)参考:http://blog.csdn.net/lalor/article/details/7626678
//二叉树结点间的最大距离
int maxdistance(TreeNode *root,int &ans)
{
if(root==NULL)
return 0;
int left=0;
int right=0;
//左子树不为空,返回当前结点到左子树的最大深度
if(root->left)
left=maxdistance(root->left,ans)+1;
//右子树不为空,返回当前结点到右子树的最大深度
if(root->right)
right=maxdistance(root->right,ans)+1;
int ret=max(left,right);
ans=max(ans,left+right);
return ret;
}
int maxNodeDistance(TreeNode *root)
{
int ans=0;
maxdistance(root,ans);
return ans;
}
16 二叉树的最大路径和(leetcode)
//二叉树的最大路径和
int maxPath(TreeNode *root,int &sum)
{
if(root==NULL)
return 0;
int ret;
int left=maxPath(root->left,sum);
int right=maxPath(root->right,sum);
ret=max(root->val,max(left,right)+root->val);
sum=max(sum,max(ret,left+right+root->val));
return ret;
}
int maxPathSum(TreeNode* root)
{
int sum=0;
maxPath(root,sum);
return sum;
}
17 所有路径和为给定值(leetcode)
//二叉树路径和为给定值的所有路径
void findPath(TreeNode *root,vector<vector<int>> &res,vector<int> &path,int target)
{
if(root==NULL)
return;
path.push_back(root->val);
if(root->left==NULL&&root->right==NULL&&target==root->val)
{
res.push_back(path);
return;
}
if(root->left)
{
findPath(root->left,res,path,target-root->val);
}
if(root->right)
{
findPath(root->right,res,path,target-root->val);
}
path.pop_back();
}
vector<vector<int> > pathSum(TreeNode *root,int target)
{
vector<vector<int> > res;
vector<int> path;
findParent(root,res,path,target);
return res;
}
18 二叉树的最低公共父节点(cc)
bool father(TreeNode* n1,TreeNode* n2)
{
if(n1==NULL)
return false;
if(n1==n2)
return true;
return father(n1->left,n2)||father(n1->right,n2);
}
//二叉树的最低公共祖先
TreeNode* findParent(TreeNode *root,TreeNode *n1,TreeNode *n2,TreeNode *&parent)
{
if(root==NULL)
return NULL;
if(father(root,n1)&&father(root,n2))
{
parent=root;
findParent(root->left,n1,n2,parent);
findParent(root->right,n1,n2,parent);
}
return parent;
}
19 判断一棵树是否为二叉搜索树
int isBST2(struct node* node)
{
return(isBSTUtil(node, INT_MIN, INT_MAX));
}
/*
给定的二叉树是BST则返回true,且它的值 >min 以及 < max.
*/
int isBSTUtil(struct node* node, int min, int max)
{
if (node==NULL) return(true);
// 如果不满足min和max约束,返回false
if (node->data<=min || node->data>=max) return(false);
// 递归判断左右子树是否满足min和max约束条件
return
isBSTUtil(node->left, min, node->data) &&
isBSTUtil(node->right, node->data, max)
);
}
20 求二叉树第K层的节点个数
递归解法:
(1)如果二叉树为空或者k<1返回0
(2)如果二叉树不为空并且k==1,返回1
(3)如果二叉树不为空且k>1,返回左子树中k-1层的节点个数与右子树k-1层节点个数之和
参考代码如下:
int getNode(TreeNode *root,int k)
{
if(root==NULL||k<1)
return 0;
if(k==1)
return 1;
int leftNum=getNode(root->left,k-1);
int rightNum=getNode(root->right,k-1);
return leftNum+rightNum;
}