如何确定二叉树是否平衡?


113

那些学期已经有一段时间了。在一家医院担任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
user7643681 2015年

1
好的答案,帮助我进入了美国。(笑话)
亨利

Answers:


165

在寻找其他东西时偶然发现了这个老问题。我注意到您从未得到完整的答案。

解决此问题的方法是从编写要编写的函数的规范开始。

规范:如果(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)

将其转换为您选择的编程语言应该很简单。

额外的练习:在计算高度时,这个幼稚的代码草图会遍历树太多次。您可以提高效率吗?

超级奖金练习:假设树严重不平衡。像是,一侧有1百万个节点,另一侧有3个节点。是否存在这种算法会使堆栈崩溃的情况?您是否可以修复该实现,以便即使给定了巨大的不平衡树,它也不会破坏堆栈?

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

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

通常真的没关系。任何树平衡算法的目的都是要确保在一侧拥有一百万个节点而另一侧拥有三个节点的情况下,您不会陷入困境。从理论上讲,Donal的定义很好,但是在实践中,满足这种严格性的树平衡算法带来了痛苦。性能节省通常不能证明实施成本的合理性。您花费大量时间进行不必要的树重新排列,以达到实际上没有太大区别的平衡水平。谁会关心有时到一百万个节点不完美平衡的树中的最远叶子有时需要四十个分支,而理论上一个完美平衡的树只需要二十个分支?关键是它永远不会花费一百万。从一百万的最坏情况降到四十的最坏情况通常就足够了;您不必一路追求最佳状态。


19
+1仅是正确答案,我不敢相信有8个月没有人能回答这个问题……
BlueRaja-Danny Pflughoeft 2010年

1
回答下面的“练习”…
Potatoswatter 2010年

回答奖金练习如下。
Brian

sdk的以下答案似乎是正确的,并且仅进行2次树遍历,所以O(n)也是如此。除非我缺少somethinig,否则至少不能解决您的第一个奖金问题。你可以的,也可使用动态规划和解决方案,以高速缓存中间的高度
阿里

从理论上讲,我将不得不坚持Donal Fellows的定义。
Dhruv Gairola

26

平衡是一种真正的微妙的财产。您以为自己知道这是什么,但是容易出错。尤其是,甚至埃里克·利珀特(Eric Lippert)的(好的)答案也都没有了。那是因为高度的概念还不够。您需要具有一棵树的最小和最大高度的概念(其中最小高度是从根到叶的最小步数,最大是...好了,您得到了图片)。鉴于此,我们可以将余额定义为:

一棵树在那里的任何分支的最大高度不超过一个比任何分支的最小高度。

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

验证此属性所需要做的只是跟踪当前深度的简单树遍历。第一次回溯时,将为您提供基线深度。此后每次回溯时,都将新深度与基线进行比较

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

在代码中:

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;
            }
        });
    }
}

我想您可以在不使用Observer模式的情况下执行此操作,但是我发现以这种方式进行推理更容易。


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

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

OK,有点乱,但根本的每侧平衡:C在深度2, ,A,,B 是深3,和,,,是深度4.左分支的高度是2(记得像你那么高进给降低分支),右分支的高度为3。然而,总体树不是平衡,因为在之间的2高度差和。您需要一个minimax规范(尽管实际的算法可能不太复杂,因为应该只有两个允许的高度)。DEFGHJCF


嗯,好点。您可能有一棵树,该树是h(LL)= 4,h(LR)= 3,h(RL)= 3,h(RR)= 2。因此,h(L)= 4和h(R)= 3,因此对于较早的算法似乎是平衡的,但是最大/最小深度为4/2,则不平衡。使用图片可能会更有意义。
蒂姆(Tim)2010年

1
这就是我刚刚添加的内容(带有世界上最讨厌的ASCII图形树)。
Donal Fellows 2010年

@DonalFellows:您提到了左分支的高度为2,但是左分支有4个节点,包括根和叶A。在这种情况下,高度为3
头脑风暴

22

这仅确定树的顶层是否平衡。也就是说,您可能有一棵树,在最左端和最右端有两个长分支,中间没有任何东西,这将返回true。您需要递归检查root.leftroot.right,并在返回true之前查看它们是否在内部达到平衡。


但是,如果代码具有最大和最小高度方法,则如果它是全局平衡的,则也将是局部平衡的。
阿里

22

额外的运动反应。简单的解决方案。显然,在实际的实现中,可以将其包装起来,以免要求用户在响应中包括身高。

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

如果树的高度大于几百层,则会出现stackoverflow异常。您已经高效地完成了此操作,但是它不能处理中型或大型数据集。
埃里克·莱斯钦斯基

您只是想出了这个伪代码还是真实的语言?(我的意思是“ out height”可变符号)
kap

@kap:这是伪代码,但是out语法来自C#。基本上,这意味着参数从被调用函数传递到调用者(与标准参数相反,标准参数从调用者传递到被调用函数,或者ref参数从调用者传递到被调用函数,然后返回)。这有效地使函数返回多个值。
布赖恩

20

发布订单解决方案,仅遍历树一次。时间复杂度为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);
    }
}

4
好的解决方案,但空间复杂度应为O(H),其中H是树的高度。这是因为递归的堆栈分配。
legrass

什么left == -1意思 什么时候会是这种情况?left == -1如果左子级的所有子树不平衡,我们是否假定递归调用暗示这是正确的?
阿斯彭

left == 1表示左子树不平衡,则整个树不平衡。我们不再需要检查正确的子树,并且可以返回-1
tning

时间复杂度为O(n),因为您必须遍历所有元素。而且,如果您有x个节点,则需要y个时间检查余额;如果您有2个节点,则需要2年的时间来检查余额。听起来不错吗?
杰克

与绘图以及解释是在这里:algorithms.tutorialhorizo​​n.com/...
SHIR

15

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

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

因此,空的二叉树总是高度平衡的。
非空二叉树在以下情况下是高度平衡的:

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

考虑这棵树:

    A
     \ 
      B
     / \
    C   D

如图所示,左侧的子树A是高度平衡的(因为它是空的),右侧的子树也是如此。但是由于左子树的0高度为,而右子树的高度为,因此不满足条件3,因此树仍未达到高度平衡2

另外,即使左右子树的高度相等,后面的树也不是高度平衡的。您现有的代码将为此返回true。

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

因此,def中的每个非常重要。

这将起作用:

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链接


所以这个答案对我很有帮助。但是,我发现免费的[MIT算法入门课程]似乎与条件3背道而驰。第4页显示了一个RB树,其中左分支的高度为2,右分支的高度为4。也许我没有得到子树的定义。[1]:ocw.mit.edu/courses/electrical-engineering-and-computer-science/…–
i8abug

差异似乎来自课程笔记中的此定义。从任何节点x到后代叶子的所有简单路径都具有相同数量的黑色节点= black-height(x)
i8abug

只是为了跟进,我发现了一个定义,该定义改变了您答案中的点(3):“每片叶子与根的距离均不超过任何其他叶子”。这似乎可以满足这两种情况。 是一些随机课程软件的链接
i8abug

8

是否可以通过级别顺序遍历检查二叉树是否平衡:

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;
}

1
极好的答案。我认为这符合Eric关于奖金和超级奖金发布的所有要求。它是迭代的(使用队列),而不是递归的-因此调用堆栈不会溢出,并且我们将所有内存问题移到堆中。它甚至不需要遍历整个树。它会逐级移动,因此,如果一棵树完全不平衡到一侧,它会很快找到它(很快?比大多数递归算法要早得多,尽管您可以实现一个后遍历迭代算法,该算法将找到最后一级较早出现失衡,但在第一阶段表现不佳)。So +1 :-)
David Refaeli

7

这变得比实际复杂得多。

算法如下:

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

  3. 如果abs(AB)<= 1,则树是平衡的


简单而直接!
Wasim Thabraze 2014年

3
有两个问题,它效率不高,您正在整个树上进行两次传递。对于左侧有一个节点,右侧有数千个节点的树,当您经过3次检查就可以停止时,您不必要遍历整个过程。
埃里克·莱斯钦斯基

5

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

所有这些结构以及更多结构都存在于大多数常见编程系统的标准库中(python,RAGE!除外)。实现一两个是很好的编程习惯,但是,除非您的问题具有某些特殊性能,而任何现成的集合都无法满足,否则可能不会很好地利用时间来进行生产。


4

注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;
}

3

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

您要实施什么?周围有自平衡树(AVL /红黑色)。实际上,Java树是平衡的。



3
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。树的根始终具有0深度
Cratylus

3

这是在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分钟内否决该答案吗??否决票可以,但您能否解释一下该解决方案出了什么问题?
sbp

2
#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;
}

您可能需要添加一些评论
jgauffin 2011年

2

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

我们遍历树并保留对vars最小/最大级别的引用,该级别描述了节点为叶的最小级别。

我相信@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)空间中运行,但是要反复进行,以免引起堆栈溢出。


1

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

我只是 return height(node->left) == height(node->right);

关于编写height函数,请阅读: 了解递归


3
您希望左右高度在1之内,不一定相等。
Alex B

1

你在说什么样的树?那里有自我平衡的树木。检查他们的算法,确定是否需要对树重新排序以保持平衡。


1

这是基于通用深度优先遍历的版本。应该比其他正确答案要快,并能处理所有提到的“挑战”。抱歉,我不太了解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 );
}

1
不错的尝试,但显然不起作用。令x为空节点。令非空树节点表示为(左值右)。考虑树(x A(x B x))。“根”永远指向节点A,B,A,B,A,B...。想要再试一次吗?提示:如果没有父指针,实际上会更容易。
埃里克·利珀特

@Eric:糟糕,固定(我认为)。好吧,我正在尝试在没有O(depth)内存的情况下执行此操作,如果结构没有父指针(通常没有),则需要使用堆栈。
Potatoswatter

因此,您要告诉我的是,您宁愿在父指针中使用O(n)永久内存以避免分配O(d)临时内存,其中log n <= d <= n吗?这似乎是一种虚假的经济。
埃里克·利珀特

不幸的是,尽管您已经解决了遍历问题,但是这里存在一个更大的问题。这不会测试树是否平衡,而是测试树的所有叶子是否都接近同一级别。那不是我给的“平衡”的定义。考虑树(((((x D x)C x)B x)A x)。您的代码报告说这很明显是最大不平衡时,它是“平衡”的。想要再试一次吗?
埃里克·利珀特

@Eric回复1:如果您已经使用父指针进行其他操作,那么这不是虚假的节约。回复2:当然,为什么不呢?这是一种奇怪的调试方式……我不应该在凌晨4点盲目写任何东西的遍历……
Potatoswatter 2010年

1
/* Returns true if Tree is balanced, i.e. if the difference between the longest path and the shortest path from the root to a leaf node is no more than than 1. This difference can be changed to any arbitrary positive number. */
boolean isBalanced(Node root) {
    if (longestPath(root) - shortestPath(root) > 1)
        return false;
    else
        return true;
}


int longestPath(Node root) {
    if (root == null);
        return 0;
    else {
        int leftPathLength = longestPath(root.left);
        int rightPathLength = longestPath(root.right);
        if (leftPathLength >= rightPathLength)
            return leftPathLength + 1;
        else
            return rightPathLength + 1;
    }
}

int shortestPath(Node root) {
    if (root == null);
        return 0;
    else {
        int leftPathLength = shortestPath(root.left);
        int rightPathLength = shortestPath(root.right);
        if (leftPathLength <= rightPathLength)
            return leftPathLength + 1;
        else
            return rightPathLength + 1;
    }
}

1
您应该在答案中添加一些描述,并在代码示例中添加注释。
布拉德·坎贝尔

1
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");
    }
}

0

这是我为埃里克(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
}

0
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;
    }

0

一棵空树是高度平衡的。如果满足以下条件,则非空二叉树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)


0

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

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;

}

-1
    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;
        }
    }
}

-2

这行不行吗?

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

任何不平衡的树总是会失败。


4
许多平衡的树木也会使它失败。
布赖恩
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.