【数据结构】二叉树的高频热门面试题大全
文章目录
- 前言
- 二叉树面试热门题目
- 总结
前言
本文建立在前两篇文章的基础上学习
树、二叉树
二叉树的遍历与操作
请大家多多支持
二叉树面试热门题目
注:该部分出现的所有题目都可以在力扣上面找到原题,大家可以去官网尝试自己做一下,题目与我写的注释题目基本一样。
//翻转二叉树public TreeNode invertTree(TreeNode root) {if(root == null){return null;}if(root.right == null && root.left == null){return root;}TreeNode tmp = root.left;root.left = root.right;root.right = tmp;invertTree(root.left);invertTree(root.right);return root;}
翻转二叉树借用tmp作为中间变量交换左右子树的值。
//检查两棵树是否相同public boolean isSameTree(TreeNode p, TreeNode q) {if(p == null && q == null){return true;}if(p == null && q != null || p != null && q == null){return false;}if(p.val == q.val){boolean leftRet = isSameTree(p.left,q.left);if(!leftRet){return false;}boolean rightRet = isSameTree(p.right,q.right);if(!rightRet){return false;}return true;}return false;}
先判断结构是否一样,再判断具体的值是不是一样。
//对称二叉树public boolean isSymmetric(TreeNode root) {if(root == null){return true;}return isSymmetricChild(root.left,root.right);}public boolean isSymmetricChild(TreeNode p,TreeNode q){if(p == null && q == null){return true;}if(p == null && q != null || p != null && q == null){return false;}if(p.val == q.val){boolean leftRet = isSymmetricChild(p.left,q.right);if(!leftRet){return false;}boolean rightRet = isSymmetricChild(p.right,q.left);if(!rightRet){return false;}return true;}return false;}
除了根节点之外,把树分成左右两部分,其实就成了判断是不是树相同。
//另一颗树的子树public boolean isSubtree(TreeNode root, TreeNode subRoot) {if(root == null){return false;}if(root.val == subRoot.val){if (isSameTree(root,subRoot)){return true;}else{if(isSubtree(root.left,subRoot)){return true;}if(isSubtree(root.right,subRoot)){return true;}}}if(isSubtree(root.left,subRoot)){return true;}if(isSubtree(root.right,subRoot)){return true;}return false;}
本质也是比较树是否相同,但是要先找到小树根节点所对应的大树里面的结点。
//平衡二叉树/** 时间复杂度O(n^2)* */public boolean isBalanced(TreeNode root) {if(root == null){return true;}if(!isBalanced(root.left)){return false;}if(!isBalanced(root.right)){return false;}int h = getHeight(root.left) - getHeight(root.right);if(h < -1 || h > 1){return false;}else{return true;}}// 获取二叉树的高度public int getHeight(TreeNode root){if(root == null){return 0;}return Math.max(getHeight(root.left),getHeight(root.right)) + 1;}
首先平衡二叉树的概念是两边高度差不能大于1。这第一种方法做起来比较容易,但是时间消耗大。
//平衡二叉树/** 时间复杂度O(n)* */public boolean isBalanced2(TreeNode root){if(root == null){return true;}return getHeight2(root) >=0;}public int getHeight2(TreeNode root){if(root == null){return 0;}int left = getHeight2(root.left);if(left == -1){return -1;}int right = getHeight2(root.right);if(right == -1 || Math.abs(left - right) > 1){return -1;}else{return Math.max(left,right) + 1;}}
时间复杂度大的原因是getHeight最坏要遍历每个结点,现在改为下面的结点出了问题返回-1判断就行,不用一步步再重新找高度。
//最近的祖先public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if(root == null){return null;}TreeNode left = lowestCommonAncestor(root.left,p,q);TreeNode right = lowestCommonAncestor(root.right,p,q);if(root == p || root == q){return root;}if(left != null && right != null){return root;}else if(left != null){return left;}else{return right;}}public boolean getPath(TreeNode root,TreeNode node,Stack<TreeNode> stack){if(root == null){return false;}stack.push(root);if(root != node){boolean ret = getPath(root.left,node,stack);if(ret){return true;}ret = getPath(root.right,node,stack);if(ret){return true;}}else{return true;}stack.pop();return false;}public TreeNode lowestCommonAncestor2(TreeNode root, TreeNode p, TreeNode q){if(root == null){return null;}Stack<TreeNode> stackP = new Stack<>();Stack<TreeNode> stackQ = new Stack<>();getPath(root,p,stackP);getPath(root,q,stackQ);int n = stackP.size() - stackQ.size();if(n > 0){while(n > 0){stackP.pop();n--;}}else{n = -n;while(n > 0){stackQ.pop();n--;}}TreeNode ret = null;while(!stackQ.isEmpty() && !stackP.isEmpty()){if(stackQ.peek() == stackP.peek()){ret = stackQ.peek();break;}else{stackQ.pop();stackP.pop();}}return ret;}
最近的祖先也是两种方法,首先第一种我的建议是直接画图,当然我这张图画的比较抽象,大家可以自己尝试一下,不画图的话自己理解起来很麻烦,我当时也是看了好久才明白。
第二种方法比较巧妙,找到pq路上所有的点,再把公共祖先之前的点都出栈,最后栈顶的就是公共祖先了。
public class buildTreeUsedPreAndInOrder {static class TreeNode {public int val;public TreeNode left;//存储左孩子的引用public TreeNode right;//存储右孩子的引用public TreeNode(int val) {this.val = val;}}public int preIndex;public TreeNode buildTree(int[] preorder, int[] inorder) {return buildTreeChild(preorder,inorder,0,inorder.length-1);}public TreeNode buildTreeChild(int[] preorder, int[] inorder,int inbegin,int inend) {//这种情况下 表明 当前root 没有子树了if(inbegin > inend) {return null;}TreeNode root = new TreeNode(preorder[preIndex]);int rootIndex = findVal(inorder,inbegin,inend,preorder[preIndex]);preIndex++;root.left = buildTreeChild(preorder,inorder,inbegin,rootIndex-1);root.right = buildTreeChild(preorder,inorder,rootIndex+1,inend);return root;}private int findVal(int[] inorder,int inbegin,int inend,int val) {for(int i = inbegin ;i <= inend;i++) {if(inorder[i] == val) {return i;}}return -1;}
}
众所周知知道中序序列和其他任意一种序列就能得到唯一的二叉树,这里用前序中序构造二叉树,主要思路是,通过前序先找到根节点,然后根据根节点在中序序列中的位置确定左右子树,然后继续根据前序确定左右子树根节点以此类推。
public class buildTreeUsedPostAndInOrder {static class TreeNode {public int val;public TreeNode left;public TreeNode right;public TreeNode(int val) {this.val = val;}}public TreeNode buildTree(int[] inorder, int[] postorder) {postIndex = postorder.length - 1;return buildTreeChild(inorder,postorder,0,inorder.length - 1);}public int postIndex;public TreeNode buildTreeChild(int[] inorder, int[] postorder, int inbegin, int inend){if(inbegin > inend){return null;}TreeNode root = new TreeNode(postorder[postIndex]);int rootIndex = findIndex(inorder,inbegin,inend,postorder[postIndex]);postIndex--;root.right = buildTreeChild(inorder,postorder,rootIndex + 1,inend);root.left = buildTreeChild(inorder,postorder,inbegin,rootIndex -1);return root;}private int findIndex(int[] inorder,int begin,int end,int val){for (int i = begin; i <= end; i++) {if(inorder[i] == val){return i;}}return -1;}}
这个思路就相反,后序序列要从后往前遍历确定根节点,然后重复一样的操作。
//根据二叉树创建字符串public String tree2str(TreeNode root) {if(root == null){return null;}StringBuilder stringbuilder = new StringBuilder();tree2strchild(root,stringbuilder);return stringbuilder.toString();}public void tree2strchild(TreeNode root,StringBuilder stringbuilder){if(root == null){return;}stringbuilder.append(root.val);if(root.left != null){stringbuilder.append("(");tree2strchild(root.left,stringbuilder);stringbuilder.append(")");}else{if(root.right == null){return;}else{stringbuilder.append("()");}}if(root.right != null){stringbuilder.append("(");tree2strchild(root.right,stringbuilder);stringbuilder.append(")");}else{return;}}
这道题其实不难,但是很可能不会审题,读懂题目的话就很简单。
总结
本文围绕二叉树面试高频题型展开,将二叉树的核心特性与实用算法结合,覆盖了从 “结构操作” 到 “逻辑判断”、从 “树的构造” 到 “数据转换” 的全场景问题,是对前期树结构基础与遍历逻辑的实战深化。
这些题目不仅是面试高频考点,更串联起二叉树的核心能力:递归分治、遍历应用、结构分析、效率优化。掌握它们既能夯实二叉树基础,也能为后续学习 BST、AVL 树等复杂树结构铺路,建议结合力扣原题反复练习,将 “思路” 转化为 “代码本能”,真正吃透二叉树的解题逻辑。
如果大家在做题过程中遇到一些困难和不理解的地方,一定要去前面的文章中回顾一下知识
树、二叉树
二叉树的遍历与操作