目录
前言
1.二叉树的最近公共祖先
2.从前序与中序遍历序列构造二叉树
3.根据二叉树创建字符串
4.二叉树前序非递归遍历实现
变式1:二叉树中序非递归遍历实现
变式2:二叉树后序非递归遍历实现
编程想要学的好,刷题少不了,我们不仅要多刷题,还要刷好题!为此我开启了一个弯道超车必做好题锦集的系列,此为二叉树面试题第三篇,每篇大约5题左右。该系列会不定期更新,敬请期待!
二叉树的最近公共祖先https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/
方法一
代码:
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) {
return null;
}
if (root == p || root == q) {
return root;
}
TreeNode leftTree=lowestCommonAncestor( root.left, p, q);
TreeNode rightTree=lowestCommonAncestor( root.right, p, q);
if(leftTree!=null&&rightTree!=null){
return root;
} else if (leftTree!=null) {
return leftTree;
}else {
return rightTree;
}
}
解析:
因考虑的所有情况
(1)
若该根节点为p或者q,那么自身则为最近祖先
(2)
(3)
方法二
代码:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null){
return null;
}
Stack stack1 = new Stack<>();
Stack stack2 = new Stack<>();
getpath( root, p, stack1);
getpath( root, q, stack2);
int stack1size=stack1.size();
int stack2size=stack2.size();
if(stack1size-stack2size>0){
balance(stack1,stack2size);
}else {
balance(stack2,stack1size);
}
while (!stack1.empty()){
if(stack1.peek()==stack2.peek()){
return stack1.pop();
}else {
stack1.pop();
stack2.pop();
}
}
return null;
}
private void balance(Stack stack,int size){
while (stack.size()!=size){
stack.pop();
}
}
public boolean getpath(TreeNode root, TreeNode node, Stack stack){
if(root==null){
return false;
}
stack.push(root);
if(root==node){
return true;
}
boolean flag1=getpath(root.left,node,stack);
if(flag1==true){
return true;
}
boolean flag2=getpath(root.right,node,stack);
if(flag2==true){
return true;
}
stack.pop();
return false;
}
}
解析:
从前序与中序遍历序列构造二叉树https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/
代码:
class Solution {
private int preindex;
public TreeNode buildTree(int[] preorder, int[] inorder) {
return buildTreechild(preorder, inorder,0,inorder.length-1);
}
private TreeNode buildTreechild(int[] preorder,int[] inorder,int begin,int end){
if(begin>end){
return null;
}
TreeNode treeNode=new TreeNode(preorder[preindex]);
int rootindex=findrootindex(inorder,begin, end,preorder[preindex]);
preindex++;
treeNode.left=buildTreechild(preorder, inorder,begin,rootindex-1);
treeNode.right=buildTreechild(preorder, inorder,rootindex+1,end);
return treeNode;
}
private int findrootindex(int[] inorder,int begin,int end,int key){
for (int i = begin; i <=end ; i++) {
if(inorder[i]==key){
return i;
}
}
return -1;
}
}
解析:
对于任意一颗树而言,前序遍历的形式总是 根 左子树 右子树,而中序遍历的形式总是 左子树 根 右子树。
只要我们在中序遍历中定位到根节点,那么我们就可以分别知道左子树和右子树中的节点数目。由于同一颗子树的前序遍历和中序遍历的长度显然是相同的,因此我们就可以对应到前序遍历的结果中,对上述形式中的所有左右括号进行定位。
这样以来,我们就知道了左子树的前序遍历和中序遍历结果,以及右子树的前序遍历和中序遍历结果,我们就可以递归地对构造出左子树和右子树,再将这两颗子树接到根节点的左右位置。
递归左子树的部分图展示:
变式:从中序与后序遍历序列构造二叉树从中序与后序遍历序列构造二叉树https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/%E3%80%81
代码:
class Solution {
private int postindex;
public TreeNode buildTree(int[] inorder, int[] postorder) {
postindex = postorder.length - 1;
return buildTreechild(postorder, inorder, 0, inorder.length - 1);
}
private TreeNode buildTreechild(int[] postorder, int[] inorder, int begin, int end) {
if (begin > end) {
return null;
}
TreeNode treeNode = new TreeNode(postorder[postindex]);
int rootindex = findrootindex(inorder, begin, end, postorder[postindex]);
postindex--;
treeNode.right =buildTreechild(postorder, inorder,rootindex+1,end );
treeNode.left =buildTreechild(postorder, inorder,begin,rootindex-1 );
return treeNode;
}
private int findrootindex(int[] inorder, int begin, int end, int key) {
for (int i = begin; i <= end; i++) {
if (inorder[i] == key) {
return i;
}
}
return -1;
}
}
根据二叉树创建字符串https://leetcode.cn/problems/construct-string-from-binary-tree/
分析:
代码:
class Solution {
public String tree2str(TreeNode root) {
StringBuffer stringBuffer = new StringBuffer();
tree2strchild( root,stringBuffer);
return stringBuffer.toString();
}
public void tree2strchild(TreeNode root,StringBuffer stringBuffer) {
if(root==null){
return;
}
stringBuffer.append(root.val);
if(root.left!=null){
stringBuffer.append("(");
tree2strchild( root.left, stringBuffer);
stringBuffer.append(")");
}else {
if(root.right==null){
return;
}else {
stringBuffer.append("()");
}
}
if(root.right!=null){
stringBuffer.append("(");
tree2strchild( root.right, stringBuffer);
stringBuffer.append(")");
}else {
return;
}
}
}
代码:
public void preorderprint(TreeNode root){
if(root==null){
return;
}
Stack stack = new Stack<>();
TreeNode cur=root;
while(cur!=null||!stack.empty()){
while(cur!=null){
System.out.print(cur.val+" ");
stack.push(cur);
cur=cur.left;
}
TreeNode top=stack.pop();
cur=top.right;
}
}
代码解读:
当遍历的根结点为root,其基本思想为:
- 当root != null时,说明当前root所指的二叉树不空,因此将其添加到栈中,然后再遍历它的左子树。
- 当root == null时,说明该根结点为空,那么此时栈顶结点就是当前root的父结点,将栈顶结点出栈,就能根据栈顶结点访问其右子树。
- 若右子树为null,继续将栈顶结点出栈,访问其右子树。
- 如此重复,遍历完整颗树。
与前序遍历步骤大致相同,不同的是中序遍历需要先添加root到栈中,出栈时再打印root的val.
代码:
public void inorderprint(TreeNode root){
if(root==null){
return;
}
Stack stack = new Stack<>();
TreeNode cur=root;
while(cur!=null||!stack.empty()){
while(cur!=null){
stack.push(cur);
cur=cur.left;
}
TreeNode top=stack.pop();
System.out.print(top.val+" ");
cur=top.right;
}
}
思路:
首先需要得到栈顶元素,但不出栈,用top表示,如果top右节点为null,则说明top为叶子结点,可以出栈进入到顺序表类,如果不为null,则遍历top的右节点。可能重复遍历top的右子树,最终程序崩溃所以我们这里加一个变量pero进行判断。
代码:
public void postrderprint(TreeNode root){
if(root==null){
return;
}
Stack stack = new Stack<>();
TreeNode cur=root;
TreeNode flag=null;
while(cur!=null||!stack.empty()){
while(cur!=null){
stack.push(cur);
cur=cur.left;
}
TreeNode top=stack.peek();
if(top.right==null||flag==top.right){
System.out.print(top.val+" ");
stack.pop();
flag=top;
}else{
cur=top.right;
}
}
}
以上为我个人的小分享,如有问题,欢迎讨论!!!
都看到这了,不如关注一下,给个免费的赞