平衡二叉树的左右子树也是平衡二叉树,那么所谓平衡就是左右子树的高度差不超过1.
平衡二叉树:平衡二叉搜索树(Self-balancing binary search tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
题目:输入一棵二叉树,判断该二叉树是否是平衡二叉树。
思路:利用求深度的方法,依次遍历左右子树,用他们的深度相减,结果大于1的,返回-1;否则返回树的深度
解题思路有两种,只遍历一次的方法最优。
重复遍历多次:
在遍历树的每个结点的时候,调用函数TreeDepth得到它的左右子树的深度。如果每个结点的左右子树的深度相差都不超过1,则这是一颗平衡的二叉树。这种方法的缺点是,首先判断根结点是不是平衡的,需要使用TreeDepth获得左右子树的深度,然后还需要继续判断子树是不是平衡的,还是需要使用TreeDepth获得子树的左右子树的深度,这样就导致了大量的重复遍历。
只遍历一次:
重复遍历会影响算法的性能,所以很有必要掌握不需要重复遍历的方法。如果我们用后序遍历的方式遍历二叉树的每一个结点,在遍历到一个结点之前我们就已经遍历了它的左右子树。只要在遍历每个结点的时候记录它的深度(某一结点的深度等于它到叶结点的路径的长度),我们就可以一边遍历一边判断每个结点是不是平衡的。
class Solution {
public:
bool IsBalanced_Solution(TreeNode* pRoot) {
int depth = 0;//
return IsBalanced(pRoot, &depth);//返回一个布尔值
}
private:
int IsBalanced(TreeNode* pRoot, int* depth){
if(pRoot == NULL){
*depth = 0;
return true;//空树是的。后续遍历时,遍历到一个节点,其左右子树已经遍历 依次自底向上判断,每个节点只需要遍历一次
}
int left, right;
if(IsBalanced(pRoot->left, &left) && IsBalanced(pRoot->right, &right)){
int diff = left - right;//差值
if(diff <= 1 && diff >= -1){//绝对值不超过1,depth保存每一步遍历的深度,同时返回该节点是否是平衡的
*depth = 1 + (left > right ? left : right);//遍历过程中求子树高度,判断是否平衡
return true;//必须左边和右边都满足平衡,并且差值小于1才是真,循环遍历,从下面开始,记录这个节点的深度
}
}
return false;//左边或者右边不平衡就一定是假,如果在底层发现不平衡了,就直接一路返回。
//自下往上,算法复杂度O(N)
}
};
给定一个二叉树的前序遍历和中序遍历的序列,输出对应这个二叉树的后续遍历序列。
输入描述:
输入为一行。 两个字符串,分别表示二叉树的前序遍历和中序遍历结果,用空格分隔。保证数据合法
输出描述:
对应输出后序遍历序列
示例1
输入
ABDEC DBEAC
输出
DEBCA
import sys
# 定义节点
class TreeNode(object):
def __init__(self,val,left=None,right=None):
self.val = val
self.left = left
self.right = right
# 重建二叉树
def rebuild(pre,pos):
if pre =='':
return
val = pre[0]
index = pos.index(val)
root = TreeNode(val)
root.left = rebuild(pre[1:1+index],pos[:index])
root.right = rebuild(pre[1+index:],pos[index+1:])
return root
# 后续遍历第归实现
def posorder(root):
if root is None:
return
posorder(root.left)
posorder(root.right)
print(root.val,end='')
if __name__ =="__main__":
line = sys.stdin.readline().strip()
pre,pos = line.split()
root = rebuild(pre,pos)
res = posorder(root)
import java.util.Scanner;
import java.util.Arrays;
// 自定义树节点
class TreeNode {
TreeNode left;
TreeNode right;
char val;
public TreeNode(char x) {
val = x;
}
}
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
String str = sc.nextLine();
String[] s = str.split(" ");
char[] pre = s[0].toCharArray(); // 前序遍历的序列
char[] in = s[1].toCharArray(); // 中序遍历的序列
TreeNode tree = constructTree(pre, in); // 构建二叉树
printPost(tree); // 打印后序遍历序列
}
sc.close();
}
// 根据前序遍历序列和中序遍历序列构建二叉树
private static TreeNode constructTree(char[] pre, char[] in) {
if (pre.length == 0 || in.length == 0)
return null;
char rootVal = pre[0];
TreeNode root = new TreeNode(rootVal); // 先构建一个根节点
for (int i = 0; i < in.length; ++i) {
if (in[i] == rootVal) {
root.left = constructTree(Arrays.copyOfRange(pre,1,i+1),
Arrays.copyOfRange(in,0,i));
root.right = constructTree(Arrays.copyOfRange(pre,i+1,pre.length),
Arrays.copyOfRange(in,i+1,in.length));
break;
}
}
return root;
}
// 打印后序遍历序列
private static void printPost(TreeNode tree) {
if (tree == null) {
return;
} else {
printPost(tree.left);
printPost(tree.right);
System.out.print(tree.val);
}
}
}
给满出二叉树,编写算法将其转化为求和树
什么是求和树:二叉树的求和树, 是一颗同样结构的二叉树,其树中的每个节点将包含原始树中的左子树和右子树的和。
二叉树:
10
/ \
-2 6
/ \ / \
8 -4 7 5
求和树:
20(4-2+12+6)
/ \
4(8-4) 12(7+5)
/ \ / \
0 0 0 0
二叉树给出前序和中序输入,求和树要求中序输出;
所有处理数据不会大于int;
输入描述:
2行整数,第1行表示二叉树的前序遍历,第2行表示二叉树的中序遍历,以空格分割
输出描述:
1行整数,表示求和树的中序遍历,以空格分割
示例1
输入
复制
10 -2 8 -4 6 7 5 8 -2 -4 10 7 6 5
输出
复制
0 4 0 20 0 12 0
因为是满二叉树,其实结果跟前序遍历数组无关,只和中序遍历数组有关,并且中序数组一定是奇数个,结果索引为偶数的一定为0,索引为奇数的值是中序遍历数组其他值之和(不包括自己),使用二分法找到根节点,然后计算子树之和,不用还原二叉树。
本题有如下规律:
求和树的根节点 = 除本身外原二叉树所有子节点之和,
本题中根节点为中序遍历数组中正中间项(满二叉树)
递归求得左右子树,直到子树节点个数为1返回[0]。
需要考虑根节点在两侧的情况,树节点个数为0时,返回空[]
mid
=
len
(d)
/
/
2
# 满二叉树根节点即正中间数值,前序遍历数组本题中用不到,
var line1 = readline().split(" ").map(Number)//第一行转换成数字
var line2 = readline().split(" ").map(Number)
/* 前中序遍历的数组恢复二叉树 */
function ArrayToTreeNode(pre, ino) {
if(pre.length === 0 || !pre) {
return null//初始
}
var root = {
val: pre[0],
left:null,
right:null,
add: 0//构造跟节点
}
var index = ino.indexOf(pre[0])//找到跟节点的下标
root.left = ArrayToTreeNode(pre.slice(1, index+1), ino.slice(0, index))//前序后面的是左边,截取数组,中序就是从0到跟节点。
root.right = ArrayToTreeNode(pre.slice(index + 1), ino.slice(index + 1))//一直截取到最后//这里是重建了二叉树
if (root.left != null) root.add = root.add + root.left.val + root.left.add
if (root.right != null) root.add = root.add + root.right.val + root.right.add
return root//重建求和的二叉树
}
var arr = []
var bt = ArrayToTreeNode(line1, line2)
/* 非递归且通用的中序遍历方法 */
var inorderTraversal2 = function(root) {
let res = [];
let stack = [];
const [W , G] = [0, 1];
stack.push({c:W, node:root});
while(stack.length){
let c,n;
let curr_n = stack.pop();
c = curr_n.c;
n = curr_n.node;
if(!n){
continue;
}
if(c == W){
stack.push({c:W, node:n.right})
stack.push({c:G, node:n})
stack.push({c:W, node:n.left})
}
else{
res.push(n.add)
}
}
return res;
};
arr = inorderTraversal2(bt).join(' ')
print(arr);