Metoda usuwająca wybrany element z drzewa

0

Witam mam za zadanie dopisać metodę usuwającą wybrany element z drzewa :) znalazłem w internecie coś takiego:

public void Delete(IComparable data)
{
   // odszukujemy węzeł do usunięcia (n)
   // przeszukujemy drzewo aż do odnalezienia węzła n
   Node current = root, parent = null;
   int result = **current.Value.CompareTo(data);**
   while (result != 0 && current != null)
   {            
      if (result > 0)
      {
         // current.Value > n.Value
         // a więc węzeł n może znajdować się
         // w lewym poddrzewie węzła current
         parent = current;
         current = current.Left;
      }
      else if (result < 0)
      {
         // current.Value < n.Value
         // a więc węzeł n może znajdować się
         // w prawym poddrzewie węzła current
         parent = current;
         current = current.Right;
      }

      result = **current.Value.CompareTo(data);**
   }

   // jeśli current == null, to nie ma co usuwać
   if (current == null)
      throw new Exception("Element do usunięcia nie został znaleziony w drzewie BST.");


   // zmienna current wskazuje teraz węzeł, który ma zostać 
   // usunięty, a zmienna parent wskazuje jego rodzica
   count--;
   
   // PRZYPADEK 1: jeśli węzeł current nie posiada prawego dziecka,
   // to węzłem wskazywanym przez rodzica zostaje lewe dziecko 
   // węzła current
   if (current.Right == null)
   {
      if (parent == null)
         root = current.Left;
      else
      {
         result = **parent.Value.CompareTo(current.Value);**
         if (result > 0)
            // parent.Value > current
            // a więc lewe poddrzewo węzła current zostaje
            // lewym poddrzewem węzła parent
            parent.Left = current.Left;
         else if (result < 0)
            // parent.Value < current.Value
            // a więc lewe poddrzewo węzła current zostaje
            // prawym poddrzewem węzła parent
            parent.Right = current.Left;
      }
   }
   // PRZYPADEK 2: jeśli prawe dziecko węzła current nie posiada lewego
   // dziecka, to prawe dziecko węzła current zastępuje węzeł current
   else if (current.Right.Left == null)
   {
      if (parent == null)
         root = current.Right;
      else
      {
         result = **parent.Value.CompareTo(current.Value);**
         if (result > 0)
            // parent.Value > current
            // a więc prawe poddrzewo węzła current zostaje
            // lewym poddrzewem węzła parent
            parent.Left = current.Right;
         else if (result < 0)
            // parent.Value < current.Value
            // a więc prawe poddrzewo węzła current zostaje
            // prawym poddrzewem węzła parent
            parent.Right = current.Right;
      }
   }   
   //  PRZYPADEK 3: Jeśli prawe dziecko węzła current posiada lewe
   // dziecko, węzeł current zostaje zastąpiony skrajnym lewym węzłem
   // swojego prawego poddrzewa
   else
   {
      // odszukujemy skrajny lewy węzeł prawego poddrzewa
      Node leftmost = current.Right.Left, lmParent = current.Right;
      while (leftmost.Left != null)
      {
         lmParent = leftmost;
         leftmost = leftmost.Left;
      }

      // prawe poddrzewo skrajnego lewego węzła zostaje
      // lewym poddrzewem węzła parent
      lmParent.Left = leftmost.Right;
      
      // lewe i prawe dziecko węzła current zostaje lewym i prawym
      // dzieckiem węzła skrajnie lewego
      leftmost.Left = current.Left;
      leftmost.Right = current.Right;

      if (parent == null)
         root = leftmost;
      else
      {
         result = **parent.Value.CompareTo(current.Value);**
         if (result > 0)
            // parent.Value > current
            // a więc węzeł skrajnie lewy zostaje lewym dzieckiem
            // węzła parent
            parent.Left = leftmost;
         else if (result < 0)
            // parent.Value < current.Value
            // a więc węzeł skrajnie lewy staje się prawym dzieckiem
            // węzła parent
            parent.Right = leftmost;
      }
   }
}

Mam pytanie do tego kodu. Jakie wartości powinienem umieścić w miejscach z grubą czcionką ??
Mógłby ktoś mi to troszkę objaśnić ?? Nie chcę gotowca!!

- < code> - msm

1

Nie chcę gotowca!!

Dah' - przecież już masz gotowca!
Jakbyś to sam napisał, to nie byłoby takiego problemu.

Mógłby ktoś mi to troszkę objaśnić ??

Zgaduję (biorąc pod uwagę nazwę metody), że CompareTo porównuje wartości; jeżeli jest mniejsza od zera, to znaczy, że parent.Value < current.Value, jeżeli jest równa zeru, to parent.Value = current.Value i analogicznie dla większej od zera.

0

Sam napisałem póki co coś takiego:

public void usuń_element2(double y)
        {   
            liść_drzewa Obecny;
            Obecny = Korzeń;
            

          ** if ( Obecny.Wartość == y)**
            {
                if (Obecny.prawy != null)
                {

                    Obecny = Obecny.prawy;
                     
                }
                else if (Obecny.lewy != null)
                {


                    Obecny = Obecny.lewy;
                    
                }
                else if (Obecny.lewy == null && Obecny.prawy == null)
                {
                    Obecny = null;
                }
            }
            else
            {
                if (Obecny.lewy != null) Obecny.lewy.usuń_element2(y);
                if (Obecny.prawy != null) Obecny.prawy.usuń_element2(y);
            }
        }

ale gdy włączam program wyskakuje mi : Object reference not set to an instance of an object. Dla miejsca które jest pogrubione. Mi się wydaje że wszystko jest git i powinno usuwać. Czemu wyskakuje mi taki komunikat ??

public void usuń_element2(double y)
        {
            liść_drzewa Obecny;
            Obecny = Korzeń;

            
            if (Obecny.Wartość == y)
            {
                if (Obecny.prawy == null)
                {
                        Obecny = Obecny.lewy;
                }
                if (Obecny.lewy == null)
                {
                        Obecny = Obecny.prawy;
                }
                else
                {
                    liść_drzewa LewyLewy = Obecny.lewy;
                    while (LewyLewy.lewy == null)
                    {
                        LewyLewy = LewyLewy.lewy;
                    }
                    Obecny = LewyLewy;
                    LewyLewy = null;
                }
            }
            else
            {
                if (Obecny.lewy != null) Obecny.lewy.usuń_element2(y);
                if (Obecny.prawy != null) Obecny.prawy.usuń_element2(y);
                if (Obecny.lewy == null && Obecny.prawy == null) Console.WriteLine("Nie ma takiego elementu w strukturze drzewa");
            }
        }

Poprawiony kod :) wcześniejszy jest zły(dopiero teraz to widzę XD ) ale nadal mi wyskakuje ten komunikat :) pomoże ktoś ??

Używaj tagu <code class="csharp"> [tutaj twój kod] </code>. Kodu wklejonego jako tekst się nie da czytać. - msm

2

Dalej błąd na: ?

 if (Obecny.Wartość == y)

Jeśli tak - to znaczy że Obecny == null. A że do obecnego przypisujesz Korzeń, to znaczy że Korzeń == null. Nie przypisujesz żadnej wartości do korzenia albo przypisujesz do niego null.

0

No dobra ten problem już wyeliminowany :) wszystko się ładnie kompiluje i nie wywala błędów :) ale nie usuwa niestety :) Myślicie że ten kod napisałem dobrze ?? I powinien mi usuwać podane elementy ?? Może jakieś podpowiedzi jeszcze co powinienem dodać ? Albo co dodatkowo zrobić żeby działało ?? Drzewo nie musi być zrównoważone. Chodzi tylko żeby usuwało jakiś wybrany element i reszta danych nie ulegała zniszczeniu :)

1
                if (Obecny.prawy == null)
                {
                        Obecny = Obecny.lewy;
                }

Wydaje mi się że to nie robi tego co byś chciał (tzn. nie jestem pewien co byś chciał, ale to na pewno nie przypisuje lewego poddrzewa do obecnego. Zmienia tylko jedną zmienną lokalną).
(nie dotyczy to sytuacji kiedy Obecny jest strukturą, ale tak u Ciebie nie ma)

Jeśli chcesz żeby w pewien sposób zmienił obecny węzeł na lewy to musisz zrobić coś w rodzaju:

Obecny.lewy = Obecny.lewy.lewy;
Obecny.prawy = Obecny.lewy.prawy;
Obecny.Wartość = Obecny.lewy.Wartość;

Nie przetestuję (bo nie mam reszty kodu drzewa), ale przynajmniej ta jedna rzecz będzie poprawiona.

Najpierw takie pytanie, chcesz usuwać z drzewa BST czy po prostu drzewa? Bo pierwszy kod który wkleiłeś to (przekomplikowane) usuwanie z BST, a Twój kod po prostu przegląda (tzn. wygląda jakby miał przeglądać) wszystkie elementy i usuwa wszystkie wystąpienia.

Swoją drogą, tak samo, bez sensu jest:

                    while (LewyLewy.lewy == null)
                    {
                        LewyLewy = LewyLewy.lewy;
                    }

Jeśli LewyLewy.Lewy będzie równy null, to masz tutaj 100% na NPE. Chodziło pewnie o != null.

A, i Twój kod często nie usunie wszystkich elementów (jeśli w drzewie jest > 1 element równy y).
Hmm, oraz będzie rzucał pełno ostrzeżeń że nie ma takiego elementu nawet jeśli był (i go usunie).

0

Dziękuję bardzo że mi pomagasz :) jestem dopiero na pierwszym roku zaocznej informatyki i mamy bardzo mało godzin z programowania :) więc trzeba samemu w domu siedzieć :) Wprowadziłem zmiany o których mówiłeś ale coś nadal nie gra :) Dodałem też coś na kształt wartownika :) Wiem że ten przykład który wkleiłem na początek jest z BST ale to ma być zwykłe drzewo :) Wklejam cały kod żeby łatwiej było się domyślić o co chodzi :)

Program:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Random Generator;
            liść_drzewa ElementyDrzewa;
            double p = 0.5, Max = 1, x, y;
            int i;
            string zmienna;



            Generator = new Random();
            x = 5;
           

            ElementyDrzewa = new liść_drzewa(x);
            ElementyDrzewa.Korzeń_Drzewa(x);

            for (i = 0; i < 10; i++)
            {

                
                ElementyDrzewa.dopiszRBT(p, Max, Generator);


            }
            Console.WriteLine("inorder:");
            ElementyDrzewa.inorder();

            Console.WriteLine("preorder:");
            ElementyDrzewa.preorder();

            Console.WriteLine("postorder:");
            ElementyDrzewa.postorder();
            Console.WriteLine("Jaki element chcesz usunąć??");
            zmienna = Console.ReadLine();
            y = Int32.Parse(zmienna);
    
            ElementyDrzewa.usuń_element(y);
            Console.WriteLine("-----");
            ElementyDrzewa.postorder();
            Console.ReadLine();
            ElementyDrzewa.usuń_drzewo();
            Console.WriteLine("-----");

            ElementyDrzewa = null;
            

            Console.ReadLine();







        }
    }
}

Klasy:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    public class liść_drzewa
    {
        public double Wartość;
        public liść_drzewa lewy;
        public liść_drzewa prawy;
        public liść_drzewa Korzeń;
        
       
        public liść_drzewa(double x)
        {
            Wartość = x;
            lewy = null;
            prawy = null;
        }
        public void Korzeń_Drzewa(double x)
        {
            Korzeń = new liść_drzewa(x);
        }

        public void preorder()
        {
            Console.WriteLine(Wartość);
            if (lewy != null) lewy.preorder();
            if (prawy != null) prawy.preorder();
        }

        public void inorder()
        {
            if (lewy != null) lewy.inorder();
            Console.WriteLine(Wartość);
            if (prawy != null) prawy.inorder();
        }

        public void postorder()
        {
            if (lewy != null) lewy.postorder();
            if (prawy != null) prawy.postorder();
            Console.WriteLine(Wartość);
        }

        public void dopiszRBT(double p, double Max, Random Generator)
        {
            double pomoc;
            double wart;

            pomoc = Generator.Next(10);
            wart = Max * Generator.Next(10);

            if (pomoc <= p)
            {
                if (lewy != null) lewy.dopiszRBT(p, Max, Generator); else lewy = new liść_drzewa(wart);
            }
            else
            {
                if (prawy != null) prawy.dopiszRBT(p, Max, Generator); else prawy = new liść_drzewa(wart);
            }
        }

        public bool czy_jest(double x)
        {
            bool wynik;
            wynik = false;
            if (x == Wartość) wynik = true;
            else
            {
                if (lewy != null) wynik = lewy.czy_jest(x);
                if (wynik == false)
                    if (prawy != null) wynik = prawy.czy_jest(x);
            }
            return wynik;
        }



        public void usuń_element(double y)
        {
            liść_drzewa Obecny;
            
            Obecny = Korzeń;
            int Usunięcie=0;
            
            
            if (Obecny.Wartość == y)
            {
                if (Obecny.prawy == null)
                {
                    Obecny.lewy = Obecny.lewy.lewy;
                    Obecny.prawy = Obecny.lewy.prawy;
                    Obecny.Wartość = Obecny.lewy.Wartość;
                    Usunięcie++;
                }
                else if (Obecny.lewy == null)
                {

                    Obecny.prawy = Obecny.prawy.prawy;
                    Obecny.lewy = Obecny.prawy.lewy;
                    Obecny.Wartość = Obecny.prawy.Wartość;
                    Usunięcie++;
                }
                else
                {
                    liść_drzewa LewyLewy = Obecny.lewy;
                    while (LewyLewy.lewy != null)
                    {
                        LewyLewy = LewyLewy.lewy;
                    }

                    Obecny = LewyLewy;
                    LewyLewy = null;
                    Usunięcie++;
                }
            }
            else
            {
                if (Obecny.lewy != null) Obecny.lewy.usuń_element(y);
                if (Obecny.prawy != null) Obecny.prawy.usuń_element(y);
                if (Obecny.lewy == null && Obecny.prawy == null && Usunięcie==0) Console.WriteLine("Nie ma takiego elementu w strukturze drzewa");
            }
        }

       


        public void usuń_drzewo()
        {
            if (lewy != null) lewy.usuń_drzewo();
            if (prawy != null) prawy.usuń_drzewo();
            lewy = null;
            prawy = null;
        }
    }


}

Bardzo będę wdzięczny jeśli jeszcze mi pomożesz :) polubiłem programowanie :) może z czasem to ja będę pomagał :)

0

Dasz mi może jeszcze jakieś podpowiedzi ?? bo sam na nic nie wpadam :)

2

Problem okazał się dziwniejszy niż się spodziewałem ;]. Napisałem w sumie dwa rozwiązania. Jedno opierało się na trzymaniu listy 'wolnych' poddrzew (i miało złożoność obliczeniową O(n), a pamięciową O(h) czyli tyle ile i tak trzeba na rekurencję), ale z powodu warunków brzegowych (a może ja za mało się postarałem z upraszczaniem) było dość skomplikowane.

W sumie praktycznie napisałem drzewo samemu, ale powinno Ci być prosto to do swojego drzewa dostosować:

class Tree<T> {
    Tree<T> left, right;
    T value;

    public Tree(T value) : this(value, null, null) { }

    public Tree(T value, Tree<T> left, Tree<T> right) {
        this.left = left;
        this.right = right;
        this.value = value;
    }

    /// <summary>Usuń wszystkie wystąpienia elementu z drzewa</summary>
    /// <remarks>nie zadziała jeśli wynikowe drzewo miałoby 0 elementów</remarks>
    public void Remove(T elem) {
        if (this.value.Equals(elem)) {
            var self = RemoveThis();
            if (self == null) {
                throw new InvalidOperationException(
                    "Niesttey, nie ma jak reprezentować pustego drzewa");
            }
        }
        if (left != null && left.value.Equals(elem)) { this.left = left.RemoveThis(); }
        if (left != null) { left.Remove(elem); }
        if (right != null && right.value.Equals(elem)) { this.right = right.RemoveThis(); }
        if (right != null) { right.Remove(elem); }
    }

    /// <summary>Usuń ten element ze struktury drzewa</summary>
    /// <returns>Wartość na którą 'zmienia' się obecne poddrzewo'</returns>
    Tree<T> RemoveThis() {
        if (left == null && right == null) {
            return null;
        } else if (left == null) {
            AssignThis(right);
        } else if (right == null) {
            AssignThis(left);
        } else {
            // tutaj lepiej losowo prawe i lewe
            // a jeszcze lepiej zapisać wysokość i balansować
            var oldLeft = left;
            AssignThis(right);
            var current = this;
            while (current.left != null) {
                current = current.left;
            }
            current.left = oldLeft;
        }
        return this;
    }

    /// <summary>Skopiuj wartości z innego drzewa</summary>
    void AssignThis(Tree<T> other) {
        this.left = other.left;
        this.right = other.right;
        this.value = other.value;
    }

    /// <summary>Łączna ilość elementów w drzewie</summary>
    public int Count { 
        get { return 1 + (left == null ? 0 : left.Count) + (right == null ? 0 : right.Count); } 
    }
}

class Program {
    static void Main(string[] args) {
        // testowe drzewo
        var t = new Tree<int>(1,
            new Tree<int>(2,
                new Tree<int>(3),
                new Tree<int>(4)),
            new Tree<int>(5,
                new Tree<int>(6),
                new Tree<int>(7)));

        // usuwanie po kolei wszystkich elementów
        Console.WriteLine(t.Count);
        t.Remove(1);
        Console.WriteLine(t.Count);
        t.Remove(2);
        Console.WriteLine(t.Count);
        t.Remove(3);
        Console.WriteLine(t.Count);
        t.Remove(4);
        Console.WriteLine(t.Count);
        t.Remove(5);
        Console.WriteLine(t.Count);
        t.Remove(6);
        Console.WriteLine(t.Count);
        try {
            t.Remove(7);
        } catch (InvalidOperationException ex) {
            Console.WriteLine("Sorry: {0}", ex.Message);
        }
    }
}
0

Wow super :) jestem Ci ogromnie wdzięczny :) :):) Zaraz sobie to wszystko dopasuję do swojego drzewa ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1