Wątek zablokowany 2015-05-02 21:24 przez furious programming.

Usuwanie poddrzewa w drzewie wielokierunkowym

0

Witam
Mam problem z usuwaniem drzew wielokierunkowych. Problem jaki muszę rozwiązać to usunięcie poddrzewa wraz z węzłem.
Nie wiem jak to rozwiązać ze względu na ilość możliwych potomków oraz rodzeństwa. Każdy węzeł może mieć różną ilość potomków oraz rodzeństwa.
W ten sposób stworzyłem typ potrzebny do stworzenia takiego drzewa:

Ptree= ^leaf;
leaf = record
        dana: string[50];
        left: Ptree;
        right: Ptree;
        down: Ptree;
        up: Ptree;
        end;   

Mam odpowiednie procedury potrzebne do tworzenia korzenia, dodawania elementów w różny sposób. Ale nie o to tutaj mi chodzi.
Potrzebuję o rady odnośnie przejścia przez wszystkie elementy od dołu aż do napotkania elementu, od którego mam wszystko usunąć.

Myślę, że trzeba to zrobić w ten sposób. Mam jakiś węzeł X. Muszę przejść w dół do liścia, który nie ma już potomków. Usuwam całe rodzeństwo (jeśli rodzeństwo ma dalej potomstwo - to tak dalej), na końcu usuwam ostatni element z potomstwa, przechodzę na pozom wyżej. Tam w analogiczny sposób postępuję.
Mam problem jak to "przekuć" na kod procedury. Wiem że trzeba rekurencyjne przejść przez kolejne poziomy drzewa.
Proszę o jakieś porady.

0

Chodzi ci o usunięcie wszystkiego czy usunięcie jednego elementu?

0

Chodzi mi o całe poddrzewo, ewentualnie całe drzewo. Pojedynczy element to jest dosyć proste, z tym sobie poradziłem. Chodzi o to by usunąć dany element wraz ze wszystkimi jego potomkami, rodzeństwem potomków itp.

0
procedure FreeNode(Node:Ptree);
begin
  if Node=nil then Exit;
  FreeNode(Node^.left);
  FreeNode(Node^.right);
  FreeNode(Node^.down);
  Dispose(Node);
end;
0

Dziękuję za pomoc, ale nadal mam problem z tym.
Wprowadziłem tą procedurę do programu. I tak, w przypadku bardziej złożonego drzewa już sypie błąd SIGSEGV. Właściwie nie ma problemów większych w przypadku usuwania korzenia. Natomiast gdy na drzewie jest korzeń oraz jeden element, usuwając ten element nadal mam "pozostałości" po tym elemencie.
Samą zasadę tej procedury rozumiem, przechodzi po tych wszystkich elementach tak jak niby być powinno, ale mimo wszystko coś jest nie tak.

0

W takim razie masz pochrzanione dodawanie.

0

Możliwe, chociaż przechodzenie w każdym kierunku działa poprawnie - mogę przechodzić w boki oraz do góry i na dół i nie ma problemu z tym.
Dodawanie nowych elementów mam podzielone na dwie procedury, która dodaje "potomka" - czyli poziom niżej - i na koniec listy. Druga procedura dodaje brata - czyli jak się znajdujemy na tym samym poziomie.

procedure dodajPotomka(var n: wskaznik; nazwa: string);
var
  nowy: wskaznik;
  tmp : wskaznik;
begin
  if n <> nil then
  begin
    new(nowy);
    nowy^.up := n;
    nowy^.left := nil;
    nowy^.right := nil;
    nowy^.down := nil;
    nowy^.nazwa := nazwa;

    if n^.down = nil then
    begin {jesli nie ma potomkow - wtedy element bedzie pierwszym elementem}
      n^.down := nowy;
      nowy^.up := n;
    end
    else
    begin
      tmp := n^.down;

      while tmp^.right <> nil do
        tmp := tmp^.right; {na koniec listy}

      tmp^.right := nowy;
      nowy^.left := tmp;
    end;
    writeln('Dodano: ', nowy^.nazwa);
  end
  else
    writeln('Blad');
end;

Wprowadziłem jeszcze poprawki pewne, ale też nic nie dały.
Tutaj natomiast druga procedura:

procedure dodajBrata(var n: wskaznik; nazwa: string; kierunek: char);
var
  nowy: wskaznik;
begin
  if n <> nil then
  begin
    new(nowy);
    nowy^.up := n^.up;
    nowy^.down := nil;
    nowy^.nazwa := nazwa;
    writeln('Dodano: ', nowy^.nazwa);

    if kierunek = 'l' then
    begin
      if n^.left = nil then
        n^.up^.down := nowy;

      nowy^.right := n;
      nowy^.left := n^.left;
      n^.left := nowy;
    end
    else
      if kierunek = 'p' then
      begin
        nowy^.left := n;
        nowy^.right := n^.right;
        n^.right := nowy;
      end
      else
        writeln('Zly kierunek');
  end;
end;
0

No tak, rzeczywiście takie formatowanie przyjazne nie jest. Ja się po prostu do takiego przyzwyczaiłem. Póki co programy piszę jedynie takie, które potrzebne mi są do nauki, formatowanie kodu stosuję tylko w "projektach".

Powracając do tematu, dla testów nie odpuszczam procedurę dodającą "brata", by wyeliminować dodatkowe problemy.
Przeanalizowałem ten kod dodający potomka i nie znalazłem błędów.

W procedurze najpierw wszystkie wskaźniki ustawiam na nil, by o jakimś czasem nie zapomnieć. Ustawiam również wskaźnik "up" dla nowego elementu na element przekazywany jako parametr.
Dalej, rozważam dwie możliwości - albo jest już jakiś potomek, albo potomków nie ma. Jeśli potomków nie mam, to nie zmieniam rodzeństwa dla nowego elementu, zmieniam tylko wskaźniki "up" i "down". Czyli w tym wypadku błędów nie ma raczej.
Drugi przypadek - są elementy. Za pomocą tej pętli while przechodzę na ostatni element na liście, tak aby jego wskaźnik "right" wskazywał na nil.

  tmp := n^.down;

  while tmp^.right <> nil do
    tmp := tmp^.right; {na koniec listy}

Potem już zmieniam wskaźnik right dla obecnego elementu oraz left dla nowego elementu. Też wszystkie wskaźniki są ustawione - odpowiednie wskaźniki mają wartość nil, inne na coś wskazują.
Reszta procedury już nie jest ważna. Tak więc - gdzie jest błąd?

0

Jako gość nie mogę komentować postów, więc muszę napisać nowy post.
W tej chwili kody tych dwóch procedur są dobrze sformatowane przez Furious Programming - i dzięki za to. I analizowałem właśnie ten sformatowany kod. Nie znalazłem tam błędu, chociaż w procedurze usuwającej również błędu nie powinno być. W takim wypadku nie wiem w czym może być problem.
Powtórzę też, że przy pomocy odpowiedniej procedury mogę z każdego elementu przejść bezproblemowo w dowolnym kierunku (oczywiście jeśli w docelowym kierunku znajduje się element), co raczej nie byłoby możliwe w przypadku błędów ze wskaźnikami.

1

Chyba rozumiem o co ci chodzi:

procedure FreeNode(Node:Ptree);
begin
  if Node^.right<>nil then Node^.right^.left:=Node^.left;
  if Node^.left<>nil then Node^.left^.right:=Node^.right
  else if Node^.up<>nil then Node^.up^.down=Node^.right;
  while Node^.down<>nil do FreeNode(Node^.down);
  Dispose(Node);
end;
0

Dziękuję za pomoc. Ta odpowiedź okazała się bardzo pomocna, gdyż ta wersja procedury działa poprawnie.
Jeszcze raz wielkie dzięki.

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