收集整理一些关于二叉树的笔试面试题。
二叉树的数据结构:
struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; };
1. 二叉树的深度
最简单的方法便是递归法。
int TreeDepth(TreeNode *proot) { if(!pRoot) return 0; return max(1+TreeDepth(pRoot->left), TreeDepth(pRoot->right)); }
2. 二叉树的遍历
这里仅给出中序遍历。
2.1 非递归遍历
思路:对于任一结点P,
1) 若其左孩子不为空,则将P入栈并将P的左孩子置为当前的P,然后对当前结点P再进行相同的处理;
2) 若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的P置为栈顶结点的右孩子;
3) 直到P为NULL并且栈为空则遍历结束。
void inOrder(TreeNode* root) { stack<TreeNode*> s; TreeNode *p=root; while(p!=NULL||!s.empty()) { while(p!=NULL) { s.push(p); p=p->left; } if(!s.empty()) { p=s.top(); cout<<p->val<<" "; s.pop(); p=p->right; } } }
2.2 递归遍历
void inOrder(TreeNode* root) { if(root!=NULL) { inOrder(root->left); cout<<root->val<<" "; inOrder(root->right); } }
3. 二叉树的层序遍历
将遍历的结果放入vector中。
vector<int> PrintFromTopToBottom(TreeNode* root) { vector<int> vec; if(root==nullptr) return vec; //指针推荐这种写法 布尔值这样写:if(!root) queue<TreeNode*> Q ; TreeNode* p = nullptr; Q.push(root); while(!Q.empty()){ //empty()返回布尔值 所以这样写 p = Q.front(); Q.pop(); vec.push_back(p->val); if(p->left != nullptr) Q.push(p->left); if(p->right != nullptr) Q.push(p->right); } return vec; }
延伸:二叉树层序遍历,将树的每一层的结果保存为一行。
vector<vector<int> > Print(TreeNode* pRoot) { vector<vector<int> > res; if(pRoot==nullptr) return res; queue<TreeNode*> q; q.push(pRoot); while(!q.empty()) { int lo = 0, hi = q.size(); //使用lo和hi提取行号 vector<int> c; while(lo++ < hi) { TreeNode *t = q.front(); q.pop(); c.push_back(t->val); if(t->left) q.push(t->left); if(t->right) q.push(t->right); } res.push_back(c); } return res; }
4. 求二叉树的镜像
操作给定的二叉树,将其变换为源二叉树的镜像。
void Mirror(TreeNode *pRoot) { if(pRoot==NULL){ return; } TreeNode *tmp = pRoot->left; pRoot->left = pRoot->right; pRoot->right = tmp; Mirror(pRoot->left); Mirror(pRoot->right); }
延伸:判断两个二叉树是否对称,即其中一个是否是另一个的镜像。
思路:
/*首先根节点以及其左右子树,左子树的左子树和右子树的右子树相同
* 左子树的右子树和右子树的左子树相同即可,采用递归
* 非递归也可,采用栈或队列存取各级子树根节点
*/
bool isSymmetrical(TreeNode* pRoot) { return isSymmetrical(pRoot,pRoot); } bool isSymmetrical(TreeNode* pRoot1, TreeNode* pRoot2) { if(pRoot1==NULL && pRoot2==NULL) return true; if(pRoot1==NULL || pRoot2==NULL) return false; if(pRoot1->val != pRoot2->val) return false; return isSymmetrical(pRoot1->left, pRoot2->right) && isSymmetrical(pRoot1->right, pRoot2->left); }
5. 给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4
int count = 0; TreeNode* KthNode(TreeNode* pRoot, int k) { if(pRoot){ TreeNode* ret = KthNode(pRoot->left,k); if(ret) return ret; //此判断为了确保在找到(count == k)的时候能逐层返回 if(++count==k) return pRoot; ret = KthNode(pRoot->right,k); if(ret) return ret; } return nullptr; }
6. 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
public class TreeLinkNode { int val; TreeLinkNode left = null; TreeLinkNode right = null; TreeLinkNode next = null; TreeLinkNode(int val) { this.val = val; } }
分析二叉树的下一个节点,一共有以下情况:
1.二叉树为空,则返回空;
2.节点右孩子存在,则设置一个指针从该节点的右孩子出发,一直沿着指向左子结点的指针找到的叶子节点即为下一个节点;
3.节点不是根节点。如果该节点是其父节点的左孩子,则返回父节点;否则继续向上遍历其父节点的父节点,重复之前的判断,返回结果。
public TreeLinkNode GetNext(TreeLinkNode pNode)
{
if(pNode == null) return null;
if(pNode.right!=null){ //如果有右子树,则找右子树的最左节点
pNode = pNode.right;
while(pNode.left!=null)
pNode = pNode.left;
return pNode;
}
while(pNode.next!=null){
//没有右子树,如果该节点是其父节点的左孩子,则返回父节点;
//否则继续向上遍历其父节点的父节点,重复之前的判断,返回结果。
if(pNode.next.left==pNode)
return pNode.next;
pNode = pNode.next;
}
return null;
}