web-dev-qa-db-fra.com

Comment déterminer si l'arbre binaire est équilibré?

Cela fait un moment de ces années scolaires. A obtenu un travail en tant que spécialiste informatique dans un hôpital. Essayer de faire de la programmation réelle maintenant. Je travaille actuellement sur des arbres binaires et je me demandais quel serait le meilleur moyen de déterminer si l'arbre est équilibré en hauteur.

Je pensais à quelque chose de ce genre:

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

Est-ce une bonne implémentation? ou est-ce que je manque quelque chose?

110
user69514

Je suis tombé sur cette vieille question en cherchant autre chose. Je remarque que vous n’avez jamais obtenu de réponse complète.

Pour résoudre ce problème, vous devez commencer par écrire une spécification pour la fonction que vous essayez d'écrire.

Spécification: Un arbre binaire bien formé est dit "équilibré en hauteur" si (1) il est vide, ou (2) ses enfants gauche et droit sont équilibrés en hauteur et que la hauteur de l'arbre gauche est à moins de 1 des hauteur de l'arbre de droite.

Maintenant que vous avez la spécification, le code est facile à écrire. Il suffit de suivre les spécifications:

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

Traduire cela dans le langage de programmation de votre choix devrait être trivial.

Exercice de bonus: cette esquisse de code naïf parcourt l'arbre trop souvent lors du calcul des hauteurs. Pouvez-vous le rendre plus efficace?

Exercice de super bonus: supposons que l’arbre soit massivement déséquilibré. Par exemple, un million de nœuds de profondeur d'un côté et trois de l'autre. Y at-il un scénario dans lequel cet algorithme explose la pile? Pouvez-vous réparer l'implémentation de sorte qu'elle n'explose jamais, même quand un arbre est déséquilibré massivement?

UPDATE : Donal Fellows souligne dans sa réponse qu'il existe différentes définitions du terme "équilibré" que l'on pourrait choisir. Par exemple, on pourrait prendre une définition plus stricte de "hauteur équilibrée" et exiger que la longueur du chemin d'accès à l'enfant vide le plus proche soit dans l'un des chemins. à l'enfant le plus éloigné . Ma définition est moins stricte que cela et admet donc plus d'arbres.

On peut aussi être moins strict que ma définition; on pourrait dire qu'un arbre équilibré est un arbre dans lequel la longueur maximale du chemin d'accès à un arbre vide sur chaque branche ne diffère pas de plus de deux, ou trois, ou d'une autre constante. Ou que la longueur maximale du chemin correspond à une fraction de la longueur minimale du chemin, par exemple moitié ou quart.

Cela n'a vraiment pas d'importance d'habitude. L’intérêt de tout algorithme d’équilibrage d’arbres est de s’assurer que vous ne vous retrouvez pas dans la situation où vous avez un million de nœuds d’un côté et trois de l’autre. La définition de Donal est bien en théorie, mais en pratique, il est difficile de trouver un algorithme d’équilibrage d’arbres qui réponde à ce niveau de rigueur. Les économies de performances ne justifient généralement pas les coûts de mise en œuvre. Vous passez beaucoup de temps à faire des réarrangements d'arbres inutiles pour atteindre un niveau d'équilibre qui, dans la pratique, ne fait guère de différence. Qui s'inquiète s'il faut parfois quarante branches pour atteindre la feuille la plus éloignée d'un arbre présentant un million de nœuds imparfaitement équilibré alors qu'en théorie, il ne pouvait en prendre que vingt dans un arbre parfaitement équilibré? Le fait est que cela ne prend jamais un million. Passer du pire cas d’un million à un pire cas de quarante est généralement suffisant; vous n'êtes pas obligé d'aller jusqu'au cas optimal.

161
Eric Lippert

L'équilibre est une propriété vraiment subtile. vous pensez savoir de quoi il s'agit, mais il est si facile de se tromper. En particulier, même la (bonne) réponse d'Eric Lippert est désactivée. En effet, la notion de hauteur ne suffit pas. Vous devez avoir le concept des hauteurs minimales et maximales d'un arbre (où la hauteur minimale est le plus petit nombre de pas entre la racine et la feuille, et le maximum, c'est… bien, vous obtenez l'image). Dans ces conditions, nous pouvons définir l’équilibre comme suit:

Un arbre où la hauteur maximale d’une branche n’est pas supérieure à n supérieure à la hauteur minimale d’une branche.

(Cela implique en fait que les branches sont elles-mêmes équilibrées; vous pouvez choisir la même branche pour le maximum et le minimum.)

Pour vérifier cette propriété, il vous suffit de parcourir une arborescence simple en gardant une trace de la profondeur actuelle. La première fois que vous revenez en arrière, cela vous donne une profondeur de base. Chaque fois après, lorsque vous revenez en arrière, vous comparez la nouvelle profondeur à la ligne de base

  • si elle est égale à la ligne de base, alors vous continuez simplement
  • si c'est plus qu'un différent, l'arbre n'est pas équilibré
  • s'il s'agit d'un problème, vous connaissez maintenant la plage d'équilibre et toutes les profondeurs suivantes (lorsque vous êtes sur le point de revenir en arrière) doivent correspondre à la première ou à la deuxième valeur.

Dans du code:

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

Je suppose que vous pouvez le faire sans utiliser le modèle Observer, mais je trouve plus facile de raisonner de cette façon.


[EDIT]: Pourquoi vous ne pouvez pas prendre la hauteur de chaque côté. Considérez cet arbre:

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

OK, un peu brouillon, mais chaque côté de la racine est équilibré: C est la profondeur 2, A, B, D, E sont la profondeur 3 et F, G, H, J sont la profondeur 4. La hauteur de la branche gauche est 2 (rappelez-vous la hauteur diminue au fur et à mesure que vous traversez la branche), la hauteur de la branche droite est de 3. Pourtant, l’arbre général est pas équilibré car il existe une différence de hauteur de 2 entre C et F. Vous avez besoin d'une spécification minimax (bien que l'algorithme réel puisse être moins complexe car il ne devrait y avoir que deux hauteurs autorisées).

26
Donal Fellows

Bonus de réponse à l'exercice. La solution simple Évidemment, dans une implémentation réelle, on pourrait envelopper ceci ou quelque chose d’éviter d’obliger l’utilisateur à inclure la hauteur dans sa réponse.

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     
22
Brian

Cela détermine uniquement si le niveau supérieur de l'arbre est équilibré. C'est-à-dire que vous pourriez avoir un arbre avec deux longues branches à l'extrême gauche et à l'extrême droite, avec rien au milieu, et cela redeviendrait vrai. Vous devez vérifier récursivement le root.left et root.right pour voir s'ils sont également équilibrés en interne avant de devenir vrais.

21
Jesse Rusak

Solution post-commande, traversez l’arbre une seule fois. La complexité temporelle est O (n), l'espace est O (1), c'est mieux qu'une solution descendante. Je vous donne une implémentation de version 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);
    }
}
19
Jiaji Li

La définition d'un arbre binaire équilibré en hauteur est la suivante:

Arbre binaire dans lequel la hauteur des deux sous-arbres de chaque nœud ne diffère jamais de plus de 1.

Ainsi, un arbre binaire vide est toujours équilibré en hauteur.
Un arbre binaire non vide est équilibré en hauteur si:

  1. Son sous-arbre de gauche est équilibré en hauteur.
  2. Son sous-arbre droit est équilibré en hauteur.
  3. La différence entre les hauteurs des sous-arbres gauche et droit n'est pas supérieure à 1.

Considérez l'arbre:

    A
     \ 
      B
     / \
    C   D

Comme on le voit, le sous-arbre de gauche de A est équilibré en hauteur (car il est vide), de même que son sous-arbre de droite. Mais toujours l'arbre n'est pas équilibré en hauteur car la condition 3 n'est pas remplie car la hauteur du sous-arbre gauche est 0 et la hauteur du sous-arbre droit est 2.

De plus, l’arbre suivant n’est pas équilibré en hauteur bien que les sous-arbres gauche et droit aient la même hauteur. Votre code existant retournera true pour cela.

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

Donc, le mot chaque dans la définition est très important.

Cela fonctionnera:

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

Lien Ideone

15
codaddict

Si l’arbre binaire est équilibré ou non, il peut être vérifié en passant par ordre de niveau:

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

Ceci est rendu beaucoup plus compliqué qu'il ne l'est réellement.

L'algorithme est le suivant:

  1. Soit A = profondeur du noeud de plus haut niveau
  2. Soit B = profondeur du noeud de niveau le plus bas

  3. Si abs (A-B) <= 1, l'arbre est équilibré

7
Mike

Ce que l’équilibre signifie dépend un peu de la structure en place. Par exemple, un arbre B ne peut pas avoir de nœuds à plus d'une certaine profondeur à partir de la racine, sinon moins, toutes les données vivent à une profondeur fixe à partir de la racine, mais il peut être déséquilibré si la distribution des feuilles entre les feuilles -mais un nœud est inégal. Liste de départ N'ont aucune notion d'équilibre, mais plutôt de probabilité pour atteindre des performances décentes. Les arbres de Fibonacci se déséquilibrent délibérément, retardant ainsi le rééquilibrage afin d'obtenir des performances asymptotiques supérieures en échange de mises à jour parfois plus longues. Les arbres AVL et rouge-noir attachent des métadonnées à chaque nœud pour atteindre un invariant d'équilibre de profondeur.

Toutes ces structures et plus sont présentes dans les bibliothèques standard des systèmes de programmation les plus courants (sauf python, RAGE!). La mise en œuvre d'une ou deux est une bonne pratique de programmation, mais ce n'est probablement pas une bonne utilisation du temps nécessaire pour la production, sauf si votre problème a des performances particulières qui ne doivent pas être satisfaites par des collections disponibles dans le commerce.

Remarque 1: La hauteur de tout sous-arbre est calculée une seule fois.

Remarque 2: Si le sous-arbre de gauche n'est pas équilibré, le calcul du sous-arbre de droite, contenant potentiellement un million d'éléments, est ignoré.

// 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;
}
4
Arun
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));
}
3
sdk

Voici une solution complète testée et élaborée en C # (désolé, je ne suis pas Java dev) (il suffit de copier/coller dans l’application de la console). Je sais que la définition de équilibré varie, de sorte que tout le monde n’aime pas ma testez les résultats, mais regardez simplement l'approche légèrement différente qui consiste à vérifier la profondeur/la hauteur dans une boucle récursive et à quitter le premier décalage sans enregistrer la hauteur/le niveau/la profondeur du nœud sur chaque nœud (en la maintenant uniquement dans un appel de fonction).

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

L'équilibrage dépend généralement de la longueur du chemin le plus long dans chaque direction. L'algorithme ci-dessus ne va pas le faire pour vous.

Qu'essayez-vous de mettre en œuvre? Il y a des arbres à auto-équilibrage autour (AVL/Rouge-noir). En fait, les arbres Java sont équilibrés.

3
Uri

Si cela concerne votre travail , je suggère:

  1. ne pas réinventer la roue et
  2. tiliser/acheter COTS au lieu de bidouiller avec des bits.
  3. Économisez votre temps/énergie pour résoudre les problèmes de votre entreprise.
3
Steven A. Lowe
#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;
}
2
Frank Raaj

RE: la solution de @ lucky utilisant un BFS pour effectuer un parcours ordre par niveau.

Nous parcourons l’arbre et gardons une référence à vars min/max-level qui décrivent le niveau minimum auquel un nœud est une feuille.

Je crois que la solution @lucky nécessite une modification. Comme suggéré par @codaddict, plutôt que de vérifier si un noeud est une feuille, nous devons vérifier si les enfants de gauche ou de droite sont nuls (pas les deux). Sinon, l'algorithme considérerait ceci comme un arbre équilibré valide:

     1
    / \
   2   4
    \   \
     3   1

En 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

Cette solution doit satisfaire à toutes les exigences énoncées dans la question initiale, opérant dans le temps O(n)) et O(n)). Le dépassement de mémoire serait dirigé. au tas plutôt que de faire sauter une pile d'appels récursive.

Alternativement, nous pourrions tout d’abord parcourir l’arbre pour calculer + les hauteurs maximales du cache pour chaque sous-arbre racine de manière itérative. Ensuite, dans une autre exécution itérative, vérifiez si les hauteurs en cache des sous-arbres gauche et droit de chaque racine ne diffèrent jamais de plus d'un. Cela fonctionnerait également dans le temps O(n)) et O(n)) de manière itérative afin de ne pas causer de débordement de pile.

1
vikasnair

Voici une version basée sur une traversée générique en profondeur d'abord. Devrait être plus rapide que l'autre réponse correcte et gérer tous les "défis" mentionnés. Toutes mes excuses pour le style, je ne connais pas vraiment Java.

Vous pouvez toujours le faire beaucoup plus rapidement en revenant plus tôt si max et min sont tous deux définis et ont une différence> 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
Potatoswatter
/* 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
Intuiter

Eh bien, vous avez besoin d’un moyen de déterminer les hauteurs de gauche et de droite, et si gauche et droite sont équilibrées.

Et je voudrais juste return height(node->left) == height(node->right);

Pour écrire une fonction height, lisez: Comprendre la récursion

1
tpdi

De quel genre d'arbre parles-tu? Il y a auto-équilibrage arbres. Vérifiez leurs algorithmes où ils déterminent s’ils doivent réorganiser l’arbre afin de maintenir l’équilibre.

1
lothar

Un arbre vide est équilibré en hauteur. Un arbre binaire non vide T est équilibré si:

1) Le sous-arbre gauche de T est équilibré

2) Le sous-arbre de droite de T est équilibré

3) La différence entre les hauteurs du sous-arbre gauche et du sous-arbre droit n’est pas supérieure à 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;
}

Complexité temporelle: O (n)

0
Saqlain

Voici ce que j'ai essayé pour l'exercice bonus d'Eric. J'essaie de décompresser mes boucles récursives et de revenir dès que je trouve un sous-arbre non équilibré.

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
Sudharsanan

Pour obtenir de meilleures performances, spécialement sur les arbres de grande taille, vous pouvez enregistrer la hauteur de chaque nœud. Il s’agit donc d’un compromis entre performances Vs et espaces:

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

Exemple d'implémentation de l'addition et idem pour la suppression

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;

}
0
Maher Rezeq
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
always
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
Pranay