持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第18天,点击查看活动详情
二叉树
搜索二叉树的性质
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉搜索树
如何判断一颗二叉树是否是搜索二叉树?
思路:采用中序遍历方式,如果遍历时的节点一直升序,说明是搜索二叉树。
public static int preValue = Integer.MIN_VALUE;
public static boolean checkBST(Node head){
if(head == null){
return true
}
boolean isLeftBst = checkBST(head.left);
if(!isLeftBst){
return false;
}
if(head.value <= preValue){
return false;
}else{
preValue = head.value;
}
return checkBST(head.right);
}
Leetcode-98. 验证二叉搜索树
javascript版本
var isValidBST = function(root) {
let prev = Number.MIN_SAFE_INTEGER;
function check(root){
if(!root) return true;
const isLeftBst = check(root.left);
if(!isLeftBst) return false;
if(root.val <= prev) {
return false;
}else{
prev = root.val;
}
return check(root.right);
}
return check(root)
};
完全二叉树
完全二叉树的性质
如果二叉树中除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布,则此二叉树被称为完全二叉树。
完全二叉树满足以下要求:
- 所有叶子节点都出现在 k 或者 k-1 层,而且从 1 到 k-1 层必须达到最大节点数;
- 第 k 层可以不是满的,但是第 k 层的所有节点必须集中在最左边。
需要注意的是不要把完全二叉树和“满二叉树”搞混了,完全二叉树不要求所有树都有左右子树,但它要求:
- 任何一个节点不能只有右子树没有左子树
- 叶子节点出现在最后一层或者倒数第二层,不能再往上
如何判断一颗二叉树是完全二叉树?
思路 采用宽度遍历方式,遍历节点过程中,如果遇到以下情况,说明不是完全二叉树:
- 1.该节点有右孩子,但没有左孩子,直接返回false
- 2.在不符合规则1的前提下,如果该节点没有左右孩子,那么后续遍历到的所有节点(包括下一层的节点)都需要是叶子节点
Leetcode-958. 二叉树的完全性检验
var isCompleteTree = function(root) {
if(!root) return true;
const queue = [root];
let isEncounteredLeaf = false; // 是否遇到过左右两个孩子不双全的节点
while(queue.length){
let cur = queue.shift();
// 该节点有右孩子,但没有左孩子,直接返回false
if(cur.right && !cur.left) return false;
// 之前已经遇到过左右孩子不双全的节点后,之后只能遇到叶子节点
if(isEncounteredLeaf && (cur.left || cur.right)) return false;
// 遇到左右孩子不双全的节点
if(!cur.left || !cur.right){
isEncounteredLeaf = true;
}
if(cur.left){
queue.push(cur.left);
}
if(cur.right){
queue.push(cur.right);
}
}
return true
};
满二叉树
满二叉树的性质
如果二叉树中除了叶子结点,每个结点的度都为 2,则此二叉树称为满二叉树。
满二叉树除了满足普通二叉树的性质,还具有以下性质:
- 满二叉树中第 i 层的节点数为 2n-1 个。
- 深度为 k 的满二叉树必有 2k-1 个节点 ,叶子数为 2k-1。
- 满二叉树中不存在度为 1 的节点,每一个分支点中都两棵深度相同的子树,且叶子节点都在最底层。
- 具有 n 个节点的满二叉树的深度为 log2(n+1)。
如何判断一颗二叉树是满二叉树?
利用“深度为 k 的满二叉树必有 2k-1 个节点”这个性质,先求二叉树的最大深度,再求二叉树的节点个数,如果满足关系式那就是满二叉树,否则就不是。
平衡二叉树
平衡二叉树的性质
概念: 对于任意一个子树,左树和右树的高度差都不超过1。
平衡二叉树的提出就是为了保证树不至于太倾斜,尽量保证两边平衡。因此它的定义如下:
- 平衡二叉树要么是一棵空树
- 要么保证左右子树的高度之差不大于 1
- 子树也必须是一颗平衡二叉树
也就是说,树的两个左子树的高度差别不会太大。
如何判断一颗二叉树是平衡二叉树?
// 主函数
public static boolean isBalanced(Node head){
return process(head).isBalanced;
}
// 返回值的数据结构
public static class ReturnType {
public boolean isBalanced;
public int height;
public ReturnType(boolean isB, int h){
isBalanced = isB;
height = h;
}
}
public static ReturnType process(Node x){
if(x == null){
return new ReturnType(true, 0);
}
ReturnType leftData = process(x.left);
ReturnType rightData = process(x.right);
int height = Math.max(leftData.height, rightData.height) + 1;
boolean isBalanced = leftData.isBalanced && rightData.isBalanced && Math.abs(leftData.height - rightData.height)<2;
return new ReturnType(isBalanced, height);
Leetcode-110. 平衡二叉树
Javascript版本
var isBalanced = function(root) {
function process(node){
if(!node) return {height:0, isBalanced: true};
const leftData = process(node.left);
const rightData = process(node.right);
const height = Math.max(leftData.height, rightData.height) + 1;
const isBalanced = leftData.isBalanced && rightData.isBalanced &&(Math.abs(leftData.height - rightData.height) < 2)
return {height, isBalanced}
}
return process(root).isBalanced
};
二叉树题目递归套路
可以搜索一下关键字: 动态规划 树型DP。像左子树、右子树要信息,大部分像这种套路的递归行为可以用动态规划 树型DP来解答。
回顾一下Leetcode-110. 平衡二叉树的递归解法:
// 主函数
public static boolean isBalanced(Node head){
return process(head).isBalanced;
}
// 返回值的数据结构
public static class ReturnType {
public boolean isBalanced;
public int height;
public ReturnType(boolean isB, int h){
isBalanced = isB;
height = h;
}
}
public static ReturnType process(Node x){
if(x == null){
return new ReturnType(true, 0);
}
ReturnType leftData = process(x.left);
ReturnType rightData = process(x.right);
int height = Math.max(leftData.height, rightData.height) + 1;
boolean isBalanced = leftData.isBalanced && rightData.isBalanced && Math.abs(leftData.height - rightData.height)<2;
return new ReturnType(isBalanced, height);
现在我们再来想一下如何用递归套路法来判断二叉树是否是搜索二叉树。 对于判断是否是搜索二叉树来说,如果满足:
- 对于每棵树,其左子树是搜索二叉树,右子树是搜索二叉树
- 并且,左子树的最大值小于当前节点值,右子树的最小值大于当前节点值
对于递归来说,每颗子树返回的信息需要都是一样的,所以,每棵子树都返回三个信息:是否是搜索二叉树、最大值、最小值。
Java版
// 判断是否是搜索二叉树
public static class ReturnData { // 返回值的数据结构
public boolean isBST;
public int min;
public int max;
public ReturnData(boolean is, int mi, int ma){
isBST = is;
min = mi;
max = ma;
}
}
public static ReturnData process(Node x){
if(x==null){
return null;
}
ReturnData leftData = process(x.left);
ReturnData rightData = process(x.right);
int min = x.value;
int max = x.value;
if(leftData!=null){
min = Math.min(min, leftData.min);
max = Math.max(max, leftData.max);
}
if(rightData!=null){
min = Math.min(min, rightData.min);
max = Math.max(max, rightData.max);
}
boolean isBST = true;
if(leftData!=null && (!leftData.isBST || leftData.max >= x.value)){
isBST = false;
}
if(rightData!=null && (!rightData.isBST || x.value >= rightData.min)){
isBST = false;
}
return new ReturnData(isBST,min,max);
}
// 判断是否是满二叉树
public static boolean isF(Node head){
if(head == null){
return true;
}
Info data = process(head);
return dara.nodes == (1 << data.height - 1);
}
public static class Info{
public int height;
public int nodes;
public Info(int h, int n){
height = h;
nodes n;
}
}
public static Info process(Node x){
if(x == null){
return new Info(0,0);
}
Info leftData = process(x.left);
Info rightData = process(x.right);
int height = Math.max(leftData.height, rightData.height)+1;
int nodes = leftData.nodes + rightData.nodes + 1;
return new Info(height, nodes);
}