web-dev-qa-db-fra.com

Vérifier si un arbre binaire est une image miroir ou symétrique

Quel est l'algorithme de base pour tester si un arbre est symétrique. Puisqu'il s'agit d'un arbre binaire, je suppose que ce serait une définition récursive de la sorte

La question formelle est ci-dessous:

Un arbre binaire est une image miroir de lui-même si ses sous-arbres gauche et droit sont des images miroir identiques, c’est-à-dire si l’arbre binaire est symétrique.

  1
 / \
2   2

VRAI

   1
  / \
 2   2
  \
   3

FALSE

     1
   /   \
  2     2
 / \   / \
4   3 3   4

VRAI

       1
     /   \
    2     2 
   / \   / \
  3   4 3   4

FALSE

       1
     /   \
    2     2
   /       \
  3         3

VRAI

Dans un langage de programmation choisi, définissez une structure BTree class/C et une méthode associée pour vérifier si l’arborescence est une image miroir. Pour les langages à typage statique, vous pouvez supposer que les valeurs de nœud sont toutes des entiers.

Class/structure definition
BTree {
  BTree left;
  BTree right;
  int value;
}

Supposons que l'appelant suit la racine de l'arborescence et que la fonction isMirror () y soit invoquée.

De même, si vous définissez une classe, veillez à fournir un constructeur sans argument et des méthodes getter/setter si les éléments de données ne sont pas accessibles au public.

50
Joe Sars Grimaldi

Pourquoi ne pas appeler mirrorEquals (root.left, root.right) sur la fonction suivante: -

boolean mirrorEquals(BTree left, BTree right) {
  if (left == null || right == null) return left == null && right == null;
  return left.value == right.value
     && mirrorEquals(left.left, right.right)
     && mirrorEquals(left.right, right.left);
}

En gros, comparez le sous-arbre gauche et le sous-arbre droit inversé, en dessinant une ligne d'inversion imaginaire sur la racine.

103
gvijay

Solution 1 - Récursivement:

bool isMirror(BinaryTreeNode *a, BinaryTreeNode *b)
{
    return (a && b) ?  
        (a->m_nValue==b->m_nValue 
        && isMirror(a->m_pLeft,b->m_pRight) 
        && isMirror(a->m_pRight,b->m_pLeft)) :  
    (a == b);
}
bool isMirrorItselfRecursively(BinaryTreeNode *root) 
{
    if (!root)
        return true;

    return isMirror(root->m_pLeft, root->m_pRight);
}

Solution 2 - Itérativement:

bool isMirrorItselfIteratively(BinaryTreeNode *root) 
{
    /// use single queue
    if(!root) return true;
    queue<BinaryTreeNode *> q;
    q.Push(root->m_pLeft);
    q.Push(root->m_pRight);
    BinaryTreeNode *l, *r;
    while(!q.empty()) {
        l = q.front();
        q.pop();
        r = q.front();
        q.pop();
        if(l==NULL && r==NULL) continue;
        if(l==NULL || r==NULL || l->m_nValue!=r->m_nValue) return false;
        q.Push(l->m_pLeft);
        q.Push(r->m_pRight);
        q.Push(l->m_pRight);
        q.Push(r->m_pLeft);
    }

    return true;
}
9
herohuyongtao

La solution récursive de @gvijay est très claire et voici une solution itérative.

Examinez chaque rangée de l'arbre de haut en bas et vérifiez si les valeurs correspondent à un palindrome. S'ils le sont tous, alors, oui, c'est un miroir. Vous devrez implémenter un algorithme pour visiter chaque ligne et inclure des valeurs NULL pour les arbres fragmentés. En pseudocode:

boolean isMirror(BTree tree) {
  foreach (List<Integer> row : tree.rows() {
    if (row != row.reverse()) return false;
  }
  return true;
}

L'astuce consiste à concevoir un algorithme permettant d'itérer les lignes d'un arbre en tenant compte du fait que les arbres clairsemés doivent avoir des valeurs NULL en tant que substituants. Cette implémentation Java semble correcte:

public static boolean isMirror(BTree root) {
  List<BTree> thisRow, nextRow;
  thisRow = Arrays.asList(root);
  while (true) {
    // Return false if this row is not a palindrome.
    for (int i=0; i<thisRow.size()/2; i++) {
      BTree x = thisRow.get(i);
      BTree y = thisRow.get(thisRow.size()-i-1);
      if ((x!=null) && (y!=null)
          && (x.value != y.value))
        return false;
      if (((x==null) && (y!=null))
          || (x!=null) && (y==null))
        return false;
    }
    // Move on to the next row.
    nextRow = new ArrayList<BTree>();
    for (BTree tree : thisRow) {
      nextRow.add((tree==null) ? null : tree.lt);
      nextRow.add((tree==null) ? null : tree.rt);
    }
    boolean allNull = true;
    for (BTree tree : nextRow) {
      if (tree != null) allNull = false;
    }
    // If the row is all empty then we're done.
    if (allNull) return true;
    thisRow = nextRow;
  }
}
4
maerics

Solutions récursives et itératives en Java en utilisant les approches discutées ci-dessus

Récursif  

public Boolean isSymmetric(TreeNode root) {
    if (root == null) {
        return true;
    }

    return isSymmetricInternal(root.left, root.right);
}

private Boolean isSymmetricInternal(TreeNode leftNode,
        TreeNode rightNode) {

    boolean result = false;

    // If both null then true
    if (leftNode == null && rightNode == null) {
        result = true;
    }

    if (leftNode != null && rightNode != null) {
        result = (leftNode.data == rightNode.data)
                && isSymmetricInternal(leftNode.left, rightNode.right)
                && isSymmetricInternal(leftNode.right, rightNode.left);
    }

    return result;
}

Itératif utilisant LinkedList comme Queue 

private Boolean isSymmetricRecursive(TreeNode root) {
    boolean result = false;

    if (root == null) {
        return= true;
    }

    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root.left);
    queue.offer(root.right);

    while (!queue.isEmpty()) {
        TreeNode left = queue.poll();
        TreeNode right = queue.poll();

        if (left == null && right == null) {

            result = true;

        }
        else if (left == null || 
                right == null || 
                left.data != right.data) {
            // It is required to set result = false here
            result = false;
            break;
        }

        else if (left != null && right != null) {
            queue.offer(left.left);
            queue.offer(right.right);

            queue.offer(left.right);
            queue.offer(right.left);
        }
    }

    return result;
}

Cas de test  

    @Test
public void testTree() {

    TreeNode root0 = new TreeNode(1);
    assertTrue(isSymmetric(root0));
    assertTrue(isSymmetricRecursive(root0));

    TreeNode root1 = new TreeNode(1, new TreeNode(2), new TreeNode(2));
    assertTrue(isSymmetric(root1));
    assertTrue(isSymmetricRecursive(root1));

    TreeNode root2 = new TreeNode(1,
            new TreeNode(2, null, new TreeNode(3)), new TreeNode(2));
    assertFalse(isSymmetric(root2));
    assertFalse(isSymmetricRecursive(root2));

    TreeNode root3 = new TreeNode(1, new TreeNode(2, new TreeNode(4),
            new TreeNode(3)), new TreeNode(2, new TreeNode(3),
            new TreeNode(4)));
    assertTrue(isTreeSymmetric(root3));
    assertTrue(isSymmetricRecursive(root3));

    TreeNode root4 = new TreeNode(1, new TreeNode(2, new TreeNode(3),
            new TreeNode(4)), new TreeNode(2, new TreeNode(3),
            new TreeNode(4)));
    assertFalse(isSymmetric(root4));
    assertFalse(isSymmetricRecursive(root4));
}

Nœud d'arbre class 

public class TreeNode {

int data;

public TreeNode left;
public TreeNode right;

public TreeNode(int data){
    this(data, null, null);
}

public TreeNode(int data, TreeNode left, TreeNode right)
{
    this.data = data;
    this.left = left;
    this.right = right;
}
}
4
Rohit

Voici une solution C++ par gvijay

bool isMirrorTree(BTnode* LP, BTnode* RP)
{
    if (LP == NULL || RP == NULL) // if either is null check that both are NULL
    { 
        return ( LP == NULL && RP == NULL );
    } 
    // check that data is equal and then recurse
    return LP->data == RP->data && 
           isMirrorTree( LP->left, RP->right ) && 
           isMirrorTree( LP->right, RP->left );
}
2
user656925

MODIFIER

Comme il a été souligné dans les commentaires, ma première version de l’algorithme a échoué pour certaines entrées. Je ne vais pas réinventer la roue, je vais juste fournir une réponse Python en utilisant l'algorithme correct @gvijay. Tout d'abord, une représentation de l'arbre binaire:

class BTree(object):
    def __init__(self, l, r, v):
        self.left  = l
        self.right = r
        self.value = v
    def is_mirror(self):
        return self._mirror_equals(self.left, self.right)
    def _mirror_equals(self, left, right):
        if left is None or right is None:
            return left is None and right is None
        return (left.value == right.value
                and self._mirror_equals(left.left, right.right)
                and self._mirror_equals(left.right, right.left))

J'ai testé le code ci-dessus en utilisant tous les exemples d'arbres de la question et les arbres qui renvoyaient des résultats incorrects, comme indiqué dans les commentaires. Maintenant, les résultats sont corrects pour tous les cas:

root1 = BTree(
    BTree(None, None, 2),
    BTree(None, None, 2),
    1)
root1.is_mirror() # True

root2 = BTree(
    BTree(None, BTree(None, None, 3), 2),
    BTree(None, None, 2),
    1)
root2.is_mirror() # False

root3 = BTree(
    BTree(
        BTree(None, None, 4),
        BTree(None, None, 3),
        2),
    BTree(
        BTree(None, None, 3),
        BTree(None, None, 4),
        2),
    1)
root3.is_mirror() # True

root4 = BTree(
    BTree(
        BTree(None, None, 3),
        BTree(None, None, 4),
        2),
    BTree(
        BTree(None, None, 3),
        BTree(None, None, 4),
        2),
    1)
root4.is_mirror() # False

root5 = BTree(
    BTree(BTree(None, None, 3), None, 2),
    BTree(None, BTree(None, None, 3), 2),
    1)
root5.is_mirror() # True

root6 = BTree(BTree(None, None, 1), None, 1)
root6.is_mirror() # False

root7 = BTree(BTree(BTree(None, None, 1), None, 2), None, 1)
root7.is_mirror() # False
2
Óscar López

Si quelqu'un a besoin d'une version Swift, en voici une.

Une autre approche consiste simplement à inverser l’un des sous-arbres et à comparer les deux sous-arbres résultants d’une manière simple.

func compareTrees(left: TreeNode?, right: TreeNode?) -> Bool {
    var res = false
    if left == nil && right == nil {return true}
    if left != nil && right != nil {
        res = left!.val == right!.val &&
              compareTrees(left!.left, right: right!.left) &&
              compareTrees(left!.right, right: right!.right)
    }
    return res
}

func invertTree(node: TreeNode?) {
    if node == nil {return}

    var tmp = node!.left
    node!.left = node!.right
    node!.right = tmp

    invertTree(node!.left)
    invertTree(node!.right)
}

// and run it as:
if root == nil {print("Y")}
invertTree(root!.right)
compareTrees(root!.left, right: root!.right) ? print("Y") : print("N")
1
aquio

Voici la solution en ce qui concerne C-COde

isMirror(root)
{ 
Symmetric(root->left, root->right);
}

Symmetric(root1,root2)
{
 if( (root1->left EX-NOR root2->right) && (root1->right EX-NOR root2->left) && (root1->value==root2->value) )        
//exnor operation will return true if either both present or both not present 
// a EX-NOR b =(!a && !b) || (a && b))
        {
    Symmetric(root1->left, root2->right);
    Symmetric(root1->right, root2->left);
        }    
else return false;
}
1

classe publique SymmetricTree {

/**
 * @param args
 */
public static void main(String[] args) {
    // TODO Auto-generated method stub
    //int[] array = {1,2,2,3,4,4,3};
    /*
     *                  1
     *                 / \
     *                /   \
     *               /     \
     *              2       2
     *             / \     / \
     *            /   \   /   \
     *           3     4 4     3
     * 
     * */
    //int[] array = {1,2};
    BinaryTree bt=new BinaryTree();
    bt.data=1;
    bt.left = new BinaryTree(2);
    bt.right = new BinaryTree(2);
    bt.left.right = new BinaryTree(3);
    bt.right.right = new BinaryTree(3);
    //bt=BinaryTree.buildATree(bt, array);
    System.out.print(isSymmetric(bt));
    BinaryTree.inOrderTraversal(bt);
}
public static boolean isSymmetric(BinaryTree root){
    if(root==null)
        return true;
    return isSymmetricLR(root.left,root.right);
}
public static boolean isSymmetricLR(BinaryTree left, BinaryTree right){
    if(left == null && right == null)
        return true;
    if(left!=null && right!=null)
        return (left.data == right.data) &&
                (isSymmetricLR(left.left, right.right)) &&
                (isSymmetricLR(left.right, right.left));
    return false;
}

}

0
Mohit Motiani

Je pensais ajouter une solution en Python que certaines personnes trouveraient plus facile à comprendre que d’autres approches. L'idée est:

  1. add +1 à la valeur renvoyée par l'enfant de gauche.
  2. add -1 à la valeur renvoyée par le bon enfant.
  3. renvoyer l+r au parent

Donc, si l+r == 0 pour un noeud de l’arbre, le sous-arbre ancré à ce noeud est symétrique. Par conséquent, toute l’arbre n’est symétrique que si l+r == 0 à la racine.

def check(root):
    l = check(root.left)+1 if root.left else 0
    r = check(root.right)-1 if root.right else 0
    return l+r

def is_symmetric(root):
    return root is not None and check(root) == 0
0
Wahab Ali

en utilisant python

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def isSymmetric(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        def helper(root1, root2):
            if not root1 and not root2: return True
            if not root1 or not root2: return False            
            if root1.val != root2.val: return False
            if helper(root1.left, root2.right): return helper(root1.right, root2.left)
            return  False
        return helper(root, root)
0
MdNazmulHossain

Approche légèrement différente.

Que diriez-vous de faire une traversée en ordre de l’arbre binaire en stockant tout le contenu dans une structure de données comme une chaîne/tableau.

Une fois le parcours terminé, vérifiez si les éléments de votre tableau forment un palindrome . Pas aussi efficace en termes d'espace (la récursivité prend O (log (n)), cette méthode indique O(n)) travailler aussi bien.

0
user2884123

Solution itérative utilisant une approche légèrement différente en python. Utilisez queue1 pour stocker les enfants laissés dans l'ordre de gauche à droite et queue2 pour stocker les enfants de droite dans l'ordre de droite à gauche et effectuez une comparaison pour l'égalité. 

def isSymmetric(root):
    if not root:
        return True
    if not (root.left or root.right):
        return True
    q1 = collections.deque([root.left])
    q2 = collections.deque([root.right])
    while q1 and q2:
        n1 = q1.popleft()
        n2 = q2.popleft()
        if n1 is None and n2 is None:
            continue
        if (n1 is None) ^ (n2 is None):
            return False
        if n1.val != n2.val:
            return False
        q1.append(n1.left)
        q1.append(n1.right)
        q2.append(n2.right)
        q2.append(n2.left)
    if not (q1 and q2):
        return True
    return False
0
Bilal