二叉树进阶问题
1.判断一颗树是否是完全二叉树
1.判断一棵树是否是完全二叉树:
(1)完全二叉树该树中若存在右子树则必然存在左子树
(2)完全二叉树的节点编号必须和满二叉树一一对应(你有的节点必须和满二叉树节点编号相同)。
(3)对于完全二叉树来说一共存在三种节点:
A.度为2的节点有N个
B.度为1的节点最多有一个(恰好就是左树的那个一个节点)
C.度为0的节点有N个
(4)判断方法:
package bin_tree.leetcode;
import java.util.LinkedList;
import java.util.Queue;
public class IsCompleteTree {
public boolean isCompleteTree(TreeNode root) {
if (root == null) {
return true;
}
// 层序遍历判断二叉树
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
// 引入标志位,来区分当前遍历过程处在第一还是第二阶段
boolean isSecondStep = false;
while (!queue.isEmpty()) {
TreeNode cur = queue.poll();
if (!isSecondStep) {
// 此时处在第一阶段
if (cur.left != null && cur.right != null) {
// 当前cur左右子树全部都存在
queue.offer(cur.left);
queue.offer(cur.right);
}else if (cur.left == null && cur.right != null) {
// 此时只有右树没有左树,反例
return false;
}else if (cur.left != null) {
// 只有左树没有右树,此时cur是碰到的第一个只有左树的节点
// 切换状态
isSecondStep = true;
queue.offer(cur.left);
}else {
// 此时左树和右树全部为空,cur第一个碰到的叶子节点
isSecondStep = true;
}
}else {
// 此时处在第二阶段,第二阶段中的所有节点不可能有子树
// 有一个反例就false
if (cur.left != null || cur.right != null) {
return false;
}
}
}
// 遍历全部结束,没有找到反例
return true;
}
}
2.二叉树遍历
2.二叉树遍历
根据题意画出该二叉树的结构:
代码实现:
package bin_tree.NewCoder;
import java.util.Scanner;
// 根据先序遍历结果还原二叉树,输出中序遍历结果
public class KY11 {
private static class TreeNode {
char val;
TreeNode left;
TreeNode right;
public TreeNode(char val) {
this.val = val;
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 获取用户多组输入
while (scanner.hasNext()) {
// 字符串形式的先序二叉树结果
String line = scanner.next();
// str -> TreeNode
TreeNode root = build(line);
// 中序遍历二叉树,按照格式打印结点值
inOrder(root);
// 每个结果占一行
System.out.println();
}
}
private static void inOrder(TreeNode root) {
if (root == null)
return;
inOrder(root.left);
System.out.print(root.val +" ");
inOrder(root.right);
}
// 根据先序遍历结果字符串还原二叉树,返回构建后二叉树的根节点
private static TreeNode build(String line) {
return preOrderBuild(line);
}
// abc##de#g##f###
// str -> char
// 当前处理到哪个字符了
static int index = 0;
private static TreeNode preOrderBuild(String line) {
char c = line.charAt(index);
if (c == '#') {
return null;
}
TreeNode root = new TreeNode(c);
index ++;
root.left = preOrderBuild(line);
index ++;
root.right = preOrderBuild(line);
return root;
}
}
运行截图:
3.从前序与中序遍历序列构造二叉树
3.从二叉树与中序遍历序列构造二叉树
前序遍历:第一个节点一定是当前树的根节点,后序节点也是左树或右树的根节点。
中序遍历:左子树在根节点左侧,右子树在根节点右侧。
1.代码实现:
package bin_tree.leetcode;
/**
* 根据前序和中序遍历结果,还原二叉树
*/
public class Num105 {
//当前处理到前序遍历的哪个位置了
int index=0;
public TreeNode buildTree(int[] preorder, int[] inorder) {
return buildTreeInternal(preorder, inorder, 0, inorder.length);
}
/**
* 根据传入的前序遍历和中序遍历的部分区间
* [left,right) 表示当前节点中序遍历结果
* 左区间连接到根节点左侧
* 右区间连接到根节点右侧
* @param preOrder 前序遍历结果
* @param inOrder 中序遍历结果
* @param left
* @param right
* @return
*/
public TreeNode buildTreeInternal(int[] preOrder,int[] inOrder,int left,int right){
//边界
if(left >=right){
//空区间,说明此时中序结果为空,空树
return null;
}
if(index >= preOrder.length){
//所有的节点已经全部处理完毕
return null;
}
//此时前序结果还有没处理的元素,从Index取得一个元素构建根节点
TreeNode root=new TreeNode(preOrder[index]);
//继续处理下一个节点
index++;
//处理左树
//拿着这个root.val去中序遍历中找到该值对应的索引pos
int pos=find(inOrder,left,right,root.val);
//左树
root.left=buildTreeInternal(preOrder,inOrder,left,pos);
//右树
root.right=buildTreeInternal(preOrder,inOrder,pos+1,right);
return root;
}
/**
* 在当前中序遍历中寻找root.val对应的索引值
* @param inOrder
* @param left
* @param right
* @param val
* @return
*/
private int find(int[] inOrder, int left, int right, int val) {
for (int i = left; i <right ; i++) {
if (inOrder[i]==val){
return i;
}
}
return -1;
}
}
4.从中序与后续遍历序列构造二叉树
4.从中序与后续遍历序列构造二叉树
package bin_tree.leetcode;
import java.util.HashMap;
import java.util.Map;
/**
* 后序 + 中序
**/
public class Num106 {
// 存储中序遍历的对应的值和索引
Map<Integer,Integer> map = new HashMap<>();
public TreeNode buildTree(int[] inorder, int[] postorder) {
// 将后序遍历结果反转
int[] preOrder = reverse(inorder,postorder);
return buildTreeInternal(preOrder,inorder,0,inorder.length);
}
int index = 0;
public TreeNode buildTreeInternal(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 ++;
int pos = map.get(root.val);
root.right = buildTreeInternal(preOrder,inOrder,pos + 1,right);
root.left = buildTreeInternal(preOrder,inOrder,left,pos);
return root;
}
private int[] reverse(int[] inorder,int[] postorder) {
int[] ret = new int[postorder.length];
for (int i = 0; i < ret.length; i++) {
ret[i] = postorder[ret.length - 1 - i];
}
for (int i = 0; i < inorder.length; i++) {
map.put(inorder[i],i);
}
return ret;
}
}
5.二叉树的最近公共祖先
5.二叉树的最近公共祖先
package bin_tree.leetcode;
/**
* 找二叉树的最近公共祖先
**/
public class Num236 {
TreeNode lca;
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) {
return null;
}
// 从根节点出发进行后序遍历,找到lca(p和q出现在三个位置的两个)
find(root,p,q);
return lca;
}
// 在以root为根节点的二叉树中,是否能同时找到p和q
private boolean find(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) {
return false;
}
int left = find(root.left,p,q) ? 1 : 0;
int right = find(root.right,p,q) ? 1 : 0;
int mid = (root == p || root == q) ? 1 : 0;
if (left + right + mid == 2) {
lca = root;
return true;
}
return (left + right + mid) > 0;
}
}
6.根据二叉树创建字符串
6.根据二叉树创建字符串
package bin_tree.leetcode;
/**
* 二叉树转为字符串
**/
public class Num606 {
StringBuilder sb = new StringBuilder();
// "1(2(4))(3)"
public String tree2str(TreeNode root) {
if (root == null) {
return "";
}
preOrder(root);
return sb.toString();
}
private void preOrder(TreeNode root) {
if (root == null) {
return;
}
// "1(2(4))(3)"
sb.append(root.val);
if (root.left != null) {
sb.append("(");
// 先序遍历左树
preOrder(root.left);
sb.append(")");
}else {
// 左空,判断右树是否为空
if (root.right != null) {
sb.append("()");
}
}
if (root.right != null) {
sb.append("(");
preOrder(root.right);
sb.append(")");
}
}
}
7.二叉搜索树与双向链表
package bin_tree.NewCoder;
import bin_tree.leetcode.TreeNode;
/**
* 将二分搜索树转为排序的双向链表
**/
public class ConvertTree2LinkedList {
// 传入一个BST的根节点,就可以将其转为双向链表,且返回链表头
public TreeNode Convert(TreeNode pRootOfTree) {
if(pRootOfTree == null ||(pRootOfTree.left == null &&
pRootOfTree.right == null)) {
return pRootOfTree;
}
//先转换左子树
TreeNode left = Convert(pRootOfTree.left);
//将左子树的尾部与根节点连接
TreeNode leftTail = left;
while (leftTail != null && leftTail.right != null) {
leftTail = leftTail.right;
}
//此时leftTail指向左链表的尾部
//连接左链表尾部和根节点
//判空
if (leftTail != null) {
leftTail.right = pRootOfTree;
pRootOfTree.left = leftTail;
}
//继续转化右子树
TreeNode right = Convert(pRootOfTree.right);
//将根节点和右子树连接起来
if (right != null) {
right.left = pRootOfTree;
pRootOfTree.right = right;
}
return left == null ? pRootOfTree : left;
}
}