• Nie Znaleziono Wyników

Wstęp do programowania Drzewa – podstawowe techniki

N/A
N/A
Protected

Academic year: 2021

Share "Wstęp do programowania Drzewa – podstawowe techniki"

Copied!
29
0
0

Pełen tekst

(1)

© Piotr Chrząstowski-Wachtel

Wstęp do programowania

Drzewa – podstawowe techniki

(2)

Drzewa wyszukiwań

Drzewa często służą do przechowywania

informacji. Jeśli uda sie nam stworzyć drzewo o niewielkiej wysokości i strukturze pozwalającej w każdym węźle określić, czy iść w lewo czy w

prawo w poszukiwaniu jakiejś danej, to dostęp do niej będzie szybki.

Takie warunki spełniają drzewa binarnych wyszukiwań (BST: Binary Search Trees)

(3)

3

Drzewa binarnych wyszukiwań

W drzewach binarnych wyszukiwań przechowujemy dane ze zbioru liniowo uporządkowanego (A,<).

Przestrzegamy następujących zasad:

Każdą wartość reprezentujemy w drzewie co najwyżej raz,

Na lewo od każdego węzła są podwieszone wyłącznie wartości mniejsze od niego, a na prawo większe.

(4)

Drzewa binarnych wyszukiwań

Przy tak określonych zasadach wiemy, jak szukać danej wartości w drzewie:

° Albo drzewo jest puste i wtedy jej nie ma

° Albo drzewo jest niepuste i wtedy:

albo wartość jest w korzeniu

albo wartość jest mniejsza od tej w korzeniu, więc szukamy jej w lewym poddrzewie

albo wartość jest większa od tej w korzeniu, więc szukamy jej w prawym poddrzewie

(5)

5

Wyszukiwanie wartości w drzewie zwykłym

function jest(x:typ; d:drzewo):Boolean;

begin

if d=nil then jest := false

else if d^.w=x then jest := true

else jest := jest(x,d^.lsyn) or jest(x,d^.psyn)

end;

{leniwie}

{Koszt O(n)}

(6)

Wyszukiwanie wartości w drzewie BST

function jest(x:typ; d:drzewo):Boolean;

begin

if d=nil then jest := false

else if d^.w=x then jest := true else if x<d^.w then

jest := jest(x,d^.lsyn) else jest := jest(x,d^.psyn) end;

(7)

7

Wstawianie wartości do drzewa BST

procedure wstaw(x:typ; var d:drzewo);

begin

if d=nil then begin new(d);

d^.w:=x;

d^.lsyn:=nil; d^.psyn:=nil;

end

else if x<d^.w then wstaw(x,d^.lsyn) else if x>d^.w then wstaw(x,d^.psyn) end; {Gdy x jest już w drzewie, to go

ignorujemy! Koszt całości O(h)}

(8)

Usuwanie wartości z drzewa BST

Usuwanie wartości z drzewa BST jest nieco trudniejsze.

Brak po danej wartości trzeba uzupełnić jednym z dwóch elementów: albo największym z lewego

poddrzewa albo najmnijeszym z prawego. Szczegóły na ASD.

Ciekawe jest pytanie o to, jaka będzie średnia głębokość węzła (albo średnia wysokość drzewa) przy budowaniu drzewa BST poprzez losowe wkładanie (i ewentualne wyjmowanie) kolejnych wartości.

(9)

9

Twierdzenie o infiksie w BST

Wartości drzewa BST wypisane w obiegu infiksowym sa posortowane rosnąco.

Dowód: indukcja ze wzgledu na liczbę węzłów drzewa:

dla drzewa pustego OK, bo pusty ciąg jest posortowany

dla drzewa niepustego mamy korzeń z podwieszonymi

dwoma poddrzewami o mniejszej liczbie węzłów – stosuje się więc do nich założenie indukcyjne. Zatem wartości

lewego poddrzewa, wypisywane są jako pierwsze – rosnąco na mocy założenia indukcyjnego – potem idzie wartość z korzenia – jeszcze większa (def. BST), a na końcu rosnące wartości z prawego poddrzewa. ¤

(10)

Techniki przydatne przy przetwarzaniu drzew

Pokażemy teraz na przykładzie obliczania wysokości drzewa dwie techniki specyficzne dla drzew.

Wysokość drzewa możemy zdefiniować rekurencyjnie tak:

h(nil) = -1

h(d) = max(h(d^.lsyn),h(d^.psyn)) + 1

Ta definicja przekłada się natychmiast na funkcję rekurencyjną:

(11)

11

Wysokość drzewa – wprost xz definicji

function h(d:drzewo):Integer;

begin

if d=nil then h := -1

else h:=max(h(d^.lsyn),h(d^.psyn))+1 end;

{Koszt O(n)}

Mamy tu oczywiście ukryty obieg postfiksowy: nie możemy poznac wysokości ojca, zanim nie poznamy wysokości jego synów.

(12)

Metoda spaceru

Jest to metoda, za pomocą której przechodzimy drzewo stosownym (dowolnym) obiegiem i zauważamy pewne fakty, które nas interesują zapamiętując ich efekty na zewnętrznej zmiennej.

W przypadku wysokości interesuje nas największa głębokość, więc przechodząc drzewo dowolnym

obiegiem po prostu za każdym razem, gdy wejdziemy do nowego węzła, aktualizujemy głębokośc, na której sie znajdujemy (aktg) i sprawdzamy, czy nie pobiliśmy

(13)

13

Wysokość drzewa spacerem - prefiksowo

function wysokośćSpacerowa(d:drzewo):Integer;

var aktg,maxg:Integer;

procedure spacer(d:drzewo);

begin if d<>nil then begin {spacer}

aktg:=aktg+1;

if aktg>maxg then maxg:=aktg;

spacer(d^.lsyn);

spacer(d^.psyn);

aktg:=aktg-1; {Bardzo ważne!!!}

end; {spacer}

begin {WysokośćSpacerowa}

aktg:=-1; maxg:=-1;

spacer(d);

WysokośćSpacerowa:=maxg end;

(14)

Wysokość drzewa spacerem - infiksowo

function wysokośćSpacerowa(d:drzewo):Integer;

var aktg,maxg:Integer;

procedure spacer(d:drzewo);

begin if d<>nil then begin {spacer}

aktg:=aktg+1;

spacer(d^.lsyn);

if aktg>maxg then maxg:=aktg; {“infiksowo”}

spacer(d^.psyn);

aktg:=aktg-1; {Bardzo ważne!!!}

end; {spacer}

begin {WysokośćSpacerowa}

aktg:=-1; maxg:=-1;

(15)

15

Wysokość drzewa spacerem - postfiksowo

function wysokośćSpacerowa(d:drzewo):Integer;

var aktg,maxg:Integer;

procedure spacer(d:drzewo);

begin if d<>nil then begin {spacer}

aktg:=aktg+1;

spacer(d^.lsyn);

spacer(d^.psyn);

if aktg>maxg then maxg:=aktg; {“postfiksowo”}

aktg:=aktg-1; {Bardzo ważne!!!}

end; {spacer}

begin {WysokośćSpacerowa}

aktg:=-1; maxg:=-1;

spacer(d);

WysokośćSpacerowa:=maxg end;

(16)

Przekazywanie parametrów w obiegu prefiksowym

procedure Nadsumuj(d:drzewo);

procedure DodajGore(d:drzewo; k:Integer);

{Dosumowuje wartość parametru k do węzła d i nową wartość przekazuje rekurencyjnie obu synom.}

begin

if d<> nil then begin

d^.w:=d^.w+k;

DodajGore(d^.lsyn,d^.w);

DodajGore(d^.psyn,d^.w);

end;

end;

(17)

17

Przekazywanie parametrów w obiegu postfiksowym

procedure Podsumuj(d:drzewo);

var dowol:Integer;

procedure DodajDół(d:drzewo; var k:Integer);

var zdolu:Integer;

begin

if d=nil then k:=0 {To przypisanie jest konieczne!}

else begin

DodajDol(d^.lsyn, zdolu);

d^.w:=d^.w+zdolu;

DodajDol(d^.psyn, zdolu);

d^.w:=d^.w+zdolu;

k:=d^.w end {else};

end; {DodajDół}

begin {Podsumuj}

DodajDol(d,dowol) {Można było uniknąć dowola wstawiając tu np.

d^.w, ale to by zaciemniło kod}

end;

(18)

Dobre rady dotyczące stylu programowania

Procedura udostępniana użytkownikowi powinna mieć tyle parametrów, ile jest to konieczne z punktu widzenia specyfikacji zadania. Wszelkie nadmiarowe parametry ułatwiające zaprogramowanie są wyrazem niechlujstwa.

Złą praktyką programistyczną jest nieumieszczanie istotnych parametrów w nagłówku i korzystanie ze zmiennych globalnych.

Rekurencja i iteracja niezbyt się lubią nawzajem.

Najczęściej dobrze jest zdecydować się na jedną z nich.

(19)

19

Dobre rady dotyczące stylu programowania

W procedurach rekurencyjnych dotyczących drzew

zawsze sprawdzamy na początku, czy argument nie jest nilem.

Przy przekazywaniu informacji od korzenia w dół używamy najczęściej porządku prefiksowego, a informację przekazujmy przez wartość

Przy przekazywaniu informacji z dołu drzewa do góry używamy najczęściej porządku postfiksowego i

przekazujemy ją albo przez parametry wołane przez zmienną albo za pomocą wywołania funkcji.

(20)

Podsumuj w obiegu postfiksowym z funkcją

procedure Podsumuj(d:drzewo);

var dowol:Integer;

function SumaPotomków(d:drzewo):Integer;

begin

if d=nil then SumaPotomków:=0 { koniecznie!}

else begin

d^.w := SumaPotomków(d^.lsyn)+SumaPotomków(d^.psyn) +d^.w;

SumaPotomków:=d^.w end {else};

end; {DodajDół}

(21)

21

Drzewa ogólne

Jeśli drzewoma stopień większy niz dwa, to możemy je implementować w następujący sposób: w kazdym węźle przechowujemy poza wartością w:typ dwa wskaźniki: do pierwszego syna z lewej oraz do listy braci.

(22)

Drzewa ogólne

type drzewoOg = ^węzeł;

węzeł = record

w : typ;

lsyn,brat : drzewoOg end;

var

5

d

(23)

23

Drzewa ogólne

Jeśli drzewo ma stopień większy niz dwa, to możemy je implementować w następujący sposób: w kazdym węźle przechowujemy poza wartością w:typ dwa wskaźniki: do pierwszego syna z lewej oraz do listy braci.

(24)

Drzewa ogólne

Widzimy, że różnica tak naprawdę polega na innej interpretacji takich samych danych.

W rzeczywistości ta interpretacja oznacza, że w korzeniu pole brat jest zawsze puste.

... chyba że nie jest puste i wtedy w naturalny sposób mamy reprezentację lasu – ważnego uogólnienia drzew (drzewo wtedy, to las z jednym elementem).

Procedury obiegu drzew ogólnych są analogiczne do takich procedur dla drzew binarnych

(25)

25

Drzewa ogólne - obiegi

Jeśli chodzi o obiegi prefiksowy i postfiksowy, to odpowiedniość jest jednoznaczna

Obiegów infiksowych można sobie wyobrazić więcej – tyle ile jest wynosi stopień drzewa minus 1. W zasadzie nie używa sie obiegów infiksowych w przypadku drzew ogólnych

(26)

Liczymy liczbę liści w drzewie ogólnym

function LiczbaLiści(d: drzewoOg): integer;

var lewo, prawo: integer;

begin

if d = nil then LiczbaLiści:=0

else if d^.lsyn=nil then LiczbaLiści:=1

else LiczbaLisci := LiczbaLiści(d^.lsyn) + Liczbaliści(d^.brat);

end end;

(27)

27

Liczymy liczbę liści w drzewie binarnym

function LiczbaLiści(d: drzewo ): integer;

var lewo, prawo: integer;

begin

if d = nil then LiczbaLiści:=0

else if d^.lsyn=d^.psyn then LiczbaLiści:=1

else LiczbaLiści := LiczbaLiści(d^.lsyn) + Liczbaliści(d^.psyn);

end end;

(28)

Przykład – liczenie wysokości drzewa ogólnego

function h(d:drzewoOg):Integer;

begin

if d=nil then h := -1

else h:=max(h(d^.lsyn)+1,h(d^.brat)) end;

(29)

29

Przykład – liczenie wysokości drzewa bin. - przypomnienie

function h(d:drzewo ):Integer;

begin

if d=nil then h := -1

else h:=max(h(d^.lsyn),h(d^.psyn))+1 end;

Cytaty

Powiązane dokumenty

• wykonać klasyfikację dla wybranych danych wielowymiarowych ze strony uci za pomocą metody drzewa klasyfikacyjnego, porównać jakość klasyfikacji na danych testowych z

Lasy losowe (random forests) b˛ed ˛ ace uogólnieniem idei drzew decyzyjnych zalicza si˛e do procedur agreguj ˛ acych (ensemble method). Działanie lasów losowych polega na

Dzień Pogoda Temperatura Wilgotność Wiatr Tenis D9 Słonecznie Zimno Normalna Słaby Tak D11 Słonecznie Przyjemnie Normalna Silny Tak D1 Słonecznie Gorąco Wysoka Słaby Nie

Wypisz wszystkie elementy tego drzewa BST obchodząc je w porządku INORDER, PREORDER, POSTORDER.. Zadanie 6 Zapisz funkcję szukającą poprzednika w

FN (false negative) - liczba nieprawidłowych klasyfikacji stanu patologicznego jako normalnego. TP (true positive) - liczba prawidłowych klasyfikacji

FN (false negative) - liczba nieprawidłowych klasyfikacji stanu patologicznego jako normalnego. TP (true positive) - liczba prawidłowych klasyfikacji stanu

• Podział drzewa kończy się gdy liczba obiektów do podziału jest już mniejsza niż pewna wartości progowa.. • Przycinanie drzew jest możliwe już po

Klasyfikacja odbywa się poprzez przeglądanie drzewa od korzenia do liści przez krawędzie opisane wartościami atrybutów... Przykład: automatyczny podział kierowców na