Jakas reklama 

 

Binarne drzewo poszukiwań o wielkości równej 9, a wysokości równej 3; wierzchołek '8' jest tu korzeniem, a wierzchołki '1', '4', '7' i '13', to liście

Binarne drzewo poszukiwań (skrót BST, z ang. Binary Search Tree) – dynamiczna struktura danych w postaci drzewa binarnego stosowana do szybkiego wyszukiwania. Drzewa BST służą do realizacji bardziej abstrakcyjnych struktur danych, np. zbiorów, czy słowników.

W każdym z węzłów drzewa BST przechowywany jest klucz (klucze są unikatowe). Wartość klucza jest zawsze nie mniejsza niż wartości wszystkich kluczy z lewego poddrzewa, a nie większa niż wartości wszystkich kluczy z prawego poddrzewa (relacja może być odwrócona, to kwestia umowy). Relacje niemniejszości i niewiększości można zamienić odpowiednio na relacje większości i mniejszości, ale ograniczamy się wówczas do przypadku unikalności kluczy (wykluczamy klucze, które mogą się powtarzać). Przechodząc drzewo metodą inorder, uzyskuje się ciąg wartości posortowanych niemalejąco (lub rosnąco w przypadku unikatowych kluczy).

Pesymistyczny koszt każdej z operacji na drzewie BST (o liczbie węzłów n), tj. wyszukiwania, dodania lub usunięcia klucza, zależy od wysokości drzewa i wynosi

Dlatego w praktyce często lepiej zastosować drzewa BST o dodatkowych właściwościach, np. AVL lub drzewa czerwono-czarne, które poprzez nakładanie pewnych dodatkowych warunków gwarantują, że drzewo będzie zrównoważone (na tyle, na ile to możliwe), dając tym samym logarytmiczny czas dostępu; dzieje się to kosztem większego skomplikowania procedur wstawiających i usuwających klucze.

Spis treści

edytuj Wyszukiwanie klucza

Parametrem wejściowym procedury wyszukującej jest żądana wartość klucza k oraz węzeł – korzeń drzewa. Algorytm iteracyjny przedstawia się następująco:

  1. Jeśli węzeł = nil (puste poddrzewo) – w drzewie nie ma klucza k, przejdź do 5
  2. Jeśli kluczwęzeł = k – znaleziono węzeł o podanej wartości klucza, przejdź do 5
  3. Jeśli kluczwęzeł < k – klucz, o ile istnieje w drzewie, zlokalizowany jest w prawym poddrzewie węzła:
    • węzeł := prawy_synwęzeł
    • przejdź do 1
  4. Jeśli kluczwęzeł > k – klucz, o ile istnieje w drzewie, zlokalizowany jest w lewym poddrzewie węzła:
    • węzeł := lewy_synwęzeł
    • przejdź do 1
  5. Koniec wyszukiwania

Wyszukiwanie klucza w drzewie poszukiwań binarnych można też zaimplementować za pomocą rekurencji:

Szukaj(węzeł):

  1. Jeśli węzeł = nil (puste poddrzewo) – w drzewie nie ma klucza k: return false;
  2. Jeśli kluczwęzeł = k – znaleziono węzeł o podanej wartości klucza: return true;
  3. Jeśli kluczwęzeł < k – klucz, o ile istnieje w drzewie, zlokalizowany jest w prawym poddrzewie węzła:
    • return Szukaj(prawy_synwęzeł);
  4. Jeśli kluczwęzeł > k – klucz, o ile istnieje w drzewie, zlokalizowany jest w lewym poddrzewie węzła:
    • return Szukaj(lewy_synwęzeł);

edytuj Wyszukiwanie klucza o minimalnej wartości

  1. x := korzeń drzewa
  2. Dopóki x ma lewy następnik:
    • x := lewy_synx

Po zakończeniu procedury x jest węzłem z kluczem o najmniejszej wartości.

edytuj Wyszukiwanie klucza o maksymalnej wartości

  1. x := korzeń drzewa
  2. Dopóki x ma prawy następnik:
    • x := prawy_synx

Po zakończeniu procedury x jest węzłem z kluczem o największej wartości.

edytuj Wstawianie klucza

Algorytm jest bardzo podobny jak przy wyszukiwaniu: jeśli przy przechodzeniu drzewa należy przejść w lewo bądź prawo, a węzeł nie ma lewego bądź prawego syna (tj. lewy_synwęzeł = nil lub prawy_synwęzeł = nil), to lewym bądź prawym synem węzła staje się nowy węzeł, z kluczem o żądanej wartości. Natomiast jeśli przy przechodzeniu drzewa uda się zlokalizować klucz, to procedura wstawiania nic nie robi.

edytuj Usuwanie klucza

Dany jest węzeł x, którego klucz ma żądaną wartość. Przy usuwaniu węzła należy rozważyć trzy przypadki:

  1. Jeśli x jest liściem (nie ma synów) – po prostu usunąć
  2. Jeśli x ma tylko jednego syna – zastąpić go synem
  3. Jeśli x ma dwóch synów:
    • Należy znaleźć jego następnik y (najbardziej lewy element w prawym poddrzewie x)
      • zamiast następnika można też użyć poprzednika (czyli najbardziej prawy element w lewym poddrzewie x). Ponieważ nie ma większego znaczenia, którego z nich użyjemy, można zastosować metodę naprzemienną, albo nawet losową. Logiczne jest również używanie zawsze poprzednika lub zawsze następnika.
    • Zastąpić zawartość węzła x przez zawartość węzła y
    • Wyciąć węzeł y o którym wiemy, że może posiadać najwyżej jednego - prawego - syna (ewentualny syn y stanie się synem swojego dziadka)

edytuj Implementacja

Przykładowy program w języku Pascal. Buduje drzewo BST i je przekształca w drzewo wyważone.

program BST;
 
uses crt;
 
type
	wsk	=	^wezel;
	wezel	=	record
	      			d : integer;
	      			l : wsk;
				r : wsk;
			end;
 
var
	n,i,x	: integer;
	p	: wsk;
	poz	: byte;
 
procedure Wstaw(var p : wsk; x : integer);
begin
	if p = nil then
	begin
		new(p);
		p^.d := x;
		p^.l := nil;
		p^.r := nil;
	end
	else if x<p^.d then
		Wstaw(p^.l,x)
	else
		Wstaw(p^.r,x);
end;
 
function Licz(p : wsk) : integer;
var k : integer;
begin
	if p <> nil then
	begin
		k := 1;
		if p^.l <> nil then
			k := k+Licz(p^.l);
		if p^.r <> nil then
			k := k+Licz(p^.r);
		Licz := k;
	end
	else Licz := 0;
end;
 
procedure Pokaz(p : wsk);
begin
	writeln(p^.d);
	if p^.l <> nil then Pokaz(p^.l);
	if p^.r <> nil then Pokaz(p^.r);
end;
 
procedure Wywaz(var p : wsk; b : integer);
var a : integer; q, w : wsk;
begin
	b := b-1;
	a := Licz(p^.l);
	b := b-a;
	while abs(a-b) > 1 do begin
		if a > b then begin
			if p^.l^.r <> nil then begin
				q := p^.l;
				repeat
					w := q;
					q := q^.r;
				until q^.r = nil;
				q^.r := p;
				q^.l := p^.l;
				w^.r := nil;
				p^.l := nil;
				p := q;
			end
		 	else begin
				p^.l^.r := p;
				q := p^.l;
				p^.l := nil;
				p := q;
			end;
			a := a-1;
			b := b+1;
		end
		else begin
			if p^.r^.l <> nil then begin
				q := p^.r;
				repeat
					w := q;
					q := q^.l;
				until q^.l=nil;
				q^.l := p;
				q^.r := p^.r;
				w^.l := nil;
				p^.r := nil;
				p := q;
			end
			else begin
				p^.r^.l := p;
				q := p^.r;
				p^.r := nil;
				p := q;
			end;
			a := a+1;
			b := b-1;
		end;
	end;
	if p^.l <> nil then Wywaz(p^.l,a);
	if p^.r <> nil then Wywaz(p^.r,b);
end;
 
begin
	p := nil;
	clrscr;
	writeln;
	write('Ile elementow? ');
	readln(n);
	for i := 1 to n do begin
		write('Element numer ',i,': ');
		readln(x);
		Wstaw(p,x);
	end;
	writeln;
	writeln('Nie wywazone:');
	Pokaz(p);
	n := Licz(p);
	writeln('Elementow jest ',n);
	Wywaz(p,n);
	writeln;
	writeln('Wywazone:');
	Pokaz(p);
	readln;
end.

edytuj Zobacz też


zespół muzyczny na wesele wierszokleci poems daria weight loss surgery ogłoszenia z Polski suknie ślubne olsztyn