【问题标题】:How to determine if binary tree is balanced?如何判断二叉树是否平衡?
【发布时间】:2010-10-19 01:48:32
【问题描述】:

从那些学年开始已经有一段时间了。在一家医院找到了一份 IT 专家的工作。现在正在尝试进行一些实际的编程。我现在正在研究二叉树,我想知道确定树是否高度平衡的最佳方法是什么。

我在想一些事情:

public boolean isBalanced(Node root){
    if(root==null){
        return true;  //tree is empty
    }
    else{
        int lh = root.left.height();
        int rh = root.right.height();
        if(lh - rh > 1 || rh - lh > 1){
            return false;
        }
    }
    return true;
}

这是一个好的实现吗?还是我错过了什么?

【问题讨论】:

  • 如果您想查看 Donal Fellows 的带图形的 ascii 二叉树:i.imgur.com/97C27Ek.png
  • 很好的答案,帮助我进入了美国。 (笑话)

标签: java algorithm data-structures binary-tree


【解决方案1】:

在搜索其他内容时偶然发现了这个老问题。我注意到您从未得到完整的答案。

解决此问题的方法是首先为您要编写的函数编写规范。

说明:如果 (1) 它是空的,或者 (2) 它的左右子树高度平衡并且左树的高度在右树高度的 1。

既然您已经有了规范,那么编写代码就很简单了。只需遵循规范:

IsHeightBalanced(tree)
    return (tree is empty) or 
           (IsHeightBalanced(tree.left) and
            IsHeightBalanced(tree.right) and
            abs(Height(tree.left) - Height(tree.right)) <= 1)

将其翻译成您选择的编程语言应该很简单。

奖励练习:这个简单的代码草图在计算高度时遍历树的次数太多了。你能提高效率吗?

超级奖励练习:假设树严重不平衡。就像,一侧深一百万个节点,另一侧深三个。是否存在这种算法炸毁堆栈的情况?您能否修复实现以使其永远不会破坏堆栈,即使在给定大量不平衡树的情况下也是如此?

更新:Donal Fellows 在他的回答中指出,人们可以选择不同的“平衡”定义。例如,可以对“高度平衡”采取更严格的定义,并要求到 最近 空子的路径长度在到 最远 空子的路径之一内孩子。我的定义没有那么严格,因此允许更多的树。

也可以没有我的定义那么严格;可以说,平衡树是这样一种树,其中每个分支上到一棵空树的最大路径长度相差不超过 2、3 或其他常数。或者最大路径长度是最小路径长度的一部分,例如一半或四分之一。

通常真的没关系。任何树平衡算法的重点是确保您不会陷入一侧有一百万个节点而另一侧有三个节点的情况。 Donal 的定义在理论上是好的,但在实践中,提出一种满足这种严格程度的树平衡算法是很痛苦的。性能节省通常不能证明实施成本是合理的。您花费大量时间进行不必要的树重新排列,以达到在实践中几乎没有什么区别的平衡水平。谁会在乎有时需要 40 个分支才能到达一棵百万节点的不完全平衡树中最远的叶子,而理论上它在完全平衡的树中只需要 20 个?关键是它永远不需要一百万。从一百万的最坏情况下降到四十的最坏情况通常就足够了。您不必一直寻找最佳情况。

【讨论】:

  • 回答下面的“练习”……
  • 回答了下面的奖励练习。
  • sdk 下面的答案似乎是正确的,并且只进行了 2 次树遍历,O(n) 也是如此。除非我遗漏了一些东西,否则至少不能解决您的第一个奖金问题。您当然也可以使用动态编程和您的解决方案来缓存中间高度
  • 理论上,我仍然必须支持 Donal Fellows 的定义。
  • 好的,我认为lucky 的答案满足奖金和超级奖金的所有要求——它是迭代的(使用队列)而不是递归的——所以堆栈不会溢出,我们移动所有内存——问题堆。此外,它会逐级移动,因此如果一棵树的一侧严重不平衡,它会很快(最快?)找到它。
【解决方案2】:

平衡是一个真正微妙的属性;你以为你知道它是什么,但它很容易出错。特别是,即使是 Eric Lippert 的(好的)答案也是错误的。那是因为 height 的概念是不够的。您需要了解树的最小和最大高度的概念(其中最小高度是从根到叶的最少步数,而最大高度是......好吧,你明白了)。鉴于此,我们可以将平衡定义为:

一棵树,其中任何分支的最大高度不超过任何分支的最小高度

(这实际上意味着分支本身是平衡的;您可以选择相同的分支作为最大值和最小值。)

验证此属性所需要做的就是简单的树遍历来跟踪当前深度。第一次回溯时,会给你一个基线深度。之后每次回溯时,都会将新深度与基线进行比较

  • 如果它等于基线,那么你就继续
  • 如果不止一个不同,则树不平衡
  • 如果它是一个关闭,那么您现在知道平衡范围,并且所有后续深度(当您要回溯时)必须是第一个或第二个值。

在代码中:

class Tree {
    Tree left, right;
    static interface Observer {
        public void before();
        public void after();
        public boolean end();
    }
    static boolean traverse(Tree t, Observer o) {
        if (t == null) {
            return o.end();
        } else {
            o.before();
            try {
                if (traverse(left, o))
                    return traverse(right, o);
                return false;
            } finally {
                o.after();
            }
        }
    }
    boolean balanced() {
        final Integer[] heights = new Integer[2];
        return traverse(this, new Observer() {
            int h;
            public void before() { h++; }
            public void after() { h--; }
            public boolean end() {
                if (heights[0] == null) {
                    heights[0] = h;
                } else if (Math.abs(heights[0] - h) > 1) {
                    return false;
                } else if (heights[0] != h) {
                    if (heights[1] == null) {
                        heights[1] = h;
                    } else if (heights[1] != h) {
                        return false;
                    }
                }
                return true;
            }
        });
    }
}

我想你可以在不使用观察者模式的情况下做到这一点,但我发现这样推理更容易。


[编辑]:为什么你不能只计算每一边的高度。考虑这棵树:

        /\
       /  \
      /    \
     /      \_____
    /\      /     \_
   /  \    /      / \
  /\   C  /\     /   \
 /  \    /  \   /\   /\
A    B  D    E F  G H  J

好吧,有点乱,但根的每一边都是平衡的:C 是深度 2,ABDE 是深度 3,F,@ 987654329@,HJ 是深度 4。左分支的高度是 2(记住,当你穿过分支时高度会减小),右分支的高度是 3。但是整个树是 平衡,因为CF 之间的高度差为 2。您需要一个 minimax 规范(尽管实际算法可能不那么复杂,因为应该只有两个允许的高度)。

【讨论】:

  • 啊,好点子。你可以有一棵树,它是 h(LL)=4, h(LR)=3, h(RL)=3, h(RR)=2。因此,h(L)=4 和 h(R)=3,因此它看起来与早期算法平衡,但最大/最小深度为 4/2,这是不平衡的。这可能对图片更有意义。
  • 这就是我刚刚添加的(世界上最讨厌的 ASCII 图形树)。
  • @DonalFellows:你提到左分支的高度是 2。但是左分支有 4 个节点,包括根和叶 A。在这种情况下高度将是 3 正确
  • “一棵树,其中任何分支的最大高度不超过任何分支的最小高度。”。这个定义是不正确的。一个例子是 - A--(右分支)---B---(右分支)---C 这里,最大深度和最小深度都等于 2 但它不是平衡二叉树
【解决方案3】:

这仅确定树的顶层是否平衡。也就是说,你可以有一棵树,在最左边和最右边有两条长长的树枝,中间什么都没有,这将返回 true。在返回 true 之前,您需要递归检查 root.leftroot.right 以查看它们是否也在内部平衡。

【讨论】:

  • 但是,如果代码有一个最大和最小高度方法,如果它是全局平衡的,它也会是局部平衡的。
【解决方案4】:

奖励练习反应。简单的解决方案。显然,在实际实现中,可能会包装这个或其他东西以避免要求用户在他们的响应中包含高度。

IsHeightBalanced(tree, out height)
    if (tree is empty)
        height = 0
        return true
    balance = IsHeightBalanced(tree.left, out heightleft) and IsHeightBalanced(tree.right, out heightright)
    height = max(heightleft, heightright)+1
    return balance and abs(heightleft - heightright) <= 1     

【讨论】:

  • 如果树的高度超过几百层,就会出现 stackoverflow 异常。您已经高效地完成了它,但它不能处理中型或大型数据集。
  • 这个伪代码是你刚想出来的还是真正的语言? (我的意思是“out height”变量表示法)
  • @kap:这是伪代码,但 out syntax 取自 C#。基本上,这意味着参数从被调用函数传递到调用者(与标准参数相反,标准参数从调用者传递到被调用函数或引用参数,从调用者传递到被调用函数并返回)。这有效地允许函数返回多个值。
  • 很想知道heightleftheightright 来自哪里
  • @JacFrall:IsHeightBalanced 的第二个参数是out 参数。例如,IsHeightBalanced(tree.left, heightleft) 没有传递 heightleft 的值,而是计算 heightleft。我已经编辑了我的答案,以更明确地使用 out 参数。
【解决方案5】:

后序解决方案,只遍历树一次。时间复杂度为 O(n),空间为 O(1),比自顶向下的解决方案要好。我给你一个java版本的实现。

public static <T> boolean isBalanced(TreeNode<T> root){
    return checkBalance(root) != -1;
}

private static <T> int checkBalance(TreeNode<T> node){
    if(node == null) return 0;
    int left = checkBalance(node.getLeft());

    if(left == -1) return -1;

    int right = checkBalance(node.getRight());

    if(right == -1) return -1;

    if(Math.abs(left - right) > 1){
        return -1;
    }else{
        return 1 + Math.max(left, right);
    }
}

【讨论】:

  • 不错的解决方案,但空间复杂度应该是 O(H),其中 H 是树的高度。这是因为堆栈分配用于递归。
  • left == -1 是什么意思?什么时候会这样?如果左孩子的所有子树都不平衡,我们是否假设递归调用意味着left == -1 为真?
  • left == 1 表示左子树不平衡,则整个树不平衡。我们不再需要检查右子树,可以返回-1
  • 时间复杂度为 O(n),因为您必须遍历所有元素。而且,如果您有 x 个节点,并且需要 y 时间来检查余额;如果您有 2 个节点,则需要 2 年时间来检查余额。听起来不错?
  • 这里有图说明:algorithms.tutorialhorizon.com/…
【解决方案6】:

高度平衡二叉树的定义是:

二叉树的高度 每个节点的两个子树从不 相差超过 1。

所以, 一棵空的二叉树总是高度平衡的。
如果满足以下条件,则非空二叉树是高度平衡的:

  1. 它的左子树是高度平衡的。
  2. 它的右子树是高度平衡的。
  3. 身高差 左右子树不大于 大于 1。

考虑树:

    A
     \ 
      B
     / \
    C   D

A 的左子树是高度平衡的(因为它是空的),它的右子树也是如此。但是由于左子树的高度为0,右子树的高度为2,因此不满足条件3,因此树的高度仍然不平衡。

即使左右子树的高度相等,以下树也不是高度平衡的。您现有的代码将为它返回 true。

       A
     /  \ 
    B    C
   /      \
  D        G
 /          \
E            H

所以def中的every这个词非常重要。

这将起作用:

int height(treeNodePtr root) {
        return (!root) ? 0: 1 + MAX(height(root->left),height(root->right));
}

bool isHeightBalanced(treeNodePtr root) {
        return (root == NULL) ||
                (isHeightBalanced(root->left) &&
                isHeightBalanced(root->right) &&
                abs(height(root->left) - height(root->right)) <=1);
}

Ideone Link

【讨论】:

  • 所以这个答案对我帮助很大。但是,我发现免费的 [MIT intro to algorithms course] 似乎与条件 3 相矛盾。第 4 页显示了一个 RB 树,其中左分支的高度为 2,右分支的高度为 4。你能给我澄清一下吗?也许我没有得到子树的定义。 [1]:ocw.mit.edu/courses/electrical-engineering-and-computer-science/…
  • 区别似乎来自课程笔记中的这个定义。从任何节点 x 到后代叶子的所有简单路径都具有相同数量的黑色节点 = black-height(x)
  • 只是为了跟进,我发现一个定义改变了您对“每片叶子比任何其他叶子距根的距离‘不超过一定距离’”的回答中的第 (3) 点。这似乎满足了这两种情况。 Here是一些随机课件的链接
【解决方案7】:

可以通过Level order traversal检查二叉树是否平衡:

private boolean isLeaf(TreeNode root) {
    if (root.left == null && root.right == null)
        return true;
    return false;
}

private boolean isBalanced(TreeNode root) {
    if (root == null)
        return true;
    Vector<TreeNode> queue = new Vector<TreeNode>();
    int level = 1, minLevel = Integer.MAX_VALUE, maxLevel = Integer.MIN_VALUE;
    queue.add(root);
    while (!queue.isEmpty()) {
        int elementCount = queue.size();
        while (elementCount > 0) {
            TreeNode node = queue.remove(0);
            if (isLeaf(node)) {
                if (minLevel > level)
                    minLevel = level;
                if (maxLevel < level)
                    maxLevel = level;
            } else {
                if (node.left != null)
                    queue.add(node.left);
                if (node.right != null)
                    queue.add(node.right);
            }
            elementCount--;
        }
        if (abs(maxLevel - minLevel) > 1) {
            return false;
        }
        level++;
    }

    return true;
}

【讨论】:

  • 优秀的答案。我认为它符合 Eric 发布的关于奖金和超级奖金的所有要求。它是迭代的(使用队列)而不是递归的 - 所以调用堆栈不会溢出,我们将所有内存问题移到堆中。它甚至不需要遍历整个树。它逐级移动,所以如果一棵树严重不平衡到一侧,它会很快找到它(最快?比大多数递归算法更快,尽管你可以实现一个后序遍历迭代算法,它会找到最后一级失衡越快,但在第一层表现较差)。所以+1 :-)
【解决方案8】:

这比实际情况要复杂得多。

算法如下:

  1. 令 A = 最高层节点的深度
  2. 令 B = 最低层节点的深度

  3. 如果 abs(A-B)

【讨论】:

  • 两个问题,它的效率并不高,你在整个树上进行了两次传递。对于左侧有一个节点,右侧有数千个节点的树,您不必要地遍历整个事物,而您本可以在 3 次检查后停止。
【解决方案9】:

平衡的含义在一定程度上取决于手头的结构。例如,B-Tree 的节点不能超过距根的一定深度,或者更少,所有数据都位于距根的固定深度,但如果叶子到叶子的分布可能会失去平衡- 但一个节点是不均匀的。跳过列表完全没有平衡的概念,而是依靠概率来实现良好的性能。斐波那契树故意失去平衡,推迟重新平衡以实现卓越的渐近性能,以换取偶尔更长的更新。 AVL 和红黑树将元数据附加到每个节点以获得深度平衡不变量。

所有这些结构以及更多结构都存在于大多数常见编程系统的标准库中(python、RAGE 除外!)。实现一两个是很好的编程实践,但它可能不是一个很好的利用时间将自己的产品投入生产,除非你的问题有一些特殊的性能需要任何现成的集合都不能满足。

【讨论】:

    【解决方案10】:

    注意 1:任何子树的高度只计算一次。

    注意 2:如果左子树不平衡,则跳过可能包含百万个元素的右子树的计算。

    // return height of tree rooted at "tn" if, and only if, it is a balanced subtree
    // else return -1
    int maxHeight( TreeNode const * tn ) {
        if( tn ) {
            int const lh = maxHeight( tn->left );
            if( lh == -1 ) return -1;
            int const rh = maxHeight( tn->right );
            if( rh == -1 ) return -1;
            if( abs( lh - rh ) > 1 ) return -1;
            return 1 + max( lh, rh );
        }
        return 0;
    }
    
    bool isBalanced( TreeNode const * root ) {
        // Unless the maxHeight is -1, the subtree under "root" is balanced
        return maxHeight( root ) != -1;
    }
    

    【讨论】:

      【解决方案11】:

      平衡通常取决于每个方向上最长路径的长度。上面的算法不会为你做到这一点。

      你想实现什么?周围有自平衡树(AVL/红黑)。 事实上,Java 树是平衡的。

      【讨论】:

        【解决方案12】:
        public boolean isBalanced(TreeNode root)
        {
            return (maxDepth(root) - minDepth(root) <= 1);
        }
        
        public int maxDepth(TreeNode root)
        {
            if (root == null) return 0;
        
            return 1 + max(maxDepth(root.left), maxDepth(root.right));
        }
        
        public int minDepth (TreeNode root)
        {
            if (root == null) return 0;
        
            return 1 + min(minDepth(root.left), minDepth(root.right));
        }
        

        【讨论】:

        • 我认为这个解决方案是不正确的。如果你传递一个只有一个节点的树,即一个根,它将返回为 maxDepth 1(与 minDepth 相同)。正确的深度应该是0。树的根总是0depth
        【解决方案13】:

        这是一个完整的 C# 测试解决方案(对不起,我不是 java 开发人员)(只需在控制台应用程序中复制粘贴)。 我知道平衡的定义各不相同,所以不是每个人都可能喜欢我的测试结果,但请看一下在递归循环中检查深度/高度并在第一次不匹配时退出而不在每个节点上保存节点高度/级别/深度的略有不同的方法(仅在函数调用中维护它)。

        using System;
        using System.Linq;
        using System.Text;
        
        namespace BalancedTree
        {
            class Program
            {
                public static void Main()
                {
                    //Value Gathering
                    Console.WriteLine(RunTreeTests(new[] { 0 }));
                    Console.WriteLine(RunTreeTests(new int[] { }));
        
                    Console.WriteLine(RunTreeTests(new[] { 0, 1, 2, 3, 4, -1, -4, -3, -2 }));
                    Console.WriteLine(RunTreeTests(null));
                    Console.WriteLine(RunTreeTests(new[] { 10, 8, 12, 8, 4, 14, 8, 10 }));
                    Console.WriteLine(RunTreeTests(new int[] { 20, 10, 30, 5, 15, 25, 35, 3, 8, 12, 17, 22, 27, 32, 37 }));
        
                    Console.ReadKey();
                }
        
                static string RunTreeTests(int[] scores)
                {
                    if (scores == null || scores.Count() == 0)
                    {
                        return null;
                    }
        
                    var tree = new BinarySearchTree();
        
                    foreach (var score in scores)
                    {
                        tree.InsertScore(score);
                    }
        
                    Console.WriteLine(tree.IsBalanced());
        
                    var sb = tree.GetBreadthWardsTraversedNodes();
        
                    return sb.ToString(0, sb.Length - 1);
                }
            }
        
            public class Node
            {
                public int Value { get; set; }
                public int Count { get; set; }
                public Node RightChild { get; set; }
                public Node LeftChild { get; set; }
                public Node(int value)
                {
                    Value = value;
                    Count = 1;
                }
        
                public override string ToString()
                {
                    return Value + ":" + Count;
                }
        
                public bool IsLeafNode()
                {
                    return LeftChild == null && RightChild == null;
                }
        
                public void AddValue(int value)
                {
                    if (value == Value)
                    {
                        Count++;
                    }
                    else
                    {
                        if (value > Value)
                        {
                            if (RightChild == null)
                            {
                                RightChild = new Node(value);
                            }
                            else
                            {
                                RightChild.AddValue(value);
                            }
                        }
                        else
                        {
                            if (LeftChild == null)
                            {
                                LeftChild = new Node(value);
                            }
                            else
                            {
                                LeftChild.AddValue(value);
                            }
                        }
                    }
                }
            }
        
            public class BinarySearchTree
            {
                public Node Root { get; set; }
        
                public void InsertScore(int score)
                {
                    if (Root == null)
                    {
                        Root = new Node(score);
                    }
                    else
                    {
                        Root.AddValue(score);
                    }
                }
        
                private static int _heightCheck;
                public bool IsBalanced()
                {
                    _heightCheck = 0;
                    var height = 0;
                    if (Root == null) return true;
                    var result = CheckHeight(Root, ref height);
                    height--;
                    return (result && height == 0);
                }
        
                private static bool CheckHeight(Node node, ref int height)
                {
                    height++;
                    if (node.LeftChild == null)
                    {
                        if (node.RightChild != null) return false;
                        if (_heightCheck != 0) return _heightCheck == height;
                        _heightCheck = height;
                        return true;
                    }
                    if (node.RightChild == null)
                    {
                        return false;
                    }
        
                    var leftCheck = CheckHeight(node.LeftChild, ref height);
                    if (!leftCheck) return false;
                    height--;
                    var rightCheck = CheckHeight(node.RightChild, ref height);
                    if (!rightCheck) return false;
                    height--;
                    return true;
                }
        
        
                public StringBuilder GetBreadthWardsTraversedNodes()
                {
                    if (Root == null) return null;
                    var traversQueue = new StringBuilder();
                    traversQueue.Append(Root + ",");
                    if (Root.IsLeafNode()) return traversQueue;
                    TraversBreadthWards(traversQueue, Root);
                    return traversQueue;
                }
        
                private static void TraversBreadthWards(StringBuilder sb, Node node)
                {
                    if (node == null) return;
                    sb.Append(node.LeftChild + ",");
                    sb.Append(node.RightChild + ",");
                    if (node.LeftChild != null && !node.LeftChild.IsLeafNode())
                    {
                        TraversBreadthWards(sb, node.LeftChild);
                    }
                    if (node.RightChild != null && !node.RightChild.IsLeafNode())
                    {
                        TraversBreadthWards(sb, node.RightChild);
                    }
                }
            }
        }
        

        【讨论】:

        • 我不明白为什么有人会在发布答案后的 2 分钟内对这个答案投反对票??反对票很好,但您能解释一下这个解决方案有什么问题吗?
        【解决方案14】:

        如果这是为了你的工作,我建议:

        1. do not reinvent the wheel
        2. use/buy COTS 而不是摆弄比特。
        3. 节省您的时间/精力来解决业务问题。

        【讨论】:

          【解决方案15】:
          #include <iostream>
          #include <deque>
          #include <queue>
          
          struct node
          {
              int data;
              node *left;
              node *right;
          };
          
          bool isBalanced(node *root)
          {
              if ( !root)
              {
                  return true;
              }
          
              std::queue<node *> q1;
              std::queue<int>  q2;
              int level = 0, last_level = -1, node_count = 0;
          
              q1.push(root);
              q2.push(level);
          
              while ( !q1.empty() )
              {
                  node *current = q1.front();
                  level = q2.front();
          
                  q1.pop();
                  q2.pop();
          
                  if ( level )
                  {
                      ++node_count;
                  }
          
                          if ( current->left )
                          {
                                  q1.push(current->left);
                                  q2.push(level + 1);
                          }
          
                          if ( current->right )
                          {
                                  q1.push(current->right);
                                  q2.push(level + 1);
                          }
          
                  if ( level != last_level )
                  {
                      std::cout << "Check: " << (node_count ? node_count - 1 : 1) << ", Level: " << level << ", Old level: " << last_level << std::endl;
                      if ( level && (node_count - 1) != (1 << (level-1)) )
                      {
                          return false;
                      }
          
                      last_level = q2.front();
                      if ( level ) node_count = 1;
                  }
              }
          
              return true;
          }
          
          int main()
          {
              node tree[15];
          
              tree[0].left  = &tree[1];
              tree[0].right = &tree[2];
              tree[1].left  = &tree[3];
              tree[1].right = &tree[4];
              tree[2].left  = &tree[5];
              tree[2].right = &tree[6];
              tree[3].left  = &tree[7];
              tree[3].right = &tree[8];
              tree[4].left  = &tree[9];   // NULL;
              tree[4].right = &tree[10];  // NULL;
              tree[5].left  = &tree[11];  // NULL;
              tree[5].right = &tree[12];  // NULL;
              tree[6].left  = &tree[13];
              tree[6].right = &tree[14];
              tree[7].left  = &tree[11];
              tree[7].right = &tree[12];
              tree[8].left  = NULL;
              tree[8].right = &tree[10];
              tree[9].left  = NULL;
              tree[9].right = &tree[10];
              tree[10].left = NULL;
              tree[10].right= NULL;
              tree[11].left = NULL;
              tree[11].right= NULL;
              tree[12].left = NULL;
              tree[12].right= NULL;
              tree[13].left = NULL;
              tree[13].right= NULL;
              tree[14].left = NULL;
              tree[14].right= NULL;
          
              std::cout << "Result: " << isBalanced(tree) << std::endl;
          
              return 0;
          }
          

          【讨论】:

          • 你可能想添加一些 cmets
          【解决方案16】:

          RE:@lucky 的解决方案使用 BFS 进行级别顺序遍历。

          我们遍历树并保留对 vars min/max-level 的引用,它描述了节点是叶子的最低级别。

          我相信@lucky 解决方案需要修改。正如@codaddict 所建议的那样,我们必须检查左或右子节点是否为空(而不是两者),而不是检查节点是否为叶节点。否则,算法会认为这是一个有效的平衡树:

               1
              / \
             2   4
              \   \
               3   1
          

          在 Python 中:

          def is_bal(root):
              if root is None:
                  return True
          
              import queue
          
              Q = queue.Queue()
              Q.put(root)
          
              level = 0
              min_level, max_level = sys.maxsize, sys.minsize
          
              while not Q.empty():
                  level_size = Q.qsize()
          
                  for i in range(level_size):
                      node = Q.get()
          
                      if not node.left or node.right:
                          min_level, max_level = min(min_level, level), max(max_level, level)
          
                      if node.left:
                          Q.put(node.left)
                      if node.right:
                          Q.put(node.right)
          
                  level += 1
          
                  if abs(max_level - min_level) > 1:
                      return False
          
              return True
          

          此解决方案应满足初始问题中提供的所有规定,在 O(n) 时间和 O(n) 空间中运行。内存溢出将被定向到堆而不是破坏递归调用堆栈。

          或者,我们可以最初遍历树以迭代地计算 + 缓存每个根子树的最大高度。然后在另一个迭代运行中,检查每个根的左右子树的缓存高度是否相差不超过一。这也将在 O(n) 时间和 O(n) 空间中运行,但要迭代运行,以免导致堆栈溢出。

          【讨论】:

            【解决方案17】:

            嗯,你需要一种方法来确定左右的高度,以及左右是否平衡。

            我只想return height(node-&gt;left) == height(node-&gt;right);

            关于编写height 函数,请阅读: Understanding recursion

            【讨论】:

            • 您希望左右高度在 1 以内,不一定相等。
            【解决方案18】:

            你说的是什么树?那里有self-balancing 树。检查他们的算法,确定是否需要对树进行重新排序以保持平衡。

            【讨论】:

              【解决方案19】:

              这是一个基于通用深度优先遍历的版本。应该比其他正确答案更快并处理所有提到的“挑战”。为风格道歉,我不太了解 Java。

              如果 max 和 min 都设置并且相差 >1,您仍然可以通过提前返回来使其更快。

              public boolean isBalanced( Node root ) {
                  int curDepth = 0, maxLeaf = 0, minLeaf = INT_MAX;
                  if ( root == null ) return true;
                  while ( root != null ) {
                      if ( root.left == null || root.right == null ) {
                          maxLeaf = max( maxLeaf, curDepth );
                          minLeaf = min( minLeaf, curDepth );
                      }
                      if ( root.left != null ) {
                          curDepth += 1;
                          root = root.left;
                      } else {
                          Node last = root;
                          while ( root != null
                           && ( root.right == null || root.right == last ) ) {
                              curDepth -= 1;
                              last = root;
                              root = root.parent;
                          }
                          if ( root != null ) {
                              curDepth += 1;
                              root = root.right;
                          }
                      }
                  }
                  return ( maxLeaf - minLeaf <= 1 );
              }
              

              【讨论】:

              • 一个不错的尝试,但它显然不起作用。设 x 为空节点。让一个非空树节点表示为 (LEFT VALUE RIGHT)。考虑树 (x A (x B x))。 “root”指向节点 A、B、A、B、A、B ……永远。介意再试一次吗?提示:实际上没有父指针更容易。
              • @Eric:糟糕,已修复(我认为)。好吧,我正在尝试在没有 O(depth) 内存的情况下执行此操作,如果结构没有父指针(通常有),则需要使用堆栈。
              • 所以你告诉我的是你宁愿在父指针中使用 O(n) 永久内存以避免分配 O(d) 临时内存,其中 log n
              • 不幸的是,虽然您已经解决了遍历的问题,但这里还有一个更大的问题。这不测试一棵树是否平衡,它测试一棵树的所有叶子是否接近同一级别。这不是我给出的“平衡”的定义。考虑树 ((((x D x) C x) B x) A x)。当它显然是最大不平衡时,您的代码报告这是“平衡的”。想再试一次吗?
              • @Eric 回复 1:如果您已经将父指针用于其他事情,这不是一个错误的经济。回复2:当然,为什么不呢。这是一种奇怪的调试方式……我不应该在凌晨 4 点盲目地编写任何东西的遍历……
              【解决方案20】:
              class Node {
                  int data;
                  Node left;
                  Node right;
              
                  // assign variable with constructor
                  public Node(int data) {
                      this.data = data;
                  }
              }
              
              public class BinaryTree {
              
                  Node root;
              
                  // get max depth
                  public static int maxDepth(Node node) {
                      if (node == null)
                          return 0;
              
                      return 1 + Math.max(maxDepth(node.left), maxDepth(node.right));
                  }
              
                  // get min depth
                  public static int minDepth(Node node) {
                      if (node == null)
                          return 0;
              
                      return 1 + Math.min(minDepth(node.left), minDepth(node.right));
                  }
              
                  // return max-min<=1 to check if tree balanced
                  public boolean isBalanced(Node node) {
              
                      if (Math.abs(maxDepth(node) - minDepth(node)) <= 1)
                          return true;
              
                      return false;
                  }
              
                  public static void main(String... strings) {
                      BinaryTree tree = new BinaryTree();
                      tree.root = new Node(1);
                      tree.root.left = new Node(2);
                      tree.root.right = new Node(3);
              
              
                      if (tree.isBalanced(tree.root))
                          System.out.println("Tree is balanced");
                      else
                          System.out.println("Tree is not balanced");
                  }
              }
              

              【讨论】:

                【解决方案21】:

                这是我为 Eric 的奖金练习所尝试的。 我尝试解开我的递归循环,并在发现不平衡的子树后立即返回。

                int heightBalanced(node *root){
                    int i = 1;
                    heightBalancedRecursive(root, &i);
                    return i; 
                } 
                
                int heightBalancedRecursive(node *root, int *i){
                
                    int lb = 0, rb = 0;
                
                    if(!root || ! *i)  // if node is null or a subtree is not height balanced
                           return 0;  
                
                    lb = heightBalancedRecursive(root -> left,i);
                
                    if (!*i)         // subtree is not balanced. Skip traversing the tree anymore
                        return 0;
                
                    rb = heightBalancedRecursive(root -> right,i)
                
                    if (abs(lb - rb) > 1)  // not balanced. Make i zero.
                        *i = 0;
                
                    return ( lb > rb ? lb +1 : rb + 1); // return the current height of the subtree
                }
                

                【讨论】:

                  【解决方案22】:
                  public int height(Node node){
                      if(node==null)return 0;
                      else{
                          int l=height(node.leftChild);
                          int r=height(node.rightChild);
                         return(l>r?l+1:r+1);
                  
                  }}
                  public boolean balanced(Node n){
                  
                      int l= height(n.leftChild);
                      int r= height(n.rightChild);
                  
                      System.out.println(l + " " +r);
                      if(Math.abs(l-r)>1)
                          return false;
                      else 
                          return true;
                      }
                  

                  【讨论】:

                    【解决方案23】:

                    一棵空树是高度平衡的。如果满足以下条件,则非空二叉树 T 是平衡的:

                    1) T 的左子树是平衡的

                    2) T 的右子树是平衡的

                    3) 左子树和右子树的高度差不大于1。

                    /* program to check if a tree is height-balanced or not */
                    #include<stdio.h>
                    #include<stdlib.h>
                    #define bool int
                    
                    /* A binary tree node has data, pointer to left child
                       and a pointer to right child */
                    struct node
                    {
                      int data;
                      struct node* left;
                      struct node* right;
                    };
                    
                    /* The function returns true if root is balanced else false
                       The second parameter is to store the height of tree.  
                       Initially, we need to pass a pointer to a location with value 
                       as 0. We can also write a wrapper over this function */
                    bool isBalanced(struct node *root, int* height)
                    {
                      /* lh --> Height of left subtree 
                         rh --> Height of right subtree */   
                      int lh = 0, rh = 0;  
                    
                      /* l will be true if left subtree is balanced 
                        and r will be true if right subtree is balanced */
                      int l = 0, r = 0;
                    
                      if(root == NULL)
                      {
                        *height = 0;
                         return 1;
                      }
                    
                      /* Get the heights of left and right subtrees in lh and rh 
                        And store the returned values in l and r */   
                      l = isBalanced(root->left, &lh);
                      r = isBalanced(root->right,&rh);
                    
                      /* Height of current node is max of heights of left and 
                         right subtrees plus 1*/   
                      *height = (lh > rh? lh: rh) + 1;
                    
                      /* If difference between heights of left and right 
                         subtrees is more than 2 then this node is not balanced
                         so return 0 */
                      if((lh - rh >= 2) || (rh - lh >= 2))
                        return 0;
                    
                      /* If this node is balanced and left and right subtrees 
                        are balanced then return true */
                      else return l&&r;
                    }
                    
                    
                    /* UTILITY FUNCTIONS TO TEST isBalanced() FUNCTION */
                    
                    /* Helper function that allocates a new node with the
                       given data and NULL left and right pointers. */
                    struct node* newNode(int data)
                    {
                        struct node* node = (struct node*)
                                                    malloc(sizeof(struct node));
                        node->data = data;
                        node->left = NULL;
                        node->right = NULL;
                    
                        return(node);
                    }
                    
                    int main()
                    {
                      int height = 0;
                    
                      /* Constructed binary tree is
                                 1
                               /   \
                             2      3
                           /  \    /
                         4     5  6
                        /
                       7
                      */   
                      struct node *root = newNode(1);  
                      root->left = newNode(2);
                      root->right = newNode(3);
                      root->left->left = newNode(4);
                      root->left->right = newNode(5);
                      root->right->left = newNode(6);
                      root->left->left->left = newNode(7);
                    
                      if(isBalanced(root, &height))
                        printf("Tree is balanced");
                      else
                        printf("Tree is not balanced");    
                    
                      getchar();
                      return 0;
                    }
                    

                    时间复杂度:O(n)

                    【讨论】:

                      【解决方案24】:

                      为了在大树上获得更好的性能,您可以节省每个节点的高度,因此这是空间与性能的权衡:

                      class Node {
                          Node left;
                          Node right;
                          int value;
                          int height;
                      }
                      

                      实现增删减的例子

                      void addNode(Node root,int v)
                      {    int height =0;
                           while(root != null)
                           {
                               // Since we are adding new node so the height 
                               // will increase by one in each node we will pass by
                               root.height += 1;
                               height++;
                               else if(v > root.value){
                                  root = root.left();
                                  }
                               else{
                               root = root.right();
                               }
                      
                           }
                      
                               height++;
                               Node n = new Node(v , height);
                               root = n;         
                      }
                      int treeMaxHeight(Node root)
                      {
                       return Math.Max(root.left.height,root.right.height);
                      }
                      
                      int treeMinHeight(Node root)
                      {
                       return Math.Min(root.left.height,root.right.height);
                      
                      }
                      
                      Boolean isNodeBlanced(Node root)
                      {
                         if (treeMaxHeight(root) - treeMinHeight(root) > 2)
                             return false;
                      
                        return true;
                      }
                      
                      Boolean isTreeBlanced (Node root)
                      {
                          if(root == null || isTreeBalanced(root.left) && isTreeBalanced(root.right) && isNodeBlanced(root))
                          return true;
                      
                        return false;
                      
                      }
                      

                      【讨论】:

                        【解决方案25】:

                        递归检查每个子树是否平衡。递归意味着,您向子树请求某些内容,子树将返回响应。你一直在问,直到你达到基本情况。在树问题中,基本情况是当您点击叶节点时。

                        在这种情况下,我们向子树提出 2 个问题:

                        1- 你平衡了吗?

                        2- 你的身高是多少?

                        所以递归函数将返回 2 个答案,我将这些答案保存在数组中。

                        这是python代码:

                        class Solution:
                            def isBalanced(self, root: Optional[TreeNode]) -> bool:
                                def dfs(root):
                                    # leaf node is balanced
                                    if not root:
                                        # we carry the height of each subtree
                                        return [True,0]
                                    # with recursion, we start from bottom, so we do not have repetitive work
                                    left,right=dfs(root.left),dfs(root.right)
                                    # if any of the subtree return false, then we know entire tree is not balanced
                                    balanced=left[0] and right[0] and abs(left[1]-right[1])<=1
                                    # 1+max(left[1],right[1]) is the height of the each subtree. 1 is the root of the subtree
                                    return [balanced,1+max(left[1],right[1])]
                                return dfs(root)[0]
                        

                        这是javascript代码:

                        var isBalanced = function(root) {
                         function dfs(root){
                             if(!root){
                                 return [true,0]
                             }
                             
                             const left=dfs(root.left)
                             const right=dfs(root.right)
                             const balanced=left[0] && right[0] && Math.abs(left[1]-right[1])<=1
                             return [balanced,1+Math.max(left[1],right[1])]
                         }
                            return dfs(root)[0]
                        };
                        

                        【讨论】:

                          【解决方案26】:
                              static boolean isBalanced(Node root) {
                              //check in the depth of left and right subtree
                              int diff = depth(root.getLeft()) - depth(root.getRight());
                              if (diff < 0) {
                                  diff = diff * -1;
                              }
                              if (diff > 1) {
                                  return false;
                              }
                              //go to child nodes
                              else {
                                  if (root.getLeft() == null && root.getRight() == null) {
                                      return true;
                                  } else if (root.getLeft() == null) {
                                      if (depth(root.getRight()) > 1) {
                                          return false;
                                      } else {
                                          return true;
                                      }
                                  } else if (root.getRight() == null) {
                                      if (depth(root.getLeft()) > 1) {
                                          return false;
                                      } else {
                                          return true;
                                      }
                                  } else if (root.getLeft() != null && root.getRight() != null && isBalanced(root.getLeft()) && isBalanced(root.getRight())) {
                                      return true;
                                  } else {
                                      return false;
                                  }
                              }
                          }
                          

                          【讨论】:

                            【解决方案27】:

                            这不行吗?

                            return ( ( Math.abs( size( root.left ) - size( root.right ) ) < 2 );
                            

                            任何不平衡的树都会​​失败。

                            【讨论】:

                            • 许多平衡树也会失败。
                            猜你喜欢
                            • 2015-07-21
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 2018-06-30
                            • 1970-01-01
                            • 2013-07-03
                            • 1970-01-01
                            相关资源
                            最近更新 更多