二叉树练习
- 一、选择题
- 1. 某二叉树共有 399 个结点,其中有 199 个度为 2 的结点,则该二叉树中的叶子结点数为( )
- 2.在具有 2n 个结点的完全二叉树中,叶子结点个数为( )
- 3.一棵完全二叉树的节点数位为531个,那么这棵树的高度为( )
- 4.一个具有767个节点的完全二叉树,其叶子节点个数为()
- 5. 某完全二叉树按层次输出(同一层从左到右)的序列为 ABCDEFGH 。该完全二叉树的前序序列为()
- 6. 二叉树的先序遍历和中序遍历如下:先序遍历:EFHIGJK;中序遍历:HFIEJKG.则二叉树根结点为()
- 7. 设一课二叉树的中序遍历序列:badce,后序遍历序列:bdeca,则二叉树前序遍历序列为()
- 8. 某二叉树的后序遍历序列与中序遍历序列相同,均为 ABCDEF ,则按层次输出(同一层从左到右)的序列为()
- 二、OJ题目
一、选择题
1. 某二叉树共有 399 个结点,其中有 199 个度为 2 的结点,则该二叉树中的叶子结点数为( )
A 不存在这样的二叉树
B 200
C 198
D 199
正确答案:B
解析:叶子结点的个数等于度为2的结点数+1.
2.在具有 2n 个结点的完全二叉树中,叶子结点个数为( )
A n
B n+1
C n-1
D n/2
正确答案:A
解析:假设叶子结点的个数为x,那么度为2的结点的个数为x-1,叶子结点和度为2的结点的和为2n-1,因此:
x+x-1=2n-1
x=n
3.一棵完全二叉树的节点数位为531个,那么这棵树的高度为( )
A 11
B 10
C 8
D 12
正确答案:B
解析:利用公式高度=
l
o
g
2
(
531
+
1
)
log_2(531+1)
log2(531+1),向上取整,结果为10。
4.一个具有767个节点的完全二叉树,其叶子节点个数为()
A 383
B 384
C 385
D 386
正确答案:B
解析:假设叶子结点个数为x个,度为2的结点个数为x-1个,x+x-1=767,解得x=384.
5. 某完全二叉树按层次输出(同一层从左到右)的序列为 ABCDEFGH 。该完全二叉树的前序序列为()
A: ABDHECFG
B: ABCDEFGH
C: HDBEAFCG
D: HDEBFGCA
正确答案:A
6. 二叉树的先序遍历和中序遍历如下:先序遍历:EFHIGJK;中序遍历:HFIEJKG.则二叉树根结点为()
A: E
B: F
C: G
D: H
正确答案:A
7. 设一课二叉树的中序遍历序列:badce,后序遍历序列:bdeca,则二叉树前序遍历序列为()
A: adbce
B: decab
C: debac
D: abcde
正确答案:D
解析:
8. 某二叉树的后序遍历序列与中序遍历序列相同,均为 ABCDEF ,则按层次输出(同一层从左到右)的序列为()
A: FEDCBA
B: CBAFED
C: DEFCBA
D: ABCDEF
正确答案:A
解析:
二、OJ题目
1. 检查两颗树是否相同
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
//总共有四种情况,两棵都空,其中一棵为空,两棵都非空
if(p==null&&q==null){
//两棵树都为空
return true;
}
if(p==null&&q!=null){
return false;
}else if(p!=null&&q==null){
return false;
}else{
return p.val==q.val&&isSameTree(p.left,q.left)&& isSameTree(p.right,q.right);
}
}
}
2. 另一颗树的子树
另一棵树的子树
利用上面写的相同的树的函数
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
//总共有四种情况,两棵都空,其中一棵为空,两棵都非空
if(p==null&&q==null){
//两棵树都为空
return true;
}
if(p==null&&q!=null){
return false;
}else if(p!=null&&q==null){
return false;
}else{
return p.val==q.val&&isSameTree(p.left,q.left)&& isSameTree(p.right,q.right);
}
}
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if(root==null&&subRoot==null){
return true;
}
if(root!=null&&subRoot==null){
//空树一定是另一棵树的子树
return true;
}
if(root==null&& subRoot!=null){
return false;
}
//两棵树均存在
//检测是否为同一棵树
if(isSameTree(root,subRoot)){
return true;
}
//不是同一棵树,看左子树是不是,再看右子树
return isSubtree(root.left,subRoot)||isSubtree(root.right,subRoot);
}
}
3. 判断一颗二叉树是否是平衡二叉树
class Solution {
public int height(TreeNode root){
if(root==null){
return 0;
}
int LeftHeight=height(root.left);
int RightHeight=height(root.right);
return LeftHeight>RightHeight?LeftHeight+1:RightHeight+1;
}
public boolean isBalanced(TreeNode root) {
if(root==null){
return true;
}
int LeftHeight=height(root.left);
int RightHeight=height(root.right);
if(Math.abs(LeftHeight-RightHeight)>1){
return false;
}
return isBalanced(root.left)&&isBalanced(root.right);
}
}
4. 对称二叉树
class Solution {
public boolean isSymmetric(TreeNode ll,TreeNode rr){
if(ll==null&&rr==null){
return true;
}
//两棵树中有一棵空
if(ll==null||rr==null){
return false;
}
return ll.val==rr.val&& isSymmetric(ll.left,rr.right)&&isSymmetric(ll.right,rr.left);
}
public boolean isSymmetric(TreeNode root) {
if(root==null){
return true;
}
return isSymmetric(root.left,root.right);
}
}
5. 二叉树的构建及遍历
//IO类型的OJ题目
//1.需要创建Main类
//2.需要提供一个main方法
//3.需要循环接收每个测试用例
//4.需要用户自己导入所需要用到的包
import java.util.Scanner;
public class Main{
//对二叉树的结点进行定义
public static class TreeNode{
char value;
TreeNode left;
TreeNode right;
public TreeNode(char value){
this.value=value;
}
}
//指向二叉树的根结点
TreeNode root;
int index=0;
void createBinaryTree(String prestr,char invalid){
index=0;
root=createBinaryTreeN(prestr,invalid);
}
TreeNode createBinaryTreeN(String prestr,char invalid){
TreeNode treeRoot = null;
if(index<prestr.length() && prestr.charAt(index)!=invalid){
//1.创建根结点
treeRoot=new TreeNode(prestr.charAt(index));
//2.创建根结点的左子树
++index;
treeRoot.left=createBinaryTreeN(prestr,invalid);
//3.创建根结点的右子树
++index;
treeRoot.right=createBinaryTreeN(prestr,invalid);
}
return treeRoot;
}
public void InOrder(){
InOrder(root);
System.out.println();
}
private void InOrder(TreeNode treeRoot){
if(treeRoot!=null){
InOrder(treeRoot.left);
System.out.print(treeRoot.value+" ");
InOrder(treeRoot.right);
}
}
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
while(sc.hasNext()){
//接收前序遍历的结果
String str=sc.nextLine();
Main tree=new Main();
tree.createBinaryTree(str,'#');
tree.InOrder();
}
}
}
6. 二叉树的分层遍历
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
//1.申请ArrayList的对象用来保存该层的结点
//2.以循环的方式将该层的结点整体遍历完,----下一层的所有结点已经全部入队列
//3.将ArrayList中保存的该层遍历结果保存起来
List<List<Integer>> list=new ArrayList<>();
if(root==null){
return list;
}
//借助队列对二叉树完成分层遍历
Queue<TreeNode> q=new LinkedList<>();
q.offer(root);
while(!q.isEmpty()){
//队列中保存的就是同一层的结点
//一次性将该层的结点遍历完
int levelsize=q.size();
List<Integer> levelval=new ArrayList<>();
for(int i=0;i<levelsize;i++){
TreeNode cur=q.poll();
levelval.add(cur.val);
if(cur.left!=null){
q.offer(cur.left);
}
if(cur.right!=null){
q.offer(cur.right);
}
}
list.add(levelval);
}
return list;
}
}
7. 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先
找公共祖先OJ链接
面试中要和面试官讨论:
- 如果树是采用双亲表示法或者孩子双亲表示法
求最近公共祖先问题:转化为两个链表相交求交点 - 二叉搜索树结构:
最左侧结点最小,最右侧结点最大。
借鉴第一种方式解题:
class Solution {
//获取从根到某个结点对应路径中包含的所有结点
boolean getNodePath(TreeNode root,Stack<TreeNode>spath,TreeNode node){
if(root==null||node==null){
return false;
}
spath.push(root);
if(root==node){
return true;
}
if(getNodePath(root.left,spath,node)){
return true;
}
if(getNodePath(root.right,spath,node)){
return true;
}
spath.pop();
return false;
}
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null||p==null||q==null){
return null;
}
//找从root到p或q对应路径中包含的所有结点
Stack<TreeNode> ppath=new Stack<>();
Stack<TreeNode> qpath=new Stack<>();
getNodePath(root,ppath,p);
getNodePath(root,qpath,q);
int psize=ppath.size();
int qsize=qpath.size();
while(!ppath.empty()&&!qpath.empty()){
if(ppath.peek()==qpath.peek()){
return ppath.peek();
}
if(psize>qsize){
ppath.pop();
psize--;
}else if(psize<qsize){
qpath.pop();
qsize--;
}else{
ppath.pop();
qpath.pop();
psize--;
qsize--;
}
}
return null;
}
}
8. 根据一棵树的前序遍历与中序遍历构造二叉树
解题思路:
- 从前序遍历结果中找二叉树的根结点
- 在中序遍历结果中找根结点的位置pos,在pos左侧的元素就是根的左子树,在pos右侧的元素就是根的右子树,以pos为分界点:将中序遍历结果划分成两个区间。
- 还原根结点,递归还原根的左子树------根据中序划分的左子树区间,递归还原根的右子树----根据中序划分根的右子树区间。
class Solution {
int index=0;
//最后要返回根结点
TreeNode reBuilderTree(int[] preorder,int[] inorder,int left,int right){
//给递归设置一个出口
if(index>=preorder.length||left>=right){
return null;
}
//1.从前序遍历结果中确定根 index的位置就是根的位置
//preorder[index]--就是根
//2.从中序遍历结果中找根的位置pos
int pos=left;
while(pos<right){
if(inorder[pos]==preorder[index]){
break;
}
pos++;
}
//还原根结点
TreeNode root=new TreeNode(preorder[index]);
index++;
//递归还原根的左子树
root.left=reBuilderTree(preorder,inorder,left,pos);
//递归还原根的右子树
root.right=reBuilderTree(preorder,inorder,pos+1,right);
return root;
}
public TreeNode buildTree(int[] preorder, int[] inorder) {
index=0;
return reBuilderTree(preorder,inorder,0,inorder.length);
}
}
9. 根据一棵树的中序遍历与后序遍历构造二叉树
class Solution {
int index=0;
TreeNode rebuildTree(int[] postorder,int[] inorder,int left,int right){
if(index<0 || left>=right){
return null;
}
//1.首先在后序结果中确定根结点
//postorder[index]就是根
//2.在中序序列中找根的位置pos,确定根的左右子树
int pos=left;//从左边界开始找
while(pos < right){
if(postorder[index] == inorder[pos]){
break;
}
pos++;
}
//还原根结点
TreeNode root=new TreeNode(postorder[index]);
index--;
//递归还原根的右子树
root.right=rebuildTree(postorder,inorder,pos+1,right);
//递归还原根的左子树
root.left=rebuildTree(postorder,inorder,left,pos);
return root;
}
public TreeNode buildTree(int[] inorder, int[] postorder) {
index=postorder.length-1;
return rebuildTree(postorder,inorder,0,inorder.length);
}
}
10. 二叉树创建字符串
二叉树创建字符串
你需要采用前序遍历的方式,将一个二叉树转换成一个由括号和整数组成的字符串。
空节点则用一对空括号 “()” 表示。而且你需要省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对。
class Solution {
//要使用递归,所以重新封装一个方法
void tree2str(TreeNode root, StringBuilder sb){
if(root==null){
return;
}
//先转化根结点
sb.append(root.val);
//递归转化根结点的左子树
if(root.left==null){
if(root.right==null){
return;
}else{
sb.append("()");
}
}else{
sb.append('(');
tree2str(root.left,sb);
sb.append(')');
}
//递归转化根结点的右子树
if(root.right==null){
return;
}else{
sb.append('(');
tree2str(root.right,sb);
sb.append(')');
}
}
public String tree2str(TreeNode root) {
StringBuilder sb=new StringBuilder();
tree2str(root,sb);
return sb.toString();
}
}
11. 二叉树前序非递归遍历实现
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list=new ArrayList<>();
if(root==null){
return list;
}
Stack<TreeNode> s=new Stack<>();
TreeNode cur=root;
while(!s.empty()||cur!=null){
//从上往下 遍历最左侧路径中的每个结点,并将其右子树保存起来
while(cur!=null){
list.add(cur.val);
if(cur.right!=null){
s.push(cur.right);
}
cur=cur.left;
}
if(!s.empty()){
cur=s.pop();
}
}
return list;
}
}
12. 二叉树中序非递归遍历实现
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list=new ArrayList<>();
if(root==null){
return list;
}
TreeNode cur=root;
Stack <TreeNode> s=new Stack<>();
while(cur!=null||!s.empty()){
//1.找当前二叉树最左侧的结点,并将路径中遇到的所有结点都保存起来
while(cur!=null){
s.push(cur);
cur=cur.left;
}
cur=s.pop();
list.add(cur.val);
cur=cur.right;
}
return list;
}
}
13. 二叉树后序非递归遍历实现
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list=new ArrayList<>();
if(root==null){
return list;
}
Stack <TreeNode> s=new Stack<>();
TreeNode cur=root;
TreeNode prev = null ; //用来标记刚刚遍历过的结点
//先找到最左侧结点
while(!s.empty()||cur!=null){
while(cur!=null){
s.push(cur);
cur=cur.left;
}
TreeNode top=s.peek();
if(top.right==null||top.right==prev){
list.add(top.val);//右子树不存在就可以直接遍历根结点,或者右子树已经遍历了
prev=top;
s.pop();
}else{
cur=top.right;
}
}
return list;
}
}