在二叉树中找到一个节点的后继节点
题目:现在有一种新的二叉树节点类型如下
public static class Node {
public int value;
public Node left;
public Node right;
public Node parent;
public Node(int data) {
this.value = data;
}
}
该结构比普通二叉树节点结构多了一个指向父节点的parent指针。假 设有一 棵Node类型的节点组成的二叉树,树中每个节点的parent指针 都正确地指向 自己的父节点,头节点的parent指向null。只给一个在 二叉树中的某个节点 node,请实现返回node的后继节点的函数。在二叉树的中序遍历的序列中, node的下一个节点叫作node的后继节点。
该二叉树的中序遍历为:7425136
则7的后继节点为4,3的后继节点为6,6则存在后继节点。
当某个节点存在右子树时,该节点的后继节点为该节点右子树的最左节点,如2的后继节点为5,3的后继节点为6;若该节点不存在右子树,则将当前节点设为x,寻找存在使x为parent节点的左孩子为止
当前节点不存在右子树,如何寻找后继节点?
假设寻找7的后继节点:
parent节点为x的后继节点,因此4为7的后继节点。
寻找5的后继节点,可是x不是Parent节点中的左孩子,将Parent节点设为原Parent节点的Parent节点,这里2的Parent节点为1,将x节点设为原来的Parent节点,即x换为2,因此发现2是1的左孩子,所以节点5的后继节点是1
public static Node getSuccessorNode(Node node) {
if (node == null) {
return node;
}
//如果存在右孩子,则找到最左节点
if (node.right != null) {
return getLeftMost(node.right);
} else {
//直到parent为null或者parent.left = node
Node parent = node.parent;
while (parent != null && parent.left != node) {
node = parent;
parent = node.parent;
}
return parent;
}
}
//寻找最左节点
public static Node getLeftMost(Node node) {
if (node == null) {
return node;
}
while (node.left != null) {
node = node.left;
}
return node;
}
二叉树的序列化和反序列化
题目:首先我们介绍二叉树先序序列化的方式,假设序列化的结果字符串为str,初始时str等于空字符串。先序遍历二叉树,如果遇到空节点,就在str的末尾加上“#!”,“#”表示这个节点为空,节点值不存在,当然你也可以用其他的特殊字符,“!”表示一个值的结束。如果遇到不为空的节点,假设节点值为3,就在str的末尾加上“3!”。现在请你实现树的先序序列化
思路:我们知道所谓的二叉树是一种由对象引用将多个结点关联起来的抽象数据结构,是存在于内存中的,不能进行持久化,如果需要将一颗二叉树的结构持久化保存,需要将其转换为字符串并保存到文件中,于是关键是建立一套规则,使得二叉树可以与字符串一一对应,根据一个二叉树可以唯一的得到一个字符串,根据一个字符串也可以唯一的还原出一棵二叉树
先序遍历序列化
public static String serialByPre(Node head) {
if (head == null) {
return "#!";
}
String res = head.value + "!";
res += serialByPre(head.left);
res += serialByPre(head.right);
return res;
}
先序遍历反序列化
public static Node reconByPreString(String preStr) {
//分隔成String数组
String[] values = preStr.split("!");
Queue<String> queue = new LinkedList<String>();
for (int i = 0; i != values.length; i++) {
queue.offer(values[i]);
}
return reconPreOrder(queue);
}
public static Node reconPreOrder(Queue<String> queue) {
String value = queue.poll();
if (value.equals("#")) {
return null;
}
Node head = new Node(Integer.valueOf(value));
head.left = reconPreOrder(queue);
head.right = reconPreOrder(queue);
return head;
}
判断一棵树是否为平衡二叉树
平衡二叉树的提出就是为了保证树不至于太倾斜,尽量保证两边平衡。平衡二叉树要么是一棵空树,要么保证左右子树的高度之差不大于 1,子树也必须是一颗平衡二叉树。
基本思路:递归遍历二叉树的每一个节点来,求出每个节点的左右子树的高度,如果每个节点的左右子树高度差不超过一,按照定义,它就是一棵平衡二叉树。即先判断根节点左右的子树,如果左右子树各自平衡,再合起来判断左右子树是否平衡。
public class IsBalancedTree {
public static class Node {
public Node left;
public Node right;
public Integer value;
public Node(Integer value) {
this.value=value;
}
}
public static class ReturnData{
public boolean isB;
public Integer height;
public ReturnData(boolean isB , Integer height){
this.isB=isB;
this.height=height;
}
}
public static boolean isBalance(Node head) {
return process(head).isB;
}
public static ReturnData process(Node head) {
if(head == null) {
return new ReturnData(true,0);
}
//左树不平衡则返回
ReturnData leftData = process(head.left);
if(!leftData.isB) {
return new ReturnData(false,0);
}
//右树不平衡则返回
ReturnData rightData = process(head.right);
if(!rightData.isB) {
return new ReturnData(false,0);
}
//左右树不平衡则返回
if(Math.abs(leftData.height - rightData.height) > 1) {
return new ReturnData(false,0);
}
//返回平衡结果和树的高度
return new ReturnData(true,Math.max(leftData.height, rightData.height) + 1);
}
}
判断一棵树是否为搜索二叉树
二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。通常下搜索二叉树不存在重复节点
二叉树中序遍历的结果下是依次升序的
在中序遍历时,保存前驱节点,如果当前节点小于前驱结点,则这棵树不是BST
public static int inOrderRecur(Node head) {
Node pre = head;
boolean flag=true;
//左->根->右
if (head == null) {
return;
}
inOrderRecur(head.left);
System.out.print(head.value + " ");
//搜索二叉树的中序遍历是升序
if(!head.value > pre.value){
flag = false;
}
if(!flag){
return false;
}
inOrderRecur(head.right);
return true;
}
判断一棵树是否为完全二叉树
完全二叉树的特点:
- 当一个节点有右孩子,但是无左孩子,直接返回false
- 当一个节点有左孩子无右孩子,那么接下来要遍历的节点必须是叶子节点(叶子节点左右孩子为空)
- 当一个节点是叶子节点,那么按照层序遍历的结果,这个节点之后的所有结点都必须是叶子节点这棵树才是完全二叉树
首先每个节点都会有以下4种情况:
情况一:
情况二:
情况三:
情况四:
我们开始寻找规律:
- 如果当前访问的节点的左右孩子是情况3,说明不是完全二叉树,直接返回false
- 如果当前访问的节点的左右孩子是情况1,继续访问其他节点
- 如果当前访问的节点的左右孩子是情况2或者情况4,那么我们定义一个状态(接下来访问的所有节点必须全部是叶子节点)。只要遇到情况2或者情况4,这个状态就开启了
使用层次遍历并检查倒数第二层是否满以及是否有元素有右孩子无左孩子即可
二叉树的层序遍历
层次遍历的遍历顺序:1–>2–>3–>4–>5–>6–>7
public static boolean isCBT(Node head) {
if (head == null) {
return true;
}
Queue<Node> queue = new LinkedList<Node>();
boolean leaf = false;
Node l = null;
Node r = null;
queue.offer(head);
while (!queue.isEmpty()) {
head = queue.poll();
l = head.left;
r = head.right;
//有右孩子而无左孩子,直接返回false
//叶节点开启模式下,存在非叶节点,直接返回false
if ((leaf && (l != null || r != null)) || (l == null && r != null)) {
return false;
}
//压入左孩子
if (l != null) {
queue.offer(l);
}
//压入右孩子
if (r != null) {
queue.offer(r);
}//有左孩子无右孩子,开启叶节点模式
else {
leaf = true;
}
}
return true;
}
已知一棵完全二叉树,求其节点的个数
回忆下完全二叉树的定义:
设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,
第 h 层所有的结点都连续集中在最左边。
如果一棵满二叉树的高度为L,则节点个数为2L-1
所以,对于完全二叉树,其总是满足以下两种情形:
-
node的右子树,到达底部,说明node的左子树是满二叉树,如图所示:
上面这种情况,左子树的节点可以直接靠公式算出,右子树需要递归计算 -
node的右子树,没有到达底部,说明node的右子树是底层高度 - 1 的满二叉树,如图所示:
上面这种情况,右子树的节点个数可以完全靠公式算出,左子树需要靠递归计算
public static int nodeNum(Node head) {
if (head == null) {
return 0;
}
return bs(head, 1, mostLeftLevel(head, 1));
}
//l代表节点所在高度
//h代表该节点下的最深左边界高度
public static int bs(Node node, int l, int h) {
if (l == h) {
return 1;
}
//如果右子树的最深左边界到达h,说明左子树为满二叉树,可以直接套公式计算
if (mostLeftLevel(node.right, l + 1) == h) {
//左子树为满二叉树计算+右子树递归计算
return (1 << (h - l)) + bs(node.right, l + 1, h);
} else {
//左子树递归计算,右子树为高度-1的满二叉树计算
return (1 << (h - l - 1)) + bs(node.left, l + 1, h);
}
}
//计算最深左边界
public static int mostLeftLevel(Node node, int level) {
while (node != null) {
level++;
node = node.left;
}
return level - 1;
}