二叉树
1。基本概念:一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵分别称为左子树和右子树的二叉树组成
2.二叉树的特点:每个结点最多有两棵子树,即二叉树不存在度大于2的结点二叉树的子树有左右之分,其子树的次序不能颠倒
3.满二叉树在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子节点都在同一层上
4.完全二叉树:如果一棵具有N个结点的二叉树的结构与满二叉树的前N个结点的结构相同,称为完全二叉树
5。二叉树的性质
性质1:二叉树第i层上的结点数目最多为2i-1(i>=1)
性质2:深度为k的二叉树至多有2k-1个结点(k>=1)
性质3:包含n个结点的二叉树的高度至少为(log2n)+1
性质4:在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1
二叉树的面试题
1.二叉树的前序遍历
public List<Integer> preorderTraversal(TreeNode root){
List<Integer> result=new ArrayList<>();
if(root==null){
//空树返回一个结果0 而不是null
return result;
}
//访问根节点 此处访问的操作 把根节点add 到lost中
result.add(root.val);
//递归遍历左子树 ,把左子树的遍历结果放到list中
result.addAll(preorderTraversal(root.left));
//递归遍历右子树 把右子树的遍历结果 放到list中
result.addAll(preorderTraversal(root.right));
return result;
}
2.二叉树的中序遍历
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result=new ArrayList<>();
if(root==null){
return result;
}
result.addAll(inorderTraversal(root.left));
result.add(root.val);
result.addAll(inorderTraversal(root.right));
return result;
}
3.二叉树的后序遍历
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result=new ArrayList<>();
if(root==null){
return result;
}
result.addAll(postorderTraversal(root.left));
result.addAll(postorderTraversal(root.right));
result.add(root.val);
return result;
}
4.检查两棵树是否相同
public boolean isSameTree(TreeNode p, TreeNode q) {
//1.两个数都为空
if(p==null&&q==null){
return true;
}
//2.p或者q一个为空
if(p==null||q==null){
return false;
}
//3.两个都不为空的情况
if(p.val!=q.val){
return false;
}
return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right);
}
5.一棵树是否是另外一棵树的子树
public boolean isSubtree(TreeNode s, TreeNode t) {
if (s == null && t == null) {
return true;
}
if (s == null || t == null) {
return false;
}
boolean ret = false;
if (s.val == t.val) {
//如果两个树的根节点相同 判断两棵树是否是相同的树
ret = isSameTree(s, t);
}
return ret||isSubtree(s.left,t)||isSubtree(s.right,t);
}
public boolean isSameTree(TreeNode p, TreeNode q) {
//1.两个数都为空
if(p==null&&q==null){
return true;
}
//2.p或者q一个为空
if(p==null||q==null){
return false;
}
//3.两个都不为空的情况
if(p.val!=q.val){
return false;
}
return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right);
}
6.二叉树的最大深度
public int maxDepth(TreeNode root) {
if(root==null){
return 0;
}
if(root.left==null&&root.right==null){
return 1;
}
return 1+Math.max(maxDepth(root.left),maxDepth(root.right));
}
7.判断一棵树是否是平衡二叉树
public boolean isBalanced(TreeNode root) {
if(root==null){
return true;
}
if(root.left==null&&root.right==null){
return true;
}
int leftDepth=maxDepth(root.left);
int rightDepth=maxDepth(root.right);
if(leftDepth-rightDepth>1||leftDepth-rightDepth<-1){
return false;
}
return isBalanced(root.left)&&isBalanced(root.right);
}
public int maxDepth(TreeNode root){
if(root==null){
return 0;
}
if(root.left==null&&root.right==null){
return 1;
}
int leftDepth=maxDepth(root.left);
int rightDepth=maxDepth(root.right);
return 1+(Math.max(leftDepth,rightDepth));
}
8.对称二叉树
public boolean isSymmetric(TreeNode root) {
if(root==null){
return true;
}
return isMirror(root.left,root.right);
}
public boolean isMirror(TreeNode t1, TreeNode t2) {
if(t1==null&&t2==null){
return true;
}
if(t1==null||t2==null){
return false;
}
if(t1.val!=t2.val){
return false;
}
return isMirror(t1.left,t2.right)&&isMirror(t1.right,t2.left);
}
二叉树的进阶面试题
1.二叉树的构建及遍历
/*
编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以
指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是
空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历
结果。
*/
import java.util.Scanner;
import java.util.Stack;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
char[] ch = sc.nextLine().toCharArray();
Stack<Character> st = new Stack<>();
st.push(ch[0]);
for(int i=1;i<ch.length-1;i++){
if(ch[i] == '#'){
System.out.print(st.pop()+" ");
}else{
st.push(ch[i]);
}
}
System.out.println();
}
}
}
2.二叉树的分层遍历
List<List<Integer>> levels = new ArrayList<List<Integer>>();
public void helper(TreeNode node, int level) {
if (levels.size() == level)
levels.add(new ArrayList<Integer>());
levels.get(level).add(node.val);
if (node.left != null)
helper(node.left, level + 1);
if (node.right != null)
helper(node.right, level + 1);
}
public List<List<Integer>> levelOrder(TreeNode root) {
if (root == null) return levels;
helper(root, 0);
return levels;
}
3.给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
//lca表示公共祖先
private TreeNode lac=null;
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null){
return null;
}
//findNode递归找的过程,一旦找到就把结果放到lac中
findNode(root,p,q);
return lac;
}
// 看从 root 出发能不能找到 p 或者 q. 只要找到 1 个, 就返回 true, 都找不到返回 false
private boolean findNode(TreeNode root, TreeNode p, TreeNode q) {
if(root==null){
return false;
}
//递归按照先后遍历的方式来查找
int left=findNode(root.left,p,q)?1:0;
int right=findNode(root.right,p,q)?1:0;
int mid=(root==p||root==q)?1:0;
if(left+right+mid==2){
lac=root;
}
// 如果三个位置之和 为 0 表示没找到. 返回 false
// 只要能找到 1 个或者以上, 都返回 true
return (left+mid+right)>0;
}
4.二叉搜素树转换为排序双向链表
public TreeNode Convert(TreeNode pRootOfTree){
//基于递归的方式来完成双向列表构建,为了确保有序性,需要中序遍历
//二叉搜素树的中序遍历的结果是有序的
if(pRootOfTree==null){
return null;
}
if(pRootOfTree.left==null&&pRootOfTree.right==null){
return pRootOfTree;
}
//最终的链表=左子树+根节点+右子树
//就需要用左子树链表的尾巴和根节点相连
//再用右子树的头部和根节点相连
//1.先递归处理左子树
//left就是左子树这个链表的头节点
TreeNode left=Convert(pRootOfTree.left);
//需要找到左子树;链表的为节点
TreeNode leftTail=left;
//right相当于链表的next
while(leftTail!=null&&leftTail.right!=null){
leftTail=leftTail.right;
}
//循环结束之后,leftTail就指向了左侧链表的尾部
//3.把左子树和当前节点连接在一起
if(left!=null){
leftTail.right=pRootOfTree;
pRootOfTree.left=leftTail;
}
// 4. 递归转换右子树, 把右子树也变成双向链表
TreeNode right = Convert(pRootOfTree.right);
// 5. 把当前节点和右子树连在一起
if (right != null) {
right.left = pRootOfTree;
pRootOfTree.right = right;
}
// 6. 最终返回 新的链表的头结点
// 如果 left 为 null, 链表的头结点就是 pRootOfTree
// 如果 left 非 null, 头结点就是 left
return left == null ? pRootOfTree : left;
}
5.根据一棵树的前序和中序遍历构造二叉树
private int index;
public TreeNode buildTree(int[] preorder, int[] inorder) {
index=0;
return buildTreeHelper(preorder,inorder,0,inorder.length);
}
private TreeNode buildTreeHelper(int[] preorder, int[] inorder, int left, int right) {
if(left>=right){
return null;//中序遍历结果为空,整个树为空数
}
if(index>=preorder.length){
return null;//遍历元素结束
}
TreeNode root=new TreeNode(preorder[index]);
index++;//节点创建完毕 就index++,准备下一个节点
//根据该节点在中序遍历结果中的位置,把inorder数组划分成俩个部分
int pos=find(inorder,left,right,root.val);
//[left,pos)表示当前root左子树中序遍历结果
//【pos+1,right)表示当前root右子树的遍历结果
root.left=buildTreeHelper(preorder,inorder,left,pos);
root.right=buildTreeHelper(preorder,inorder,pos+1,right);
return root;
}
private int find(int[] inorder, int left, int right, int tofind) {
for(int i=left;i<right;i++){
if(inorder[i]==tofind){
return i;
}
}
return -1;
}