web-dev-qa-db-fra.com

Tri des éléments dans les arbres binaires

Voici une question que j'ai récemment posée lors d'une interview. Un arbre binaire est donné avec une condition que chaque enfant gauche est 1 plus petit que la racine et que l'enfant droit est 1 plus grand. Voici un exemple d'arbre 

 A tree

Triez-le en O(1) et O(n) complexité du temps.

Voici les approches que j'ai suggérées:

  1. Utilisez un décompte pour conserver le décompte de chaque élément, puis revenez une fois le parcours terminé. O(n) time et O(n) complexité de l'espace.
  2. Utilisez l'encodage en longueur. Formez une chaîne lorsque l'élément est répété avec le nombre comme clé et compte comme valeur. Requiert de l’espace pour compter uniquement lorsque non est répété et ne nécessite donc pas d’espace supplémentaire en dehors du tableau, mais la complexité temporelle sera de O (n log n), car nous devons parcourir le tableau pour voir s’il existe.
  3. Enfin, j'ai suggéré la largeur première traversée. Nous avons besoin de O (log n) d’espace pour la file d’attente et de O(n) complexité temporelle (l’insertion supposée est O(1) liste chaînée).

Quelles sont vos approches?

14
user2223032

Correction d'un nœud feuille de l'arbre en tant que NewHead.

Ecrire une fonction Pop () supprime un noeud de l’arbre donné ..!

Ecrivez un noeud pop de telle sorte que vous ne le retiriez que quand il le sera! égal à NewHead.

Pour extraire la valeur de l’arborescence, insérez-la dans l’arborescence Nouvelle recherche binaire avec le nœud Nouvelle tête comme tête.

Donc, vous allez supprimer un élément de l’arbre et l’ajouter à un nouvel arbre de recherche.

Jusqu'à ce que la tête de l'arbre pointe NewHead.

Donc, tous vos éléments sont maintenant dans l’arborescence de recherche binaire pointant vers la nouvelle tête, qui sera 

évidemment dans l'ordre de tri.

Cela vous promet un tri dans O(NlogN).

1
MissingNumber

Une analyse

Compte tenu de votre définition d'un arbre binaire, nous avons ce qui suit,

Chaque nœud a un parent, un enfant L et un enfant R .. où:

L < N

R > N

P > N

Nous pouvons aussi faire ceci:

L < N AND R > N => L < N < R => L < R

L < N AND P > N => L < N < P => L < P

R > N AND P > N => N < MIN(P,R)

N < MIN(P,R) AND L < N => L < N < MIN(P,R)

Et maintenant essayons de l’étendre, N.L = Left-child of N:

N.L < N
N.R > N
N.P > N

N.L.L < N.L < MIN(N, N.L.R)
N.L.R > N.L > N.L.L

N.R.L < N.R < MIN(N, N.R.R)
N.R.R > N.R > N.R.L

IF N IS N.P LEFT-CHILD: N < N.P < MIN(N.P.P, N.P.R)

IF N IS N.P RIGHT-CHILD: N > N.P.R

Solution proposée

Ce problème semble complexe, mais ma solution utilisera le tri par fusion après l’insertion de valeurs dans un ordre de traversée gauche-droite-parent, ce qui aidera le tri à fusionner pour obtenir une complexité temporelle située entre son cas moyen et optimal, mais avec une astuce simple: les comparaisons que j'ai faites ci-dessus.

Nous collectons d’abord les nœuds d’arbres dans une liste, en utilisant la traversée Gauche-Droite-Parent, étant donné que: N.L < N < MIN(N.R, N.P) et en donnant au parent un poids supérieur en supposant que O(N.R) <= O(N.P) avec les valeurs décroît linéairement lorsque nous passons à gauche chaque fois .. > N.R.R > N.R > N > N.L > N.L.L > ...

Après avoir collecté les nœuds de l’arbre dans cet ordre de traversée, la liste contient des morceaux triés, ce qui facilitera le tri par fusion que nous utiliserons ensuite.

Cette solution fonctionne dans: Time = O(n log n + n), Space = O(n)

Voici l'algorithme écrit en Java (non testé) :

private class Node Comparable<Node>
{
    public Node R;
    public Node L;
    public int value;

    public Node (Node L, int val, Node R)
    {
        this.L = L;
        this.value = val;
        this.R = R;
    }

    @Override
    public int compareTo(Node other)
    {
        return ((other != null) ? (this.value-other.value) : 0);
    }
}

class Main
{
    private static Node head;

    private static void recursive_collect (Node n, ArrayList<Node> list)
    {
        if (n == null) return;
        if (n.left != null) recursive_collect (n.L, list);
        if (n.right != null) recursive_collect (n.R, list);
        list.add(n.value);
    }

    public static ArrayList<Node> collect ()
    {
        ArrayList<Node> list = new ArrayList<Node>();
        recursive_collect (head, list);
        return list;
    }

    // sorting the tree: O(n log n + n)
    public static ArrayList<Node> sortTree ()
    {
        // Collecting nodes: O(n)
        ArrayList<Node> list = collect();

        // Merge Sort: O(n log n)
        Collections.sort(list);

        return list;
    }

    // The example in the picture you provided
    public static void createTestTree ()
    {
        Node left1 = new Node (new Node(null,-2,null), -1, new Node(null,0,null));

        Node left2 = new Node (new Node(null,-1,null), 0, new Node(null,1,null));

        Node right = new Node (left2, 1, new Node(null,2,null));

        head = new Node (left1, 0, right);
    }

    // test
    public static void main(String [] args)
    {
        createTestTree ();

        ArrayList<Node> list = sortTree ();

        for (Node n : list)
        {
            System.out.println(n.value);
        }
    }
}
2
Khaled.K

Je suppose que vous recherchez DFS (recherche en profondeur d'abord). Dans le cadre de la recherche en profondeur d'abord, l'idée est de voyager aussi profondément que possible d'un voisin à l'autre avant de revenir en arrière. Ce qui détermine la profondeur possible, c'est que vous devez suivre les contours et que vous ne visitez aucun sommet. 

boost le fournit déjà: voir ici

1
Vijay

Utilisez le tri rapide. 

Les nœuds sont triés au niveau le plus bas dans plusieurs tableaux et ces tableaux d'éléments triés sont fusionnés à la fin.

Par exemple. 

Fonction quick_sort (noeud n)
1. Aller en mode gauche, si n'est pas null, appeler quick_sort dessus.
2. Aller aux éléments de droite, si n'est pas null, appeler quick_sort dessus.
3. Fusionner les résultats du tri du noeud gauche et du tri du noeud droit et du noeud actuel.
4. Renvoie le tableau fusionné.

0
Ganesh Bhosle