Wykład 8 - Dynamiczne struktury danych -uporządkowane(część II) 8.1. Lista jednokierunkowa uporządkowana
8.2. Lista dwukierunkowa
8.3. Drzewo binarne poszukiwań
Algorytmy stosowane w strukturach uporządkowanych sterowane są wartościami przechowywanymi w poszczególnych elementach tej struktury, w odróżnieniu od algorytmów dla struktur nieuporządkowanych, które sterowane są kolejnością powstawania elementów struktur ( stosy, kolejki...).
8.1. Lista jednokierunkowa uporządkowana
8.1.1. Wyszukanie w liście uporządkowanej za pomocą funkcji Szukaj
1) Lista pusta: Poczatek = nil; stąd Gdzie= nil oraz Szukaj := False
Dane_2
„A”
Nastepny Poczatek
Jeśli Poczatek<> nil, wtedy
Nast Dane_2
„A”
Nastepny
Poczatek Dane_3
„C”
Nastepny nil
jeśli Poczatek^.Dane.Nazwisko = Klucz wtedy Gdzie:= nil, Szukaj:= True, w przeciwnym wypadku,
gdy Poczatek^.Dane.Nazwisko > Klucz wtedy Gdzie:= nil, Szukaj:= False 2) Wyszukanie elementu na początku listy:
Dane_1
„D”
Nastepny nil
3) Wyszukanie w środku lub na końcu listy:
Gdzie:=nil;
Nast:=Poczatek;
jeśli to nie jest ostatni element listy (Nast^.Nastepny <> nil) i jeśli jeszcze można odszukać element równy kluczowi ( Nast^.Dane.Nazwisko < Klucz) wtedy:
Gdzie:= Nast;
Nast:= Nast^.Nastepny;
w przeciwnym przypadku :
Gdzie^.Nastepny wskazuje na poszukiwany element równy Kluczowi i Szukaj := True;
lub Szukaj := False i Gdzie^. Nastepny wskazuje na element większy od Klucza Dane_1
„D”
Nastepny Gdzie
Gdzie:= nil Klucz:= „A”
kolejność wstawiania elementów do listy
Klucz := „C”
Implementacja operacji wyszukania miejsca do wstawienia (Szukaj := False) oraz elementu do usunięcia (Szukaj:= True)
unit MLISTALU;
interface
uses Modul, Rozne, Crt, MlistaLN;
{reszta procedur jak dla listy nieuporządkowanej}
function Szukaj(Poczatek:PElement;Klucz:lan;var Gdzie:PElement):Boolean;
implementation
function Szukaj(Poczatek:PElement;Klucz:lan;var Gdzie:PElement): Boolean;
var Nast: PElement;
begin
Gdzie := nil;
Szukaj:= False;
if Poczatek= nil then exit;
Nast:=Poczatek;
while ( Nast^.Dane.Nazwisko < Klucz) and (Nast^.Nastepny <> nil) do
begin
Gdzie:= Nast;
Nast := Nast^.Nastepny;
end;
if Nast^.Dane.Nazwisko = Klucz then Szukaj := True
else
{nie znaleziono w liście elementu większego od klucza}
if Nast^.Dane.Nazwisko < Klucz then Gdzie:= Nast;
end;
end.
8.1.2. Wstawianie w miejsce wyszukane przez funkcję Szukaj:= False, gdy znalazła element większy od Klucza lub koniec listy (operacje implementowane jak dla listy nieuporządkowanej - wykład 7)
Nowy^.Nastepny:= Poczatek
Poczatek:= Nowy
New(Nowy), gdzie N=1,2,...N Nowy Dane_N
Nastepny nil
if Poczatek = nil then Poczatek:= Nowy;
Nowy
Dane_2
„A”
Nastepny Poczatek
Nowy Dane_2
„A”
Nastepny
Poczatek Dane_3
„C”
Nastepny nil
2.2) wstawianie na początku listy, Szukaj:= False Poczatek
if Gdzie = nil then:
1) Tworzenie kolejnego elementu Nowy na stercie:
2) Wstawianie do listy kolejnego elementu Nowy
Nowy Dane_1
„D”
Nastepny nil
2.1) wstawianie do listy pustej , Szukaj:= False
Dane_1
„D”
Nastepny
nil
2.3) wstawianie wewnątrz lub na końcu listy (Szukaj:=False:)
jeśli Gdzie nie wskazuje na ostatni element listy, to Gdzie wskazuje na element większy od Klucza i wstawiaj wewnątrz listy,
Nowy^.Nastepny:= Gdzie^.Nastepny;
Gdzie^.Nastepny:= Nowy
Dane_1
„D”
Nastepny Gdzie
„A” < „D”
„C” < „D”
Gdzie Dane_2
„A”
Nastepny
Poczatek Dane_1
„D”
Nastepny nil
Dane_3
„E”
Nastepny
„E” > „D” Nowy
nil
w przeciwnym przypadku Gdzie wskazuje na element mniejszy od Klucza i wstawiaj na końcu listy (te same przypisania):
8.1.3. Usuwanie w miejscu wyszukanym przez funkcję Szukaj:= True, gdy znalazła element równy Kluczowi (operacje implementowane jak dla listy nieuporządkowanej - wykład 7)
Pom
Pom Dane_1
Nastepny Poczatek
Dane_2 Nastepny
Dane_3 Nastepny
nil Gdzie
nil 1) Usuwanie na początku listy: Szukaj:=True Gdzie:= nil
2) Usuwanie elementu w środku listy lub na końcu:
Dane_2
„A”
Nastepny
Poczatek Dane_3
„C”
Nastepny
Dane_2
„D”
Nastepny nil
Dane_1 Nastepny
Poczatek Dane_2
Nastepny
Dane_3 Nastepny
nil Gdzie
Gdzie:=nil
Klucz:=”A jeśli Gdzie= nil wtedy:
Pom:= Poczatek;
Poczatek:= Poczatek^.Nastepny
Dispose (Pom);
Pom
jeśli Gdzie<> nil wtedy usuwany jest element wskazywany przez Gdzie^.Nastepny
Pom:= Gdzie^.Następny;
Gdzie^.Następny := Pom^.Nastepny
Dispose (Pom);
Gdzie<> nil nigdy nie wskazuje na ostatni element listy, stąd ostatni element listy jest wskazywany przez Gdzie^.Nastepny i jest usuwany tak samo jak wewnątrz listy
8.2. Lista dwukierunkowa
Lista dwukierunkowa uporządkowana ma identycznie zaimplementowane procedury wstawiania, usuwania i przejścia przez strukturę (wykład 7), natomiast funkcja Szukaj, umożliwiająca wyszukanie elementów może być zaimplementowana podobnie jak dla listy jednokierunkowej lub z uwzględnieniem przejścia w obie strony
8.2.1. Wyszukanie zawsze od początku listy, podobnie jak dla listy jednokierunkowej
unit MLISTDLU;
interface
uses Modul, Rozne, Crt, MListDLN;
{reszta procedur jak dla listy nieuporządkowanej}
function Szukaj(Poczatek:PElementD;Klucz:lan;var Gdzie:PElementD):Boolean;
implementation
function Szukaj(Poczatek:PElementD;Klucz:lan;var Gdzie:PElementD): Boolean;
begin
Gdzie := Poczatek;
Szukaj:=False;
if Poczatek= nil then exit;
while ( Gdzie^.Dane.Nazwisko < Klucz) and (Gdzie^.Nastepny <> nil) do Gdzie := Gdzie^.Nastepny;
if Gdzie^.Dane.Nazwisko = Klucz then Szukaj := True;
{ znaleziono w liście element większy lub równy kluczowi}
if not (Gdzie^.Dane.Nazwisko < Klucz) then Gdzie:= Gdzie^.Poprzedni;
end;
end.
8.2.2. Wyszukanie zawsze od miejsca ostatniego wyszukania, charakterystyczne dla listy dwukierunkowej
unit MLISDLU1;
interface
uses Modul,Rozne,Crt,MListDLN;
{reszta procedur jak dla listy nieuporządkowanej}
function Szukaj(Poczatek:PElementD;Klucz:lan;var Gdzie:PElementD):Boolean;
implementation
function Szukaj(Poczatek:PElementD;Klucz:lan;var Gdzie:PElementD):Boolean;
begin
Szukaj:= False;
if Poczatek= nil then begin
Gdzie:=nil;
exit;
end;
if Gdzie=nil then Gdzie:= Poczatek;
if Gdzie^.Dane.Nazwisko < Klucz then
while (Gdzie^.Dane.Nazwisko < Klucz) and (Gdzie^.Nastepny <> nil) do Gdzie := Gdzie^.Nastepny
else
if Gdzie^.Dane.Nazwisko > Klucz then
while (Gdzie^.Dane.Nazwisko > Klucz) and (Gdzie^.Poprzedni<> nil) do
Gdzie := Gdzie^.Poprzedni;
if Gdzie^.Dane.Nazwisko = Klucz then Szukaj := True;
{ znaleziono w liście element większy lub równy kluczowi}
if not (Gdzie ^.Dane.Nazwisko < Klucz) then Gdzie:= Gdzie^.Poprzedni;
end;
end.
program Lista_uporzadkowana_dwukierunkowa; {przykład zastosowania}
uses Crt, Rozne, Modul, MListDLN, MLisDLU1;
const Tab_menu : Lancuchy =
('1 : Zakladanie listy','2 : Usuwanie z listy', '3 : Wydruk listy', '4 : Usun liste', 'K/k - Koniec programu','','','','','','');
procedure Podaj_klucz(var Klucz: lan);
begin
repeat Write('Podaj nazwisko: '); Readln(Klucz); until (Klucz <> '');
end;
procedure Wstaw_do_listy(var Poczatek, Gdzie: PElementD);
var Dana : Osoba; Z: char;
begin
Podaj_dane(Dana);
if not Szukaj(Poczatek, Dana.Nazwisko, Gdzie) or Pusty(Poczatek) then begin
Wstaw(Poczatek,Dana,Gdzie);
Dla_jednego(Poczatek,Gdzie,Wydruk_danych); Pauza(Z, False);
end;
end;
procedure Usun_z_listy(var Poczatek, Gdzie: PElementD);
var Klucz:lan; Z: char;
begin
Podaj_klucz(Klucz);
if Szukaj(Poczatek,Klucz,Gdzie) then Writeln(Usun(Poczatek,Gdzie)) else Writeln(False); Pauza(Z,False);
end;
var Wybor,Z : char; Poczatek_DLN, Gdzie : PElementD;
begin ClrScr;
Randomize; Inicjalizacja(Poczatek_DLN);
repeat
Menu(Tab_menu, 5, Wybor);
case Wybor of
'1' : Wstaw_do_listy(Poczatek_DLN, Gdzie);
'2' : Usun_z_listy(Poczatek_DLN, Gdzie);
'3' : begin
Writeln(Dla_kazdego(Poczatek_DLN,Wydruk_danych)); Pauza(Z,False);
end;
'4' : Usun_Pamiec(Poczatek_DLN);
end;
until Wybor = 'K';
end.
8.3. Drzewo binarne poszukiwań
typ proceduralny dla procedur przejścia przez drzewo
zrob= procedure(A: Osoba);
definicja elementu drzewa POsobaD = ^ROsobaD;
ROsobaD = record
Dane: Osoba;
Lewy, Prawy: POsobaD;
end;
Budowa interfejsu
procedure Inicjalizacja( var Wezel : POsobaD);
{ działanie: inicjuje drzewo
warunki wstępne: Wezel wskazuje na pierwszy element, zwany korzeniem warunki końcowe: drzewo zostaje zainicjowane jako puste}
procedure Szukaj(Wezel : POsobaD; var Gdzie : POsobaD; Klucz : lan);
{ działanie: szuka elementu w drzewie
warunki początkowe: Wezel wskazuje na zainicjowane drzewo, Klucz jest poszukiwanym elementem,
warunki końcowe: jeśli to możliwe, procedura szuka elementu w drzewie o wartości równej Kluczowi idąc na lewo każdego z węzłów, jeśli element w węźle jest większy i na prawo, jeśli element w węźle jest mniejszy, natomiast jeśli znajdzie węzeł równy Kluczowi, Gdzie wskazuje na ten element drzewa, w przeciwnym wypadku zwraca adres pusty w Gdzie}
procedure Wstaw(var var Wezel, :Pozycja : POsobaD);
{działanie: dodaje element do drzewa
warunki początkowe: Pozycja jest daną do wstawienia do zainicjowanego drzewa
warunki końcowe: jeśli to możliwe, procedura dodaje daną Pozycja do drzewa idąc na lewo każdego z węzłów, jeśli element w węźle jest większy od Pozycja i na prawo, jeśli element w węźle jest mniejszy aż do osiągnięcia węzła z wolnym łączem, który po wstawieniu wskazuje na element Pozycja, natomiast jeśli znajdzie węzeł z elementem równym Pozycja, to kończy poszukiwania, usuwa element Pozycja i zwraca adres pusty w Pozycja }
procedure Usun(var Wezel : POsobaD; Klucz : lan);
{działanie: usuwa element z drzewa
warunki początkowe: Wezel jest zainicjowanym drzewem
warunki końcowe: jeśli jest to możliwe, procedura szuka elementu w drzewie równego Kluczowi idąc na lewo każdego z węzłów, jeśli element w węźle jest większy i na prawo, jeśli element w węźle jest mniejszy, natomiast jeśli znajdzie węzeł równy Kluczowi, to jeśli ma on tylko jednego następcę (prawego lub lewego), zostaje nim zastąpiony i następnie usunięty. W przeciwnym przypadku (węzeł równy Kluczowi nie jest liściem) procedura szuka elementu największego idąc w lewo wyszukanego węzła i potem w prawo lewego następcy, wstawia kopię wartości znalezionego elementu do usuwanego węzła, w miejsce skopiowanego elementu w drzewie podłącza jego lewego następcę oraz usuwa węzeł ze skopiowanym elementem, }
procedure Usun_pamiec(Wezel : POsobaD );
{ działanie: usuwa elementy z drzewa i inicjuje drzewo jako puste warunki początkowe: Wezel jest zainicjowanym drzewem
warunki końcowe: liczba elementów w drzewie jest równa 0, funkcja przechodzi przez wszystkie gałęzie aż do osiągnięcia liści i usuwa je z pamięci zaczynając zawsze od lewego liścia. Każdy węzeł z usuniętymi liśćmi staje się liściem }
procedure Dla_kazdego(Wezel : POsobaD; funkcja: zrob );
{działanie: wykonuje funkcje na każdym wstawionym elemencie do drzewa warunki początkowe: Węzeł jest zainicjowanym drzewem, zrob jest typem
funkcji, która pobiera element drzewa i nie zwraca wartości
warunki końcowe: jeśli jest to możliwe, procedura typu zrób jest wykonywana tylko raz dla każdego elementu wstawionego do drzewa zaczynając od najmniejszego elementu }
procedure Dla_jednego(Wezel : POsobaD; klucz: lan; funkcja: zrob);
{ działanie: wykonuje funkcja na elemencie wyszukanym w drzewie
warunki początkowe: Wezel jest zainicjowanym drzewem, zrob jest typem procedury, która pobiera element z drzewa i nie zwraca wartości
warunki końcowe: procedura typu zrób jest wykonywana tylko raz dla elementu z drzewa Wezel o wartości Klucza, jeśli zostanie wyszukany przez funkcję Szukaj}
8.3.1. Wstawianie do drzewa
Należy wstawić do drzewa ciąg: 8, 5, 9, 2, 11, 1, 4, 3, 10, 15, 13, 7 , 6
8
5 9
2
1
2 3
4 1 8
8
5
8
5 9
1 2
1
2 3
8
5 9
2
4
11 1 1
1
2 3
4
7
5
6 8
5 9
2 11
1
2 3
4 5
8
5 9
2 11
1
1
2 3
4 5
8
5 9
2
1
2 3
4
11 5
8
5 9
2
4 10
11
1
1
2 3
4
7 9
5
6 8
5 9
2
4 10
11
1
1
2 3
4
7 9
5
6 6
8
5 9
2
4
3 11
1
1
2 3
4
7
8 5
6
3 8
3 8
15 10 1
6
4 7
3 8
10 9
15 10
13 11 7
12
6 13 ...
procedure Wstaw(var Wezel,Nowy : POsobaD);
begin
if Wezel = nil then Wezel := Nowy;
else
if Wezel^.Dane.Nazwisko > Nowy^.Dane.Nazwisko then Wstaw(Wezel^.Lewy, Nowy)
else
if Wezel^.Dane.Nazwisko < Nowy^.Dane.Nazwisko then
8.3.2. Usuwanie z drzewa
8
5 9
2
4 10
11 1
1
2 3
4
7 9
5
6
3 8
15 10
13 11
8
5 9
2
4 10
11 1
1
2 3
4
7 9
5
6
15 10
13 11
8
5 9
2
4 10
11 1
1
2 3
4
7 9
5
6
13 11
8
5 9
2 4
10 1
1
2 3
4
7
9
6
13 11
7 9
2 4
10 1
3
4
7
9
6
13 11 7
12
7 12
7 12
7 12
5 12 2 613
6 13
6 13
6 13
6 13
procedure Usun(var Wezel : POsobaD; Klucz : lan);
var Usuwany : POsobaD;
procedure Usun_(var Lisc_Prawy : POsobaD);
begin
if Lisc_Prawy^.Prawy <> nil then Usun_(Lisc_Prawy^.Prawy)
else begin {wymiana danych usuwanych z liściem}
Usuwany^.Dane := Lisc_Prawy^.Dane;
Usuwany := Lisc_Prawy;
Lisc_Prawy := Lisc_Prawy^.Lewy; end;
end;
begin
if Wezel <> nil then
if Klucz<Wezel^.Dane.Nazwisko then Usun(Wezel^.Lewy, Klucz)
else if Klucz > Wezel^.Dane.Nazwisko then Usun(Wezel^.Prawy, Klucz) else {znaleziono element do usunięcia}
begin
Usuwany := Wezel;
if Usuwany^.Prawy = nil then Wezel := Usuwany^.Lewy else
if Usuwany^.Lewy = nil then Wezel := Usuwany^.Prawy else Usun_(Usuwany^.Lewy);
Dispose(Usuwany); end;
end;
8.3.3. Przeszukiwanie w drzewie
procedure Szukaj(Wezel : POsobaD; var Gdzie : POsobaD; Klucz : lan);
begin
if Wezel = nil then begin
Gdzie := nil; Exit;
end;
if Wezel^.Dane.Nazwisko = Klucz then Gdzie := Wezel else
if Wezel^.Dane.Nazwisko > Klucz then Szukaj(Wezel^.Lewy,Gdzie,Klucz) else
Szukaj(Wezel^.Prawy,Gdzie,Klucz) end;
8.3.4. Przejście przez drzewo
procedure Dla_kazdego(Wezel : POsobaD; funkcja: zrob);
begin
if Wezel <> nil then begin
Dla_kazdego(Wezel^.Lewy,funkcja);
funkcja(Wezel^.Dane);
Dla_kazdego(Wezel^.Prawy, funkcja) end;
end;
procedure Dla_jednego(Wezel : POsobaD; klucz:lan; funkcja: zrob);
var Gdzie: POsobaD;
begin
Szukaj(Wezel, Gdzie, klucz);
if Gdzie <> nil then funkcja(Gdzie^.Dane);
end;
8.3.5. Usunięcie drzewa
procedure Usun_drzewo(Wezel : POsobaD);
begin
if Wezel <> nil then begin
Usun_drzewo(Wezel^.Lewy);
Usun_drzewo(Wezel^.Prawy);
program Drzewo_binarne;
uses Crt, Rozne, Modul, MDrzewoB;
const Tab_menu : Lancuchy =
('1 :Wstawianie', '2 :Usuwanie', '3 :Wyswietlenie jednego',
'4 :Wydruk drzewa', '5 : Usun drzewo', 'K/k - Koniec programu','','','','','');
procedure Wstaw_do_drzewa(var Poczatek:POsobaD);
var Dana : Osoba; Nowy : POsobaD;
begin
Podaj_dane(Dana);
if Nowy_Element_D(Nowy, Dana) then begin
Wstaw(Poczatek, Nowy);
if Nowy <> nil then
Dla_jednego(Poczatek, Dana.Nazwisko, Wydruk_danych);
end;
end;
function Klucz: lan;
var Pom: lan;
begin
repeat Write('Podaj nazwisko: '); Readln(Pom); until Pom <> '';
Klucz:= Pom;
end;
var Wybor, Z : char; Poczatek_D: POsobaD;
begin
ClrScr; Randomize;
Inicjalizacja(Poczatek_D);
repeat
Menu(Tab_menu, 6, Wybor);
case Wybor of
'1' : Wstaw_do_drzewa(Poczatek_D);
'2' : Usun_element_drzewa(Poczatek_D, Klucz);
'3' : Dla_jednego(Gdzie^.Dane, Klucz, Wydruk_danych);
'4' : Dla_kazdego(Poczatek_D, Wydruk_danych);
'5' : begin
Usun_drzewo(Poczatek_D); Poczatek_D :=nil;
end;
end;
Pauza(Z, False);
until Wybor = 'K';
end.