LITERATURA
[1] Cormen T. H., Leiserson C. E., Rivest R. L.: Wprowadzenie do algorytmw.
WNT, Warszawa, 2006.
[2] Drozdek A.: C++. Algorytmy i struktury danych. Helion, Gliwice, 2004.
[3] Neapolitan R., Naimipour K.: Podstawy algorytmw z przykadami w C++.
Helion, Gliwice, 2004.
--------------------------------------------------------------------------------------------------------------------------[4] Harris S., Ross J.: Od podstaw Algorytmy. Helion, Gliwice, 2006.
[5] Adamski T., Ogrodzki J.: Algorytmy komputerowe i struktury danych. PW, Warszawa 2005
[6] Drozdek A., Simon D. L.: Struktury danych w jzyku C. WNT, Warszawa, 1996.
[7] Sedgewick R.: Algorytmy w C++. RM, Warszawa, 1999.
[8] Sedgewick R.: Algorytmy w C++. Grafy. RM, Warszawa, 2003.
[9] Aho Alfred V., Hopcroft John E., Ullman Jeffrey D.: Projektowanie i analiza algorytmw. Helion,
Gliwice, 2003 (1974).
[10] Wrblewski P.: Algorytmy, struktury danych i techniki programowania. Helion, Gliwice, 2003.
[11] Wirth N.: Algorytmy+struktury danych=programy. WNT, Warszawa, 1989.
[12] Banachowski L., Diks K., Rytter W.: Algorytmy i struktury danych. WNT, Warszawa, 1996.
[13] Syso M. M.: Algorytmy. Wydawnictwa szkolne i pedagogiczne, Warszawa, 1997.
1. POJCIE ALGORYTMU
Pojcie algorytm zwizane jest z nazwiskiem perskiego matematyka Muhammada Ibn Musa
Al Chorezmi (IX w), ktry poda reguy wykonywania operacji arytmetycznych na liczbach
dziesitnych. Jego praca zostaa w XII wieku przetumaczona na acin.
Pierwszym algorytmem by przepis znajdowania najwikszego wsplnego podzielnika /NWD/
dwch liczb naturalnych a i b, podany przez Euklidesa (365 -300 p.n.e.).
Algorytm: uporzdkowany zbr operacji, ktrych wykonanie udostpnia rozwizanie zadania z
okrelonej klasy zada.
Algorytm jest cile okrelon procedur obliczeniow, ktra dla waciwych danych Wejciowych generuje oczekiwane dane Wyjciowe (zwane te wynikami).
Rozwizanie okrelonego problemu obliczeniowego polega na sprecyzowaniu wymaga odnonie relacji danych Wejciowych i Wyjciowych.
Algorytm opisuje stosown technik obliczeniow, ktra realizuje relacj We/Wy.
Specyfikacja: dokadny opis zadania lub problemu do rozwizania, obejmujcy:
Dane: dla postawionego zadania i warunki, ktre musz spenia,
{a ( x ), a2 ( x ) L} dla x > 0
Wyniki: z uwzgldnienie relacji do danych np.: f ( x ) = 1
Error dla x 0
Wskazane jest zamieszczenie penej specyfikacji problemu przed podaniem algorytmu.
Opracowujc algorytm zakada si poziom szczegowoci, bdcy podstaw algorytmu.
Na wstpie naley sprawdzi, czy zadanie jest jednoznaczne rozwizywalne.
Algorytm powinien uwzgldnia wszystkie moliwe teoretyczne warianty
oblicze, wynikajce z rnorodnoci danych Wejciowych.
Oznaczenia: n liczba operacji w algorytmie; oi - i-ta operacja; t(x) chwila wykonania operacji x.
Kolejno wykonywania operacji ponumerowana jest od 1 do n wcznie.
Algorytm rwnolegy: moliwo wykonywania jednoczenie rnych operacji (instrukcji) w tej
samej chwili czasowej tzn.
t(koniec( oi )) > t(pocztek ( oi+1 )). ( czytaj: istnieje)
i [1, n ]
Algorytm: liniowy (bezwarunkowa lista krokw), -z rozgazieniami (spenienie warunkw okrela kolejno).
START
Schemat blokowy
Graficzny, sformalizowany zapis algorytmu, trudnoci w
fizycznej prezentacji duych problemw
Wykorzystuje si zazwyczaj nastpujce bloki:
pocztek i koniec algorytmu;
operacyjny zawiera opisy operacji na danych;
decyzyjny okrela kierunek sterowania.
dane wejsciowe
zwieksz
operacja
warunek
KONIEC
R1.1
dla n = 0
1
n! =
n ( n 1)! dla n > 0
dla n < 2
n
Fib(n)=
Fib(n 2 ) + Fib(n1) dla n 2
Jzyki formalne lub pseudojzyki jzyk programowania lub specjalnie stworzona notacja.
1.
2.
3.
4.
5.
6.
7.
8.
9.
ptle iteracyjne while - do, for - do, repeat, instrukcje warunkowe if, then, else
// pocztek komentarza a do koca wiersza
a
b; oznacza przypisanie wartoci wyraenia z prawej strony do lewej strony.
Dopuszczalne s podstawienia wielokrotne a
b
c,
uyte zmienne s lokalne w procedurze (zmienne globalne zaznaczane s sowem global),
A[i] jest i-tym element tablicy A; A[3k] jest podtablic zoon z elementw A[3] A[k],
dugo tablicy okrela si piszc length(A),
parametry do procedury przekazywane s przez warto.
struktur blokow sygnalizuje si poprzez wcicia.
Location(int n, type a, X[ ] )
{
index loc= 1;
while (loc <=n && X[loc] != a)
loc ++;
if (loc > n) return -1;
else return loc;
}
Lista krokw kolejne kroki zawieraj opis czynnoci wykonywanych przez algorytm.
Poszukiwanie elementu w wektorze
Dane: wektor X[pk] ze zbiorem elementw; warto poszukiwanego element key.
Wynik: wskanik m okrelajcy pozycj elementu w X (m= -1 jeeli brak elementu w X)
Krok 0: Wprowad elementy do wektora X
//mona pomin
//wstawienie wartownika na koniec
Krok 1: xl+1 = key
Krok 2: i = p
Krok 3: Czy xi = key?, jeeli tak to KONIEC, jeeli nie id do 4
Krok 4: i = i + 1 id do 3
KONIEC:
// Jeeli m=l+1 to m = -1 co oznacza, e wektor X nie zawiera elementu key
Projektowanie algorytmu nie jest czynnoci prost, gdy brak jest uniwersalnych
regu jego tworzenia
1.3. Przykady
Przykad 1.1. Schemat Hornera (1819 r.) - warto wielomianu n-go stopnia w zadanym punkcie.
f(n,x) = anxn + an-1xn-1 + + a2x2 + a1x + a0
f(n,x) = (((anx + an-1)x + an-2)x + + a1)x + a0
f ( n , x) :=
w an
for i n 1 .. 0
fw
i = n - 1,,1, 0
w = wz + ai
f(z) = w
w w x + ai
w = an
g(n, x) :=
bn if n 0
g(n 1 , x) x + bn if n > 0
Jedn z regu jest upraszczanie algorytmu tak, aby uzyska maksymalnie prost, krtk i
czyteln posta, w ktrej atwo bdzie wyodrbni niezalene fragmenty.
1.2. Pojcie zoonoci obliczeniowej
Zoono obliczeniowa algorytmu: liczba zasobw komputerowych, potrzebnych do jego wykonania.
Zoono pesymistyczna liczba zasobw komputerowych potrzebnych przy najgorszych
danych wejciowych o okrelonym rozmiarze n.
Zoono oczekiwana liczba zasobw komputerowych potrzebnych dla typowych danych
wejciowych o okrelonym rozmiarze n.
Rozmiar danych wejciowych nie jest pojciem jednoznacznym i zaley od problemu.
Dla sortowania rozmiarem danych jest liczba elementw w zbiorze do posortowania.
W analizie efektywnoci mnoenia liczb cakowitych, najbardziej wiarygodnym rozmiarem
jest liczba bitw potrzebnych do przechowywania tych liczb.
// zapis w pseudokodzie
max X[1]
for i 2 to n do
if X[i] > max then max X[i]
procedure MaxVec (n: integer; var X:vec; var Max:real;);
var i: integer; max: real;
begin
max := X[1]
for i:=2 to n do if X[i] > max then max:=X[i]
Max:=max;
end;
R1.2
1.
2.
3.
4.
5.
6.
>
8
6
>
>
>
>
>
>
Pr = 2 * (n - 1) = 2n 2 (lub 2n - 3)
Przykad:
a11
a 21
a12
a 22
b
a13 11
b
a 23 21
b31
b12
c
b22 = 11
c
b32 21
MultiplyMat(A, B)
if col[A] row[B] then return Error Bd wymiarw
for i 1 to row[A] do
for j to col[B] do
C[i,j] 0
for k 1 to col[A] do C[i,j] C[i,j]+A[i,k]@B[k,j]
return C
procedure macMult(m n,r: integer; var A, B, C:mac);
var i, j, k: integer;
begin
for i:=1 to m do
for k:=1 to r do
begin
C[i,k]:=0;
for j:=1 to n do C[i,k] := C[i,k] + A[i,j]*B[j,k];
end;
end;
j=1,2,...r.
k =1
cij=0
k=1
cij=cij+aijbjk
i=i+1
i>m
k>r
j>n
Algorytm Euklidesa NWD wystpuje pod nazw gcd(a, b) (greatest commom divisor).
Mona zbudowa algorytm NWD na bazie: odejmowania
dzielenia
Dane: liczby naturalne a, b
Wynik: nwd(a, b)
dopki a b wykonaj
jeeli a > b to a a b
przeciwnie b b a
nwd a
NWD(153, 85)
a b=true; a>b=true; a=a-b=68
a b=true; a >b=false; b=b-a = 85-68 = 17
a b=true; a>b=true; a=a-b = 68-17 = 51
a b=true; a>b=true a=a-b = 51-17 = 34
a b=true; a>b=true a=a-b = 34-17 = 17
a b=false; nwd =a =17
NWD(30, 21)
k=k+1
j=j+1
N
KONIEC
Dane:
a, b
Wyniki: nwd(a, b)
while b > 0 do
r a mod b
ab
br
nwd a
2 mod 2 = 0
0 mod 23 = 0
46 mod 4 = 2
46 mod 2 = 0
2 mod 48 = 2
46 mod 48 = 46
48 mod 46 = 2
64 mod 24 = 16
NWD(21, 9)
NWD(9, 3)
NWD(3, 0)
3
Rekurencyjna implementacja algorytmu NWD jest skoczona, gdy
w kadym woaniu ulega zmniejszeniu warto 2-go argumentu.
j=1
i=1
START
Przykad 1.6. Najwikszy wsplny dzielnik NWD(a, b) dwch liczb naturalnych dodatnich.
R1.6
START
i=j=1
bj=a1
i<n
KONIEC
i=i+1
h=j+1
k=1
T a >b
i
k
T
k<j
bh=bh-1
h=h-1
N
T
k=k+1
j=j+1
bj=ai
h>k
bk=ai
j=j+1
R1.7
2. ANALIZA ALGORYTMW
Zadaniem analizy algorytmw, -dziau informatyki- jest poszukiwanie najlepszych algorytmw
realizujcych okrelone zadania komputerowe, prbujc odpowiedzie na pytania:
Czy problem opisany algorytmem moe by zrealizowana na komputerze w realnym czasie?
Ktry z algorytmw naley zastosowa dla okrelonego problemu i zadanego sprztu?
Czy przyjty algorytm jest optymalny?
Moe istnieje lepszy od przyjtego?
Czy mona udowodni, e dla wybranego algorytmu uzyska si jednoznaczne rozwizanie?
2.1. Pojcie zoonoci
Identyczny problem moe by rozwizany z wykorzystaniem algorytmw o rnej efektywnoci, dajcej si odczu dopiero przy przetwarzaniu narastajcych liczebnie zbiorw.
Zoonoci obliczeniowa jako miar porwnywania efektywnoci algorytmw
wprowadzili J. Hartmanis i R. E. Stearns.
Okrela ona ile zasobw zuywa dany algorytm lub jak jest on kosztowny.
Zbir: zestaw oddzielnych, nie numerowanych obiektw, zwanych elementami zbioru.
Moe zosta zdefiniowany przez wypisanie elementw w nawiasie np. Z = { 2, 5, 4, 7 }.
Zapis A B oznacza: wszystkie elementy zbioru A zawieraj si w zbiorze B, tym samym jeeli x A to x B.
Zbir A jest podzbiorem zbioru B.
A B -cz wsplna, zbir wsplnych elementw
Oznaczenia typowych zbiorw:
w obu zbiorach.
pusty, nie zawiera elementw,
A B -suma, zbir wszystkich elementw wystpujcych w obu zbiorach.
Z liczb cakowitych Z = {-2,-1,0,1,2,},
A / B -rnica midzy A i B, zbir elementw
R liczb rzeczywistych,
bdcych w A lecz nie wystpujcych w B.
N liczb naturalnych, N = {0,1,2,3,}.
Mona zdefiniowa nowy zbir, bazujc istniejcych zbiorach, stosujc zapis z dwukropkiem ( takie, e ).
Zbir liczb parzystych mona zdefiniowa: P = { X: X Z i reszta z X/2 jest zerem }
Rozmiar danych wejciowych liczba pojedynczych danych wchodzcych w skad zbioru danych.
Dla sortowania -liczba elementw zbioru; dla analizy drzewa liczba wzw w drzewie.
Wprowadmy oznaczenia:
D2
D1
m1
m2
n zbir z zestawami danych wejciowych D o jednakowych rozmiarach,
D4
mDn liczba operacji dominujcych dla zestawu danych D,
D5
D3
Xn zmienna losowa zawierajca wartoci mDn dla D n,
pk,n prawdopodob., e algorytm wykona k operacji dominujcych dla danych o rozmiarze n.
Podstaw okrelenia rozkadu prawdopodobiestwa zmiennej losowej Xn s informacje o aplikacjach algorytmu.
Czsto przyjmuje si, e kady zestaw danych o rozmiarze n, z jednakowym prawdopodobiestwem moe stanowi zbir wejciowy algorytmu.
W praktyce, dla realnego zbioru danych nie zawsze tak musi by.
Pesymistyczna zoono czasowa: W(n) = sup{ mDn: D n },
n
A
za
lg(n) log2(n)
lg(n) = ln(n)/ln(2)
n
256
4096
65536
1048576
n2
65536
16777216
4294967296
1099301922576
n1.5
4096
262144
16777216
1073588228
nlg(n)
2048
49152
1048565
20969520
lg(n)
8
12
16
20
Z tabeli 2.1 wynika, e dla zbioru danych wyej 1 miliona funkcj opisujca zoono obliczeniow f(n)
mona ograniczy do jednego skadnika tzn. f(n) = n2, bez popenienia istotnego bdu obliczeniowego.
n0
2
2
2
Przyjmujc c = 1/2 oraz n0 = 0 dowodzimy, e T(n) O(n2)
Mwimy: due O nakada na funkcj asymptotyczne ograniczenie grne.
f( n
)
11
n
13
n2
Tablica 2.2
n 2n2 11n
1 2
11
2 8
22
3 18 33
4 32 44
5 50 55
6 72 66
Mona wyznaczy nieskoczenie wiele par (c, n0) speniajcych w/wym nierwno.
Wyznaczenie najlepszych wartoci c i n0 wymaga okrelenia, dla jakich
wartoci n0 okrelony skadnik w funkcji f(n) staje si dominujcy i ze
wzrostem n pozostaje.
Dla przykadowej funkcji kandydatami na skadnik dominujcy s: 2n2 raz 11n.
Mona zauway, e nierwno 2n2 11n jest speniona dla n > 5.
Poszukiwane wartoci graniczne to n0 = 6 oraz c 3.28.
5n2
10
2.2.2. Notacja
Funkcja f(n) jest klasy (g(n)) jeeli istniej dwie stae: rzeczywista c > 0
i naturalna n0, takie e relacja f(n) cg(n) zachodzi dla kadego n n0.
Mona powiedzie, e dla dostatecznie duych n funkcja f(n) ronie przynajmniej tak
szybko jak funkcja g(n).
n
8
10
Wasnoci notacji O
1. Jeeli funkcja f(n) jest klasy O(g(n)) i g(n) jest klasy O(h(n)), to f(n) jest O(h(n)) prawo przechodnioci.
2. Jeeli funkcja f(n) jest klasy O(h(n)) i g(n) jest klasy O(h(n)), to f(n) + g(n) jest O(h(n)).
3. Funkcja f(n) = ank jest klasy O(nk).
4. Funkcja f(n) = nk jest klasy O(nk+i) dla dowolnego i > 0.
5. Funkcja f(n) = cg(n) jest klasy O(g(n)).
6. Funkcja loga(n) jest klasy O(logb(n)) dla dowolnych a > 1 i b>1.
Podstawa logarytmu nie ma znaczenia w notacji O. Zachodzi loga(n) = c*logb(n), c = ln(b)/ln(a)
7. Funkcja loga(n) jest O(lg(n)) dla dowolnego a > 0.
Z przedstawionych wasnoci notacji O wynika uoglniony wniosek:
f(n) = aknk + ak-1nk-1 + +a1n + a0
jest klasy O(nk)
Notacja O okrela grn granic asymptotycznego szacowania.
Szacujc pesymistyczny czas dziaania algorytmu, rwnie szacuje si z gry czas
dziaania danego algorytmu dla wszystkich danych wejciowych.
Orzeczenie, e czas dziaania algorytmu jest rzdu O(g(n)) oznacza, e
pesymistyczny czas dziaania szacuje O(g(n)) lub, e niezalenie od struktury
danych o rozmiarze n, czas dziaania algorytmu szacuje O(g(n)).
Koszt algorytmu sortowania przez wstawienie dla najgorszego przypadku danych szacowany jest
jako O(n2), z czego wynikaoby, e pesymistyczny czas dziaania tego algorytmu jest take O(n2).
Notacji O uywa si do opisu czasu dziaania algorytmu poprzez badanie jego oglnej struktury.
Klasycznym przykadem moe by algorytm zawierajcy podwjnie zagniedon ptl,
gdzie wskaniki ptli i oraz j osigaj warto n.
Dla n 1 prawdziwa jest relacja: n 1 n2. Przyjmujc n0 = 1 oraz c = 1 dowodzimy, e n O(n2).
Funkcja zoonoci f(n) nie musi posiada skadnika kwadratowego aby nalee do klasy O(n2).
Jej wykres musi jedynie znajdowa si pod wykresem pewnej czysto kwadratowej funkcji.
Funkcja f(n) jest klasy (g(n)) jeeli istniej stae rzeczywiste c1 > 0 i c2 > 0 oraz
staa naturalna n0, takie e relacja c1g(n) f(n) c2g(n) zachodzi dla kadego n n0.
Funkcja f(n) jest klasy (g(n)), jeeli istniej dodatnie stae c1 oraz c2 takie, e funkcja ta
moe by zawarta miedzy c1g(n) i c2g(n) dla dostatecznie duych n. c1g(n) f(n) c2g(n)
Funkcja f(n) jest rzdu g(n) tzn. obie funkcje rosn tak samo szybko dla dostatecznie duych n.
Funkcja g(n) jest asymptotycznie dokadnym oszacowaniem funkcji f(n), gdy dla
wszystkich n n0 funkcja f(n) jest rwna g(n) z dokadnoci do staego wspczynnika.
Dla dwch dowolnych funkcji f(n) i g(n) zachodzi zaleno:
f(n) (g(n)) wtedy i tylko wtedy, gdy f(n) (g(n)) oraz f(n) O(g(n)).
cg
(n
)
)
f(n
)
cg(n
c 2g
(n)
n))
(g(
n)
c 1g(
O(g(n))
n0
5n+12 2n2+6n
2lg(n) 8n2-11
6lg(n)-8 12n2
n0
)
f (n
n0
2n2+6n
2n2+6n 3n6+6n2
8n2-11
8n2-11 2n3-5n
12n2
12n2
2n+2n2
Przykady funkcji nalecych do rnych notacji dla g(n) = n2
Notacja O(g(n))
Nie moe by gorzej
Notacja (g(n))
Nie moe by lepiej
Notacja (g(n))
Moe by dokadnie
(g(n) = O(g(n) (g(n)
11
1
6.64
100
10000
1030
<<s
<<s
<<s
10 ms
3.2*1016lat
1
9.97
1000
106
10301
<<s
<<s
1 ms
1s
1
19.93
106
1012
10301030
<<s
<<s
1s
11.6 dni
Tabela 2.3. pokazuje, e dla pewnej klasy algorytmw ich implementacja na wspczesnych
komputerach jest bezowocna, ze wzgldu na nierealny czas wykonania.
12
13
n=100 2*106
Wane jest okreli, czy istnieje moliwo znalezienia jeszcze bardziej efektywnego algorytmu.
Analiza zoonoci obliczeniowej jest prb okrelenia dolnej granicy efektywnoci wszystkich
algorytmw rozwizujcych dany problem.
Istnieje dowd, e problem mnoenia macierzy mona rozwiza za pomoc algorytmu, o
zoonoci nie mniejszej ni (n2). Stwierdzenie jest efektem analizy zoonoci obliczeniowej.
Na podstawie przeprowadzonej analizy moemy powiedzie, e dolne ograniczenie dla
problemu mnoenia macierzy to (n2). Nie oznacza to, e musi istnie moliwo
skonstruowania algorytmu mnocego macierze o zoonoci czasowej O(n2).
Oznacza to jedynie, e teoretycznie taka moliwo istnieje.
Celem dla danego problemu jest wyznaczenie dolnego ograniczenia w postaci (g(n))
i stworzenie algorytmu o zoonoci O(g(n)), ktry rozwizuje ten problem.
Kiedy uda si osign ten cel, many pewno, e znalezionego algorytmu nie mona ju
poprawi (oczywicie, poza popraw staych, ktrych nie uwzgldniamy w zapisie zoonoci).
Dla sortowania stworzono algorytmy o zoonoci czasowej zblionej do dolnego ograniczenia.
Dla klasy algorytmw sortujcych na podstawie operacji porwna kluczy
dolne ograniczenie wynosi (nlg(n)).
Powstay ju algorytmy O(nlg(n)).
Algorytmy sortujce wycznie poprzez porwnanie kluczy mog porwnywa dwa klucze aby okreli, ktry
z nich jest wikszy, oraz kopiowa klucze, ale nie mog wykonywa na kluczach adnych innych operacji.
14
0
T (n) =
T ( n / 2) + c
dla
dla
n =1
n >1
T(n) = O(lg(n))
Rwnanie zoonoci dla problem analizy zbioru wielkoci n, sprowadzonego do analizy dwch
podzbiorw n/2 plus pewna staa liczba dziaa.
0
T (n) =
T ( n / 2) + T ( n / 2) + c
dla n = 1
dla n > 1
T(n) = O(n)
Rwnanie zoonoci dla problem analizy zbioru wielkoci n, sprowadzonego do analizy dwch
podzbiorw n/2 plus pewna liniowa liczba dziaa.
0
T (n) =
T ( n / 2) + T ( n / 2) + cn
dla
dla
n =1
n >1
T(2k) = 2T(2k-1) + c2k = 2(2T(2k-2) + c2k-1) +c2k = 22T(2k-2) + c2k +c2k =2kT(20) + kc2k =
= cnlg(n)
T(n) = O(nlg(n))
Rekurencja uniwersalna
Jest metod pozwalajc na szablonowe rozwizanie rwna postaci:
T(n) = aT(n/b) + f(n)
gdzie a > 1, b > 1 pewne stae, za f(n) jest funkcja asymptotycznie dodatni.
Rwnanie opisuje czas dziaania algorytmu, ktry dzieli zadany problem o rozmiarze n na a
podproblemw o rozmiarze n/b kady.
Kady podproblem a rozwizywany jest rekurencyjnie w czasie T(n/b).
Funkcja f(n) opisuje czas dzielenia i czenia wynikw czciowych.
Upraszczanie rwna rekurencyjnych
Rozwizujc rekurencyjne rwnanie warto zastanowi si nad pominiciem szczegw.
Klasycznym zaoeniem upraszczajcym jest przyjcie za argument funkcji liczby cakowitej, gdy
dla innych wartoci prezentowane zalenoci na czas dziaania algorytmu nie musz by aktualne.
Rozwamy rwnanie , ktre dla poprawnego rozwinicia potrzebuje warunkw brzegowych.
Mona przyj zaoenie, e dla maych n funkcja T(n) jest staa i pomin warunki brzegowe otrzymujc:
T(n) = 2T(n/2) + O(n)
Nie podaje si wartoci funkcji T(n) dla maych n, gdy ich uwzgldnienie zmienia jedynie
rozwizanie o pewn sta bez wpywu na rzd wielkoci.
15
L L L L
n
n
T ( n ) = 2 T k + k n 2i = 2k T i + k n 2k + 1
2
2
i =0
Jezeli k = lg(n ) to :
k
n
T ( n ) = 2lg( n ) T lg( n ) + n lg(n ) 2lg( n ) + 1 = n lg(n ) n + 1
2
T(n) = T(n-1) + n
T(n-1) = T(n-2) + n - 1
T(n-2) = T(n-3) + n - 2
T(2) = T(1) + 2
T(1)= 1
n >1
i =1
n =1
dla
n >1
n ( n + 1)
2
n 1
2
2
2
2
+L+
+
+ = 2 2 ln( n )
i=2 i
2
n 2 n 1 n
n=0
dla
n 1
Operacja dominujca:
porwnanie
Zaoenia: poszukiwany obiekt key znajduje si w zbiorze A, gdzie wszystkie elementy s rne.
nie ma podstaw do stwierdzenia, e wystpowanie key na jednej pozycji jest bardziej
prawdopodobne ni na innej,
std prawdopodobiestwo znalezienia si poszukiwanego obiektu key na
dowolniej pozycji tablicy A jest jednakowe i wynosi: pk,n = 1/n
dla k = 1,2,,n.
n
1 n
1 n( n + 1) n + 1
Oczekiwana zoono czasowa: A( n ) = k pkn = k =
=
n
k =1
n k =1
2
2
n
n + 1 1
n2 1
=
0.29n
k
k =1
2 n
12
Wnioski: wraliwo czasowa jest klasy O(n) co sugeruje wraliwo algorytmu na struktur
danych wejciowych i moliwo odstpstwa od oczekiwanej zoonoci czasowej.
Gdy key moe nie wystpowa w tablicy, przypisujemy pewne prawdopodobiestwo p do
zdarzenia polegajcego na tym, e key wystpuje w tablicy.
Przyjmujemy, jednakowe prawdopodobne, e key znajdzie si na ktrejkolwiek pozycji tablicy.
Prawdopodobiestwo tego, e key znajduje si na k-tej pozycji, wynosi wwczas p/n,
natomiast prawdopodobiestwo, e nie znajduje si w tablicy, wynosi 1 - p.
n
p
p p
p 1
dla
n 1
1
1 1
+ + L + = = ln( n )
2 3
n i =1 i
Przykad 2.2.
Wypenianie fragmentu macierzy od przektnej wcznie.
H n =1 +
dla
2.6. Przykady
Wzory uyteczne przy szacowaniu zoonoci.
n
n +1
n
n ( n + 1)
( n + 1)( n + 2)
i = i + ( n + 1) =
i =
i =1
i =1
i =1
2
2
1
Rozwiza rwnanie: T ( n ) =
T ( n 1) + 1
T(n) = T(n-1) + 1
T(n-1) = T(n-2) + 1
T(n-2) = T(n-3) + 1
T(1) = T(0) + 1
T(0) = 1
n =1
dla
Rozwiza rwnanie: T ( n ) =
2
T ( n 1) + n
dla
16
k 1
Rozwiza rwnanie: T ( n ) =
{
int n=6, A[6][6] = {0}, i, j;
for (i=0; i<n; i++)
for (j=0; j<=i; j++) A[i][j]=2;
Drukuj;
}
2
2
2
2
2
2
0
2
2
2
2
2
0
0
2
2
2
2
0
0
0
2
2
2
0
0
0
0
2
2
0
0
0
0
0
2
T ( n ) = t p + t pr + 2t p + 2t pr + (2t p + t pr )
i =1
j =1
T ( n ) = t p + t pr + (2t p + 2t pr + i ( 2t p + t pr ) )
n
i =1
int main()
{
int n=6, A[6][6] ={0}, i, j;
i=0;
/
while (i < n) {
j=0;
while (j <= i) {
A[i][j] = 2;
j++;
}
i++;
}
Drukuj;
}
//P25b
/ przypisanie tp
// porwnania tpr
// przypisanie tp
// porwnanie tpr
// przypisanie tp
// przypisanie tp
// przypisanie tp
17
for i 1 to n do
s 0
for j 1 to i do s s + A[j]
sA[i] s
W(n) O(n2)
18
19
3. REKURENCJA
Rekurencja wystpuje wtedy, gdy obiekt skada si czciowo z samego siebie lub w definicji
odwouje si do siebie samego. Pozwala opisywa zbiory nieskoczone lub due
zbiory skoczone, przy pomocy skoczonego wyraenia.
Narzdziem implementacyjnym dla algorytmw rekurencyjnych jest pojcie funkcji.
W praktycznych realizacjach istotne jest pokazanie, e gboko rekurencji jest
skoczona oraz, e ich liczba jest relatywnie maa.
Struktura definicji rekurencyjnej:
1. warunek pocztkowy: element podstawowy, bdcy czci skadow pozostaych elementw.
2. krok rekurencyjny:
specyfikuje reguy tworzenia nowych obiektw z elementw podstawowych.
}
Fib(n 2) + Fib(n 1) dla n 2
dla n = 0
3.1. Silnia n! = n(n-1)(n-2)(3)(2)(1) dla n 1
1
n! =
Warto silni definiuje wzr rekurencyjny, skadajcy si z:
n
*
(
n
1
)!
dla
n>0
-warunku brzegowego,
-kroku rekurencyjnego.
Istotn cech rekurencji jest to, e do wyznaczenia wartoci long Silnia(int n)
{
elementu ak naley obliczy wczeniejsze elementy a1,ak-1.
if (!n) return 1;
4! wymaga wczeniejszego obliczenia wartoci 0!, 1!, 2!, 3!.
return n*Silnia(n-1);
Zastosowanie definicji rekurencyjnej sprawia, e implementacja formu }
matematycznych jest niemal automatyczna.
Funkcja Silnia(3) otrzymuje jako parametr
n:=3
n:=0
Silnia 3!
N
3!:=3
!:=3*2!
F(4)
F(3)
poziom 1
F(3)
F(2)
F(2)
F(0)
F(1)
F(0)
F(1)
F(1)
F(2)
poziom 2
2!:=2
!:=2*1!
n:=1
n=0
poziom 3
1!:=1
!:=1*0!
1
)
+
1
dla
n
si wyraenie: T(n) = n + 1.
Przedstawia ono czas wykonania algorytmu, ktry wynosi: T(n) = (n + 1)*tm
gdzie tm oznacza jednostkowy czas wykonania operacji mnoenia w okrelonym rodowisku.
1+ 5
2
= 1.61803
= 1 5 = 0.61803
F(0)
F(1)
F (n ) =
n n
5
R3.2
n:=2
n=0
F(5)
F(1)
R3.1
20
n
5
10
20
30
40
Dodawanie
7
88
10945
1346268
165580140
Wywoanie
15
177
21891
2692337
331160281
21
g ( n ) = g ( 2 + g ( 2 n ))
n
dla n = 0
dla n 4
dla n > 4
A(n, m) = A(n 1, m)
dla n > 0 i m = 0
A(n 1, A(n, m 1)) dla pozostalych
16
A( 4,1) = 2 2 3 = 2 65536 3
dla n 1
dla m 2
dla m, n 2
N2 16
A(3, 2) = 22
N2 4
22
= 22
22
= 2 65536
void RekKoncowa(int n)
{
if (n>0) {
printf("%d ",n);
RekKoncowa(n-1);
}
}
main( )
// P34 rekurencja koncowa
{
int n=7; RekKoncowa(n);
return 0 ;
}
7654321
#include <stdio.h>
void Non_RekKoncowa(int n)
{
for (; n > 0; n--) printf("%d ",n);
}
main( )
{
int n=7; Non_RekKoncowa(n);
return 0;
}
Rekurencja kocowa jest inn odmian konstrukcji
ptlowych i moe by przez nie zastpiona.
22
rekord
wywoania
funkcji
F3()
zaleno rekurencyjna.
Funkcja main() wywouje funkcj Power(4.4, 2) jest to "pierwszy poziom rekurencji".
float Power(float x, int n)
{ float wrk=-999;
printf("poziom %d, wrk=%g\n", n,wrk);
if (n==0) wrk= 1;
else wrk = x*Power(x, n-1);
printf("POZIOM %d, wrk=%g\n", n,wrk);
return wrk;
}
main()
// P33a
{ float f;
f = Power(4.4, 2);
printf("Power=%g \n",f);
}
poziom 1: Power(4.4, 2)
poziom 2: Power(4.4, 1)
poziom 3:
Power(4.4, 0)
poziom 2, wrk=-999
poziom 1, wrk=-999
poziom 0, wrk=-999
POZIOM 0, wrk=1
POZIOM 1, wrk=4.4
POZIOM 2, wrk=19.36
poziom 3:
poziom 2:
poziom 1:
1
4.4
19.36
23
ALGORYTMY
Dziel i
zwyciaj
Zachanne
Programowanie
dynamiczne
z
powrotami
Dziel:
podzia problemu na mniejsze
Zwyciaj: rozwi mniejszy problem rekurencyjnie
Scal:
czenie rozwizanych podproblemw
Maksimum {4, 1, 6, 8, 2, 5, 7, 3}
dziel
4168
2573
dziel
4 1 6 8
2 5 7 3
zwyciaj 4
8
5
7
zwyciaj
8
7
scal
8
2 4 8 11 14 25 27 33 34 45 55
0
dla n = 1
Zoono czasowa: T ( n ) = 1
dla n = 2
2T ( n / 2) + 2 dla n > 2
T (n ) =
3
n2
2
Merge(b, p, p+1,e, X)
Sortowanie { 8, 3, 6, 5, 4, 7, 2, 1 }
dziel
8365
4721
dziel
83 65
47 21
dziel
8 3 6 5 4 7 2 1
zwyciaj
38 56 47 12
3568
1247
zwyciaj
scal
12345678
// tablica X[be]
// b = e to dugo 1
// rodkowy indeks
// dziel X[bp]
// dziel X[p+1...e]
// scalanie podtablic
50 25 18 40 88 19 4 72 15
50 25 18 40 88
50 25 18
50 25
25
18
40 88
40
88
18 25 50
poziom 2
19 4 72 15
19 4
19
72 15
4
72
poziom 3
15 poziom 4
poziom 5
40 88
18 25 40 50 88
R4.1
poziom 1
50
25 50
dla n = 1
1
wikszy (mniejszy) od wszystkich elementw tablicy.
T (n) = lg(n) + 1
Jeeli n=1 nastpi porwnanie z key, a nastpnie T (n) =
T
(
n
/
2
)
+
1
dla n > 1
p = (b + e)/2
MaxMin(b, p, X, min1, max1)
MaxMin(p+1, e, X, min2, max2)
Algorytm
rekurencyjny
wyszukiwania
wykorzystuje rekurencj kocow.
Mona j zastpi iteracj, co pozwala unikn BinarySRek(A[ ]; int l, p; type key)
budowania stosu, o gbokoci do lg(n) + 1.
if (l > p) then return -1; // brak elementu
BinSearch(array A, int Left, Right, key)
mid (l + p)/2;
do forever
if (A[mid]=key) then return mid;
if (Left >Right ) then return -1
if (key<A[mid]) then
m= (Right + Left )/2
return BinarySRek(A, l, mid-1, key);
if (key < A[m] then Left =m -1
else
else if (key > A[m]) then Left =m + 1 else return m;
return BinarySRek(A, mid+1,p, key);
Podzia
Brak uniwersalnej metodologii budowy algorytmw, gdy jest to proces indywidualny i twrczy.
24
Scalanie
poziom 4
4 19
15 72
4 15 19 72
4 15 18 19 25 49 50 72 88
poziom 3
poziom 2
poziom 1
25
2 4 16 18 11 13 15 20 30
2
4 16 18
11 13 15 20 30
2 4
16 18
11 13 15 20 30
2 4 11
16 18
13 15 20 30
2 4 11 13
16 18
15 20 30
2 4 11 13 15
16 18
20 30
2 4 11 13 15 16
18
20 30
2 4 11 13 15 16 18
20 30
2 4 11 13 15 16 18 20 30
dla n = 1
0
T (n) =
T (n ) = n lg(n ) ( n 1)
2
T
(
n
/
2
)
+
n
1
dla n > 1
klasa O(nlg(n))
4.1.4. Uwagi
Program P41a1 realizuje algorytm poszu- int Szukanie_Lin_R(int A[ ], int l, int p, int key)
kiwania elementu w tablicy A, zgodnie ze {
if (l > p) return -1;
// brak elementu
strategi dziel i zwyciaj.
Problem gwny rozbijany jest na
podproblemy o analogicznej strukturze lecz
mniejszym stopniu zoonoci, gdy z rozmiaru
n schodzi si do n-1, poprzez sukcesywne
zwikszanie zmiennej l.
Implementacja zrealizowana zostaa z
wykorzystaniem techniki rekurencyjnej, dla
strategii dziel i zwyciaj.
Popeniony zosta istotny bd:
nieefektywna dekompozycja problemu.
26
27
28
ji
return A
i
1
2
3
4
5
6
7
8
nazwa
C1
C2
W1
L1
C3
L1
W1
L2
dT
3
3
2
4
3
4
2
4
bi
815
915
1115
1215
1315
1315
1515
1615
ei
11
12
13
16
16
17
17
20
29
30
+
0
// metoda wstpujca
long FibD1(int arg)
{ if (arg <= 0) return 0;
if (arg == 1) return 1;
long F[47] = { 0, 1 }; // inicjalizacja
for (int i = 2; i <= arg; i++)
F[i] = F[i-1] + F[i-2] ;
return F[i -1];
+
1
13
R4.2
if (arg > 1)
return F[arg] = FibD2(arg-1) + FibD2(arg-2);
}
1
0
0
0
0
0
Narastajce
2 3 4
0 0 0 0
0 1 1 1
0 0 1 1
0 1 1 1
0 0 0 0
1
rozmiary plecaka [j ]
5 6 7 8 9 10
0 1 1 1 1 1
1 0 0 1 1 1
1 1 1 0 1 1
1 1 1 1 1 1
0 1 1 1 1 1
31
#define num 5
int size[num] = {3, 4, 7, 8, 9};
int cost[num] = {4, 5,10,11,13};
int maxK[60], S[60];
0 1 2 3 4
s 3 4 7 8 9
c 4 5 10 11 13
A B C D E
cap S
0: 3
1: 3
3
2:
if (maxK[cap] != 0) return maxK[cap];
3: 3
4: 4
for (i = 0, max = 0; i < num; i++)
5: 4
if ((space = cap - size[i]) >= 0) 6: 3
if ((t = Knap1(space) + cost[i]) > max) {7: 7
8: 8
max = t; maxi = i;
9: 9
}
10: 3
3
11:
maxK[cap] = max;
12: 0
S[cap] = size[maxi]; // rozmiary przedmiotw 13: 3
14: 7
return max; // zwraca max. warto plecaka
15: 0
}
16: 0
17: 3
maxVal = 24 dla cap = 17
{ // modyfikacja funkcji rekurencyjnej P4_4c1
int i, space, max, maxi = 0, t;
#define num 5
int size[num] = { 3, 7, 12, 15, 22 };
int cost[num] = { 5, 2, 10, 15, 30 };
//P4_4b
// metoda wstepujca
5,3
dla n = k lub k = 0
1
n
C ( n, k ) = = n 1 n 1
k
+
dla 0 < k < n
k k 1
int Dwumian(int n, int k)
{ // n k
if ((n==k) || (k==0)) return 1;
else
return Dwumian(n-1,k)+Dwumian(n-1,k-1);
}
4,3
3,3
1
4,2
3,2
3,2
2,2 + 2,1
2,2 + 2,1
3,1
2,1 + 2,0
1
1
1
1 1
Algorytm rekurencyjny na zoono
wykadnicz, wynikajc z powtarzajcych si wywoa funkcji.
R4.3
32
1
2
3
4
5
6
7
8
9
1
3
10
15
21
28
36
10
20
35
56
84
1
5
15
35
70
126
1
6
21
56
126
1
7
28
84
1
8
36
1
9
Uwaga: Po obliczeniu wszystkich wartoci w wierszu i nie s potrzebne wartoci w wierszu i-1.
Obliczajc warto C(n,k) mona uy tablic jednowymiarow, indeksowan od 0 do k.
Zoono obliczeniowa algorytmu tablicowego
Tabelka obok pokazuje liczb obiegw ptli for-j w zalenoci od zmiennej i.
Cakowit liczb obiegw okrela zaleno:
1 + 2 + 3 + 4 + L + k + ( k + 1) + ( k + 1) + L + ( k + 1)
14444
4244444
3
nk +1
k ( k + 1)
( k + 1)(2n k + 2)
+ ( n k + 1)(k + 1) =
O ( nk )
2
2
i
0
1
2
3
przy czym k n
j
1
2
3
4
k
k+1
k+1 k+1
n
k+1
33
Nr
1
2
3
4
Kod
Lewy brzeg
123 3M 2L 1B
012 2M 1L 0B
113
3M 1L 1B
011
1M 1L 0B
112
2M 1L 1B
Rzeka
1M & 1L
1M
2M
1M
Prawy
0 0
1M 1L
1L
2M 1L
1M
Uwagi
ruch efektywny
ruch efektywny
ruch efektywny
ruch efektywny
ruch efektywny
1L
ostatni efektywny ruch
2L
2L
stan nieefektywny
powrt i zmiana stanu 6
1L
znowu ruch efektywny
1L
ruch efektywny
2L 1M
6
001
1M 0L 0B
1M & 1L 2M
7
103
3M 0L 1B
2M
0M
8 (6a) 010
0M 1L 0B
3M
2M
7a
111
1M 1L 1B
1M
2M
8a
000
0M 0L 0B 1M & 1L
3M
Problem zosta rozwizany z jednym krokiem powrotnym.
Sze pierwszych krokw jest efektywnych. Stanu 7 uniemoliwia wykonanie efektywnego ruchu,
std konieczno wycofania si do stanu 6 i pjcie inn drog (stan 6a), tzn. przeprawienie si
dwch misjonarzy, z ktrych jeden powrci po ludoerc. Dalsze kroki prowadz do sukcesu.
34
W pracy [2] zastosowano kodowanie jedn liczb w zapisie czwrkowym, gdzie poszczeglne
dwjki bitw to informacje o: odzi | ludoercach | misjonarzach ALL = 1234 = 2710
#include <stdio.h>
#include <stdlib.h>
#define MIS 1
#define CAN 4
#define BOAT 16
#define ALL BOAT+2*CAN+3*MIS
//1 10 11
#define NONE 0
#define SUBSET(x,y) ((x)%4 <= (y)%4 && (x)/4%4 <= (y)/4%4 && (x)/16 <= (y)/16)
int n, position[ALL+1], visited[ALL+1];
void printSide(int p)
{ printf("%*dM %dC %dB\n", n+1, p%4, (p/4)%4, p/16); }
int validMove(int pos1, int pos2)
/* Returns 1 if move is valid from pos1 to pos2, pos2 is legal, and has not already been seen.
Otherwise returns 0. */
{
//change in position
int diff = (SUBSET(pos2,pos1)?pos1-pos2:(SUBSET(pos1,pos2)?pos2-pos1:0));
int mis2 = pos2%4;
//missionaries in pos2
int can2 = pos2/4%4;
//cannibals in pos2
return(!visited[pos2]
//have not been there already
&& (diff==BOAT+MIS || diff==BOAT+MIS+CAN || diff==BOAT+2*MIS)
//valid move
&& can2 <= 2
//pos2 has at most two cannibals
&& (mis2 == 0 || can2 <= mis2)
//left bank missionaries uneaten
&& (3-mis2 == 0 || 2-can2 <= 3-mis2));
//right bank missionaries uneaten
}
int main()
// P4.2
{
position[0] = ALL;
//initial position is under consideration
visited[ALL] = 1;
printSide(position[0]);
position[1] = NONE;
//start with the first possible next move
n = 1;
while(position[n-1] != NONE) {
while(position[n]<=ALL && !validMove(position[n-1],position[n]))
position[n]++;
//loop until a valid next move, or no move
if(position[n]<=ALL) {
//there is a valid next move
visited[position[n]] = 1;
//mark that it is being considered
printSide(position[n]);
position[++n] = NONE;
//start its next moves at first possible one
}
else {
// no valid next move
visited[position[--n]] = 0;
// no longer considered prev state, backtrack
if(n==0) return 0;
position[n]++;
//advance previous state's next move
}
3M 2C 1B
}
2M 1C 0B
return 1;
3M 1C 1B
}
1M 1C 0B
2M 1C 1B
1M 0C 0B
3M 0C 1B
0M 1C 0B
1M 1C 1B
0M 0C 0B
35
Indeksy lewych przektnych sumuj si zgodnie z ir + jc, przy czym 2,0 2,1 2,2 2,3
jest to numer tej przektnej np.: ir + jc=2.
Szachownica zawiera 7 lewych przektnych, ktrym przypisujemy numery 3,0 3,1 3,2 3,3
od 0 6, czemu odpowiada tablica LDiag[7].
Indeksy prawych przektnych maj rnice zgodnie z ir - jc co pozwala przypisa im numery
od 3 3. Tworzymy tablic Pdiag[7], lecz do indeksw obliczonych ze wzoru ir jc naley
doda sta liczb norm, likwidujc ujemne wartoci.
Dodatkowa tablica PosInRow[row] kojarzy wiersz z aktualnym stanem kolumny.
Nie potrzeba pamita stanu wierszy, gdy ity Hetman przesuwa si wzdu rzdu i-tego.
Hetmany o numerach < od itego s ju ustawione w wierszach o numerach mniejszych od i.
void Hetman(int ir)
{ int jc, norm=3, OK=1;
for (jc=0; jc <= 3; jc++)
if (Col[jc]==OK && LDiag[ir+jc]==OK && RDiag[ir-jc+norm]==OK) {
PosInRow[ir] = jc;
Col[jc]=!OK; LDiag[ir + jc]=!OK; RDiag[ir-jc+norm]=!OK;
if (ir < 3) Hetman(ir+1); else Wynik();
Col[jc] = OK; LDiag[ir + jc] = OK;
}
RDiag[ir-jc+norm] = OK;
}
(1)
1
(2)
1
(3)
(4)
1
2
1
2
3
8 ir=3, jc=2,
4 ir=2, jc=1, Adr
7 ir=2, jc=0,
2 ir=1, jc=2, Adr
3 ir=1, jc=3, Adr
6 ir=1, jc=3,
1 ir=0, jc=0, Adr0 1 ir=0, jc=0, Adr0 5 ir=0, jc=1,
(2)
(4)
(8)
Zawarto ramek wywoa funkcji Hetman
//przywraca dostp do pl
Tablica 4.6
r
c
1
0
1 0
2
2 no
2
2 1
3
3
2 1
1 no
4
3 2
4
5
1
1 0
Adr
6
3
2 1
7
0
Adr
3 2
8
2
4 3
Adr
Adr0 Tablica 4.6 przedstawia kolejne
wykonane kroki przez funkcj Hetman,
prowadzce do pierwszego udanego
ustawienia czterech Hetmanw.
(8)
1
//testuje dostpnoci pl
// lokalizacja Hetmana
//markuje pola jako niedostpne
Rruch Nr H
36
#define OK 1
#define size 4
#define norm size-1
int Column[size], PosInRow[size], Count = 0;
int LDiag[2*size - 1], RDiag[2*size - 1];
void Disp()
{ int col, row;
for (row = 0; row < size; row++) {
for (col = 0; col < PosInRow[row]; col++) putchar('.');
putchar('Q');
for (col = PosInRow[row] + 1; col < size; col++) putchar('.');
putchar('\n');
} putchar('\n');
Count++;
}
.......Q
.Q......
....Q...
..Q.....
Q.......
......Q.
...Q....
.....Q..
.......Q
..Q.....
Q.......
.....Q..
.Q......
....Q...
......Q.
...Q....
int main()
{ int i;
for (i = 0; i < size; i++) PosInRow[i] = -1;
for (i = 0; i < size; i++) Column[i] = OK;
for (i = 0; i < 2*size - 1; i++) LDiag[i] = RDiag[i] = OK;
Queen(0);
printf("%d solutions\n", Count);
}
.Q..
...Q
Q...
..Q.
..Q.
Q...
...Q
.Q..
2 solutions
.......Q
...Q....
Q.......
..Q.....
.....Q..
.Q......
......Q.
....Q...
92 solutions
37
38
5. SORTOWANIE
Sortowanie jest procesem ustawiania zbioru obiektw w okrelonym porzdku.
Dana jest lista obiektw Q = [X1, X2, X3, ......, Xn ], bdca podzbiorem zbioru X.
Sortowanie moe polega na przestawianiu obiektw tak dugo a uzyska si ich ustawienie
w cigu niemalejcym X1 X2 X3 ...... Xn wedug zadanego klucza (funkcji sortujcej).
W praktyce klucz sortujcy jest przechowywany w postaci jawnej, jako
skadowe okrelonego pola obiektu. Wygodn form opisu obiektu jest struktura.
Dla analizy algorytmw sortowania istotne jest zdefiniowanie skadowej
struktury z kluczem, pozostae pola s nieistotne.
Typ klucza dopuszcza du dowolno np. char, integer, double.
struct Osoba {
int
wiek;
char[30] nazwisko;
char[20] zawod;
double waga;
};
12
13
14
15
16
n ln( n ) n + 1
i =2
ln( 2)
n lg( n ) 1.45n
Twierdzenie 5.2. Determistyczny algorytm sortujcy n kluczy wycznie poprzez porwnywanie ich
wartoci w najgorszym przypadku wykonuje nlg(n) 1.45n porwna.
Metoda stosowana automatycznie przy grze w karty, podczas ukadania ich w rozdaniu.
Obiekty sortowane dzielone s umownie na dwa podcigi o zmieniajcej si licznoci.
Jeden to podcig wynikowy (uporzdkowany) x1,.
..xi-1,
for i 2 to length(X) do
drugi to podcig rdowy (nieuporzdkowany) xi,....., xn.
v X[i]
Dla kadego i poczwszy od i=2 i-ty element podcigu
rdowego przenoszony jest do podcigu wynikowego i v w odpowiednie miejsce:
x1,..,xi-1
wstawiany w odpowiednie miejsce.
6
12
23
28
33
25
18
48
Istot metody jest przesuwanie wielu elementw w prawo o 1 pozycj a nastpnie wstawienie wybranego.
Tablica 5.1
15 12 30 40 33
Pocztek:
22
Krok 1:
15 22
Krok 2:
12 15 22
Krok 3:
12 15 22 30
Krok 4:
12 15 22 30 40
12 30 40 33
30 40 33
40 33
33
Krok 5:
12 15 22 30 33 40
Proste wstawianie
Miejsce do wstawienia elementu podcigu rdowego mona przygotowa poprzez
naprzemienne wykonywanie operacji porwnywania i przesuwania (przesiewanie).
InsertSort1(X)
// P5.1a
// i indeks zbioru rdowego
// j indeks zbioru wynikowego
// v zmienna robocza
X[0]
-32000
// wartownik
for i 2 to length(X) do
Liczba porwna w i-tym kroku: 1<Po# i - 1, rednio 0.5i
v X[i]
ji1
Elementy X[1]X[i-1] to podcig wynikowy.
while v < X[j] do
Elementy X[i]X[n] to podcig rdowy.
X[j+1] X[j]
Indeks i wskazuje element aktualnie wstawiany; przesuwa
si od lewej do prawej.
jj1
W kadej iteracji ptli for element X[i] jest pobierany z X[j+1] v
tablicy i przypisywany do zmiennej roboczej v.
//
bez wartownika byoby:
Porwnuje si element v z poprzednim elementem X[j],
// while v < X[j] && j > 0 do
poczym albo wstawia si v albo przesuwa X[j] na prawo.
Wartownik jest elementem cigu i
Zakoczenie przesiewania:
pilnuje jego koca, czyli
znaleziono element xi mniejszy od v;
powstrzymuje przeszukiwania przed
wyjciem poza cig.
osignito lewy koniec podcigu wynikowego.
Liczba porwna Po i przesuni Pr dla tablicy X[n]:
Pomin = n 1
Posr = 0.25(n2 + n - 2)
Pomax = 0.5(n2 + n) - 1
2
Prsr = 0.25(n +9n - 10)
Prmax = 0.5(n2 + 3n - 4)
Prmin = 2(n-1)
W/wym zalenoci pokazuj, e czas sortowania jest:
najmniejszy gdy elementy s ju uporzdkowane,
najwikszy gdy s ustawione w kolejnoci odwrotnej.
39
c4
X[j+1]X[j]
c5
ki
i=2
( ki 1)
i=2
jj-1
c6
X[j+1]v
c7
( ki 1)
i=2
n-1
1 = n 1
i =2
Algorytm sortowania przez wstawianie jest klasy O(n) dla zbioru posortowanego.
Przypadek pesymistyczny wystpuje dla cig uporzdkowanego w kierunku malejcym.
Kady element tablicy X[i] naley porwna z kadym elementem podtablicy X[1i-1],
std ki = i dla i (2, n).
n
n( n + 1)
1 oraz
2
(i 1) =
i =2
n( n 1)
2
T(n) = c1n+c2(n-1)+c3(n-1)+c4[0.5n(n+1)-1]+c5[0.5n(n-1)]+c6[0.5n(n-1)]+c7(n-1) =
= O(n2) + O(n) + O(c) = O(n2) + O(n)
40
Etap 22
y
50
4
18
4
40
4
88
4
19
4
72
4
4
4
i=5 4
i=6 4
i=7 4
i=8 4
22
18
18
18
18
18
18
50
22
19
19
19
19
19
18
50
22
22
22
22
22
40
19
50
40
40
40
40
88
40
40
50
50
19
88
72
72
72
72
72
72
88
88
88
88
88
i=2
i=3
i=4
50
50
72
for i
2 to n do
for j n downto i do
if X[j-1] > X[j] then X[j-1] X[j]
(n 1)*(n 1) = n2 2n + 1 O(n2)
void BubbleSort(int n, int *X)
{ int i, j;
//P5.2a
for ( i= 1; i < n; i++)
for (j = n-1; j >= i; j--)
if ( X[j] < X[j-1] ) swap(X[j-1], X[j]);
}
Po = 0.5(n2 - n)
Prmin = 0
Prsrednia = 0.75(n2 - n)
Prmax = 1.5(n2 - n)
Dwie istotne wady metody BubbleSort:
czsto wystpuj niepotrzebne przebiegi gdy elementy zostay ju posortowane (w tabeli
5.2. trzy ostanie przebiegi s ju zbdne, gdy stan posortowania uzyskano ju dla i = 5);
wraliwa na struktur danych: X1 =(8 4 5 22 30 66 50) sortuje w 1-szym przebiegu;
X2 = (8 5 22 30 50 66 4 ) wymaga 2-ch przebiegw.
Moliwoci poprawy algorytmu to midzy innymi:
zapamitanie czy w trakcie przejcia dokonano jakiejkolwiek permutacji - przejcie, w
ktrym nie ma zmian wskazuje, e proces sortowania moe by zakoczony;
zapamitanie indeksu ostatniej permutacji (k = j) pozwala ogranicza zbdne przebiegi,
gdy wszystkie obiekty poniej tego indeksu (k) s uporzdkowane.
Nastpne przejcie mona zakoczy na tym indeksie zamiast i do aktualniej granicy i.
przeczanie kierunku przegldania tablicy agodzi niekorzystne konfiguracje danych.
Tablica uporzdkowana z wyjtkiem lekkiego elementu na kocu X1= 4 12 16 22 44 55 2
posortowana zostanie w 1-szym przebiegu.
Z cikim na pocztku X2= 55 2 4 12 16 22 44 w 6-ciu (X2(1-szy) = 2 55 4 12 16 22 44).
Algorytm sortowania bbelkowego, poprawiony wg w/wym
ShakerSort (n, X)
// P5.2b
uwag nazywa si ShakerSort sortowanie przez wytrzsanie.
lewy2; prawyn
Tablica 5.3. Sortowanie bbelkowe poprawione ShakerSort
kn
repeat
Etapy 1
2
3
4
5
6
7
8
1-szy 22 50 18 40 88 19 4
72
for jprawy downto lewy do
if X[j-1] > X[j] then
22 50 18 40 88 19 72
2-gi 4
X[j-1] X[j]
22 18 40 50 19 72 88
3-ci
4
kj
18 22 19 40 50 72 88
4-ty 4
lewyk+1
18 19 22 40 50 72 88
5-ty
4
for jlewy to prawy do
Pomin = n 1, za pozostae parametry mona szacowa wg
if X[j-1] > X[j] then
zalenoci podanych przez Knutha:
X[j-1] X[j]
k
j
Posrednia 0.5(n2 - n(k1 - ln(n)); Prsrednia . n - k2sqrt(n)
prawyk
1
Szybki wzrost czasu sortowania ze wzrostem n.
Modyfikacja nie zmniejsza liczby permutacji tylko redukuje liczb porwna. until lewy > prawy
41
R5_3
h=4
h=2
h=1
22 50 18 40 88 19
22 19
72
40 88 50 18 72
22 19 4 40 88 50 18 72
4 19 18 40 22 50 88 72
4
18 19 22 40 50 72 80
p
o
d
z
b
i
o
r
y
12 9
12
9
c
o
5
5 22 4
2 30 1
2
30
1
22
0 28 29
29
0
4
s
ShellSort1(n, X)
// P5.3a
o
r
h n div 2
// wartoc poczatkowa odstpu
t
while (h > 0) do
wynik: 2
for i h + 1 to n do
c 2
jih
o
while (j > 0) do
3
jh j + h
s
o 0
if X[j] X[jh] then j0 //koniec ptli
r
t
else Swap(X[j], X[jh])
28
12
29
30
5
0
0
0
9
1
2
4
1
1
1
2
22
4
4 12 30
30
4
12
28
5
12
5 12 28
5 12 28
5 9 12
28
5 22 28
28
5
22
30
9
22
9 22 30
9 22 30
22 29 28
29
29
29
jjh
wynik: 0 4
2
29
Sort co 1 0 4
h h div 2
2
29
4
30
0 1
Ptla zewntrzna while kontroluje odstp
midzy porwnywanymi elementami.
W kadym kroku odstp zmniejszany jest dwukrotnie.
ShellSort2(n, X)
//P5.3b
W ptli rodkowej porwnuje si elementy odlege o h.
w3;
k[1..w]
(5,
3,
1)
W ptli wewntrznej elementy s przestawianie, jeeli
nie s uporzdkowane rosnco.
for m1 to w do
rednia efektywno algorytmu Shella wynosi n1.5
hk[m]
sh
//wartownik
Analiza efektywnoci algorytmu Shella wykazaa:
1. przyrosty nie musz by potg liczby 2,
for ih+1 to n do
2. cig przyrostw nie musi by cile zdefiniowany.
wrkX[i];
ji h
Przyrosty odstpw nie powinny by swoimi dzielnikami.
if s=0 then sh
Knuth wykaza, e dobrym cigiem przyrostw jest cig:
ss+1
hi+1=3hi +1 ` np.1,4,13,40,121 lub 1,3,15,31
X[s]wrk
Algorytm ShellSort2 zakada dowolny cigu przyrostw h1, h2,
while wrk < X[j] do
...,hi ,przy czym kade z hisortowa realizowane jest
metod prostego wstawiania.
X[j+h]X[j]
Metoda z z wartownikiem upraszcza warunek szukania miejsca
jj h
wstawienia elementu. Kade z hsortowa musi mie wasnego
X[j+h]wrk
wartownika, co rozszerza deklaracj wektora X o hmax.
42
43
Algorytm QuickSort nie jest stabilny oraz nie jest efektywny dla maych n.
Niech warto osiowa v jest zawsze wybierana jako najwiksza ze zbioru.
Przy kadym podziale lewy podzbir zawiera n-1 elementw za prawy tylko 1 element.
Jest to najgorszy przypadek dziaania algorytmu, gdy trzeba dokona n takich podziaw.
Koszt podziau wynosi n - 1, koszt sortowania lewego podzbioru T(n-1), za prawego T(0).
dla
0
T (n) =
T ( n 1) + n 1 dla
n =1
n( n 1)
T (n) =
n >1
2
T(n) = O(n2)
Sortowanie QuickSort nie jest bezporednie. Kiedy algorytm sortuje 1-sz podtablic,
pierwszy i ostatni indeks drugiej podtablicy musz by trzymany na stosie.
QuickSort nie daje gwarancji, podziau tablicy na p.
Funkcja Partition moe wielokrotnie dzieli tablic na jedn pust podtablic.
Na stosie znajdzie si wwczas n-1 par indeksw.
Zamiana rekordw wymaga trzech przypisa: w rednim przypadku zoono czasowa liczby
operacji przypisania rekordw wynosi: A(n) = 2.07(n+1)lg(n) .
QuickSort moe by zrealizowany technik rekurencyjn.
Nie absorbuje zbytnio zasobw komputera, gdy nie jest tworzona kopia tablicy X. W procesie
rekurencyjnym przechowywane s na stosie tylko indeksy wyznaczajce segmenty tablicy,
ktrych elementy maj ulec przestawieniu. Gboko stosu moe nie przekracza wartoci lg(n).
Efektywno algorytmu mona zwikszy trzymajc indeksy i, j tablic w rejestrach komputera.
44
1
1
1
1
1
1
1
B
C
5
1
2
1
5
3
C 0 1 2 2 4 6 8 8
Po 3-cim wykonaniu ptli A[6]=7
1
C 0 1 2 2 4 6 7 8
Po 4-tym wykonaniu ptli A[5]=5
1
B
C
CountSort(k, A, B)
n length[A]
for i 1 to k do C[i]
0
// O(k)
// O(n)
C[A[i]] C[A[i]] - 1
45
Zbir Wejciowy
589 234 867 238 123 987 232 192 876 543 124
Etap 1
wg
skrajnie lewych
Nr pojemnika 0
Etap 2
wg
2-ich od lewej
Etap 3
wg
3-ich od lewej
12
24
19
92
12
23
23
32
23
38
23
34
54
43
58
89
3
123 124
4
232 234
2
238
4
87
76
86
67
98
87
543
3 589
9
192
234
4
238
8
232
2
123
3
124
4 192
2
R5.6
a
876
6 867
7
543 589
3
876 867
6
Nr pojemnika
522
232
12
0
234
876
197
987
867
628
238
129
589
12 23
32 52
22 23
34 5 87
76 86
67 98
87 19
97 23
38 62
28 58
89 12
29
Etap 2
wg
2-ich od prawej
Etap 3
wg
3-ich od prawej
589
9 234
4 867
7 238
8 12
2 987
7 232
2 197
7 876
6 5 129
9 628
8 522
2
Etap 1
wg
skrajnie prawych
987
7
987
46
12
129
628
522
867
987
589
987
197
5 12 522 628 129 232 234 238 867 876 987 589 197
12
5
0
R5.6B
238
234
232
197
129
238
234
232
589
522
628
876
867
7
987
5 12 129 197 232 234 238 522 589 628 867 876 987
RadixSort1(We)
//podstawa systemu liczbowego
r 10
for i 0 to r1 do P[i]Null // utwrz pojemniki
for k m to 1 do
for i 0 to n - 1 do
We={5,234,867,238,123,98,232,192,876,543,124}
Etap 1: 5, 4, 7, 8, 3, 8, 2, 2, 6, 3, 4
Etap 2: 0, 3, 6, 3, 2, 9, 3, 9, 7, 4, 2
Etap 3: 0, 2, 8, 2, 1, 0, 2, 1, 8, 5, 1
ci Cyfra(We[i], m - k)
Put(P[ci], We[i])
Pocz pojemniki w nowy zbir We
W sortowaniu pozycyjnym liczba kluczy n w zbiorze nie jest wystarczajcym miernikiem zoonoci,
liczba znakw do przeanalizowania w cyfrze moe by wiksza od liczby n.
47
Algorytm sortowania traktuje liczb cakowit jako rekord, gdzie kluczem s cyfry na
kolejnych pozycjach poczynajc od najmniej znaczcej.
431
1
124
4
547
7
867
7
192
2
232
2
987
7
3
123
238
8
867
7
234
4
23
38
58
89
54
47
86
67
98
87
86
67
12
24
23
34
12
23
19
92
23
32
43
31
Wejcie
1 92
5 89
9 87
8 67
8 67
5 47
2 38
2 34
2 32
4 31
1 24
We
RadixSort2(We)
m length(Liczba)
for k 1 to m do
for j 0 to n - 1 do
Klucz[j] Cyfra(We[j], m - k)
SortujStabilnie(We.Klucz)
Etap 2
X 11
1000
P 10
0000
A 00
0001
R 10
0010
S 10
0011
T 10
0100
L 01
1100
E 00
0101
E 00
0101
M 01
1101
N 01
1110
G 00
0111
O 01
1111
Etap 3
P 10000
A 00001
R 10010
S 10011
T 10100
E 00101
E 00101
G 00111
X 11000
L 01100
M 01101
N 01110
O 01111
Etap 4
A 00001
E 00101
E 00101
G 00111
L 01100
M 01101
N 01110
O 01111
P 10000
R 10010
S 10011
T 10100
X 11000
Etap 5
// 0 do bytesword-1
Etap 1
T 101
100
X 110
000
P 100
000
L 011
100
E 001
101
A 000
001
E 001
101
M 011
101
N 011
110
R 100
010
S 100
011
G 001
111
O 011
111
N 0111
10
10
R 1001
T 1010
00
X 1100
00
P 1000
00
L 0110
00
E 0010
01
A 0000
01
S 1001
11
G 0011
11
E 0010
01
M 0110
01
O 0111
11
WE
1
0
1
0
0
1
1
1
1
1
0
0
0
R5.6d
Etap 3
czenie
Mona zaimplementowa metod zliczania indeksowanego kluczami lecz musi by ona stabilna.
0.11
0.96
0.45
0.66
0.19
0.22
0.14
0.74
0.27
0.77
0.11
0.14 0.22
0.19 0.27
Etap 2
Sortowanie
987
867
867
589
547
431
238
234
232
0.14
0.19 0.27
0.11 0.22
0
3
4
5
0.77
0.66 0.74
0.45
3
0.45
0.74
0.66 0.77
0.96
8
0.14
0.19
0.11
0.27
0.22
0-0.1 0.
0.1-0.2 0.
0.2-0.3
0.96
0.11 0.14 0.19 0.22 0.27 0.45 0.66 0.74 0.77 0.96
B
0
1
2
R5.6d
0.11 0.96 0.45 0.66 0.19 0.22 0.14 0.74 0.27 0.77
Etap 1
Wstawianie
1 1 4 2 2 2 5 8 8 9 5 1
192
Etap 3
wg
3-ich od prawej
Sortowanie kubekowe
3 3 9 2 3 2 6 8 6 4 3 8
124
Etap 2
wg
2-ich od prawej
48
9 4 7 8 3 7 2 2 7 7 4 1
1 23
Etap 1
wg
skrajnie prawych
123
Zbir Wejciowy
589
9
0.14
0.27
0.19
0.22
0.11
0.45
6
7
8
0.66
0.77
0.96
BUCKETSORT(We)
n length[We]
for i 0 to n - 1 do
Wstaw We[i] do listy B[ i ]
for i 0 to n 1 do
i =0
i =0
49
50
6. WYSZUKIWANIE WZORCA
8
w
9
a
10 11 12
n i e
n i e
Istot algorytmu jest przeszukiwanie w ptli, okrelonej dugoci wzorca W[1, m].
Sprawdzany jest warunek W[1, m] = T[s+1, s+m] dla kadej z n m +1 moliwych
wartoci przesunicia s.
c e r c e r a
c e r c e r a
c e r c e r a
s=0 c e r a
s=1 c e r a
s=2 c e r a
Wzorzec W mona interpretowa jako okienko przesuwajce si sukcesywnie ( dla narastajcych
wartoci przesunicia s ) nad analizowanym tekstem.
Po kadym przesuniciu okienka (zmiana s) sprawdzane jest ponownie czy wszystkie symbole w
okienku zgodne s z odpowiadajcymi im symbolami w tekcie.
BruteForce(T, W)
int BruteSearch(char *W, char *T)
{
n length[T]
int i, j, N, M = strlen(W);
// j indeks dla wzorca
m length[W]
N = strlen(T);
// dugo tekstu
for s 0 to n m do
for (i = 0, j = 0; (j < M) && (i < N); i++, j++)
if W[1, m] = T[s+1, s+m]
while (W[j] != T[i]) {
then "znaleziono wzorzec"
i = i - (j - 1);
T = 1010100111 W = 1001
j = 0;
T: 1010100111
}
W: 1001
if (j == M) return (i - M); else return (i);
}
1001
n
1001
Rozwamy tekst T skadajcy si z cigu n symboli a (a ) i
1001
wzorzec W = am.
1001 Znaleziono dla s=4
Dla kadego z n m + 1 moliwych wartoci przesunicia s,
ptla for bdzie wykonywana m razy, aby sprawdzi
Czy ponisza koncepcja jest moliwa?
poprawno przesunicia.
c e r c e r a
Pesymistyczny czas dziaania wynosi (porwnania):
O((n - m + 1)m), za dla m = n/2 O(n2).
Algorytm wyszukiwania naiwnego nie jest optymalny, gdy s=0 c e r a
q = 3
informacja o strukturze tekstu dla danej wartoci S nie jest
brana pod uwag przy nastpnych poszukiwaniach.
c e r c e r a
Po kadej zmianie s nastpuje cofanie si o ca dugo
wzorca, zapominajc o wynikach testw wczeniejszych.
s=s+q = 3 c e r a
T = 10101001110
Niech oznacza zbir wszystkich sw utworzonych z alfabetu . Dugo sowa x oznaczmy |x|.
Konkatenacja (zoenie) dwch sw x oraz y (oznaczona xy) jest sowem o dugoci |x| + |y|.
Sowo w jest prefiksem sowa x (w x), gdy x = wy dla sowa y , |w| |x|.
Sowo w jest sufiksem sowa x (w x), gdy x = yw dla sowa y , zatem |w| |x|.
Dla sowa x = abcca: ab jest prefiksem (ab
abcca) za cca jest jego sufiksem (cca abcca)
Niech Wk oznacza ksymbolowy prefiks W[1, k] wzorca W[1, m]. Zatem W m = W = W[1, m].
Niech Tk oznacza ksymbolowy prefiks T[1, k] tekstu T[1, n].
Zatem Tn = T =T[1, n]
Wyszukiwanie wzorca jest problemem znajdowania wszystkich przesuni s w
przedziale 0 s n m takich, e W Tk+m.
Przesunito okienko ze wzorcem W = ALALAno wzgldem tekstu T na pozycj s = 4
Stwierdzono, e q = 5 symboli
wzorca pasuje do tekstu.
e s
A L A L A e f
g f
e f
s=4 A L A L A n o
q=5
e s t A L A L A e
s=6 A L A L
nowa cz wsplnaq=3
s=8 A L
f g f e f
j= [5] + 1
A n o
si = 4 + (5 3) = 6
A L A n o
si+1 = 6 + (3 1) = 8
51
KMP_Search(W, T)
Prefix(W)
m length(W); n length(T)
Prefix(W)
q0
for i1 to n do
while q > 0 && W[q+1] T[i] do q [q]
if W[q+1] = T[i] then q q + 1
if q = m then s i m
// znaleziono
q [q]
mlength(W)
[1]0; k0
for q2 to m do
// q zakadana cz wsplna
while k>0 && W[k+1] W[q] do k [k]
if W[k+1] = W[q] then k k + 1
[q] k
q
1 2 3 4 5 6 7 8 9 10
Wq a b c d b a b c a b
return
(q) 0 0 0 0 0 1 2 3 1 2
// analiza wzorca
Podsumowanie:
1. Dla kolejnych podtablic[1..q] wzorca W[1..m] zbuduj tablic [q] zawierajc
hipotetyczne dugoci wsplnych prefiksw i sufiksw.
2. Przesuwaj wzorzec nad tekstem w poszukiwaniu czci wsplnych q wzorca i tekstu.
3. Nowe przesunicie ramki wzorca okrela wzr:
snowe = s + (q [q]).
4. Po wykonaniu nowego przesunicia rozpocznij porwnania od poz. (q)+1 wzorca.
T o B A T A i
B A T A i
B A T A m a m B A T
B A T A N
s=2 B A T A i
1 2 3 4 5 6 7 8 9 10
q
Wq B A T A i B A T A N
(g) 0 0 0 0 0 1 2 3 4 0
B A T A N
B A T A i
B A T A N
s=11 B A T A i
52
6 9 5
2 2 6 9 5 3 8
s=1
6 9 5
2 2 6 9 5 3 8
s=2
6 9 5
Schemat pokazuje, e istot algorytmu jest umiejtno szybkiej zamiany tekstu w liczb.
Oglnie mona przyj, e kady symbol jest liczb w systemie o podstawie d, co
oznacza, e dysponujemy delementowym alfabetem = { 0, 1, 2, 3,, d-1 }.
Dany jest dowolny cig M znakw T[i], T[i+1],,T[i+M-1].
abcdefghlj
Niech d (podstawa systemu) bdzie liczb wszystkich moliwych znakw.
Cig znakw T moemy interpretowa jako liczb cakowit postaci:
(0)
M-1
M-2
x = d T[i]+ d T[i+1]+ + dT[i+M-2] + T[i+M-1]
abcdefghlj
Jeeli przesuniemy si w tekcie o jeden znak to otrzymamy now liczb postaci:
x(1) = dM-1T[i+1] + dM-2T[i+2] + + dT[i+M-1] + T[i+M]
abcdefghlj
Liczb x(1) mona wyznaczy iteracyjnie: x(1) = d(x(0) dM-1T[i]) + T[i + m]
lub x(1) = d(x(0) mod dM-1) + T[i + m]
Wemy alfabet, w ktrym symbole s cyframi dziesitnymi = {0,1,2,3,4,5,6,7,8,9} (d = 10).
j = [9] + 1
s=7
si = 2 + (9 4) = 7
si+1 = 7 + (4 0) = 11
T o B A T A i
B A T A i
s=2
B A T A i
B A T A N
B A T A N
B A T A i
B A T A N
j = [9] + 1
s=7 B A T A i
B A T A N
Warto p (wzorzec) mona obliczy w czasie O(m) z reguy Hornera (2*103 +4*102 +3*101+6):
p = (((10W[1]+ W[2])10 + + W[m - 2])10 + W[m - 1])10 + W[m]
Dla tekstu T[1, n] odpowiadajc warto t0 mona obliczy wg tej samej reguy w czasie O(m).
Wartoci t1, t2, , tn - m mona obliczy w czasie O(n m) z zalenoci iteracyjnej:
ts+1 = 10(ts 10m 1T[s + 1]) + T[s + 1+m]
int next[100];
// odpowiada tablicy (q)
void Prefix(char *W)
{ int j, q
// q zakadana dugo czci wsplnej
int m = strlen(W);
next[0] = -1;
for ( q=0, j=-1; q<m; q++, j++, next[q] = j )
while ((j>=0) && (W[q] != W[j])) j = next[j];
}
1 2 3 4 5 6 7 8 9 10
q
Wq a b a b a b a b c a
(q) 0 0 1 2 3 4 5 6 0 1
m1
ts 10
T[s + 1] usuwa najbardziej znaczc cyfr z ts ,
10()
przesuwa cyfry liczby o jedn pozycj w lewo,
+ T[s +m +1]
wstawia najmniej znaczc cyfr.
Dugo wzorca m = 5, ts = 92345, za nastpny fragment tekstu ma posta T = 9234567 .
ts+1 = 10(92345 1049) + 6 = 23456 lub ts+1 = 10(92345 mod 104) + 6
m 1
Staa 10
jest obliczana raz, zatem kadorazowa iteracja ts wymaga wykonania staej liczby
operacji arytmetycznych.
Wartoci p oraz t0, t1, , tn -m mona obliczy w czasie O(n + m).
Algorytm umoliwia znalezienie wszystkich wystpie wzorca W w tekcie T w czasie O(n + m).
podstawa d = 64
={
= 0
a =1
b =2
c =3
d =4
e =5
...
z = 26
A = 27
B = 28
C = 29
D = 30
E = 31
...
Z = 52 }
W = Cabec
m=5
4
3
2
p = 2964 +164 +264 +564+3 = 486809923
p = (((C64+a)64+b)64+e)64+c
T=Gdzie jestnaszCabec
t0=(((3364 + 4)64 + 26)64 + 9 )64 +5 = 554803781
4
t1 =dzie= 64(t0 64 33) + 0 =
4
lub t1 = 64(t0 mod 64 ) + 0 =
53
q = 17; p mod q = 6
t1=36834, t1 mod q =12
10 11 12 14 14 15 16 17
2 3 6 8 3 4 5 6 7 3 8 6 2 8 3 8 7
ti mod q 2
12 4
6 11 9
14 13 4
6 14
mod 17
NIEzgodne
Zazwyczaj za warto q przyjmuje si tak liczb pierwsz, e d*q zajmuje jedno sowo
komputerowe, co umoliwia realizacj operacji arytmetycznych z pojedyncz precyzj.
Dla delementowego alfabetu ={0, 1, , d-1} wzr iteracyjny na wartoci ts+1 przyjmuje posta:
m-1
ts + 1 = (d(ts hT[s + 1]) + T[s + m +1]) mod q
gdzie h = d
mod q.
Zauwamy, e ts p (mod q) nie implikuje rwnoci ts = p.
Zatem przesunicie s moe lecz nie musi by waciwe.
Zauwamy: jeeli ts p (mod q) to na pewno ts p i tym samym przesuniecie s jest niewaciwe.
W praktyce test ts p (mod q) uywany jest do eliminowania niepoprawnych przesuni.
Gdy przesunicie spenia powyszy warunek naley jeszcze zweryfikowa czy rzeczywicie jest poprawne.
Weryfikacj poprawnoci mona przeprowadzi bezporednio porwnujc W[1, m]=T[s+1, s+m].
Przyjmujc warto q dostatecznie du mona spodziewa si rzadkiej potrzeby dodatkowej weryfikacji.
RabinKarp(T,W, d, q)
n length [T]; m length [P]
h dm - 1 mod q
p 0;
t0 0
for i 1 to m do
p(dp + W[i]) mod q
t0(dt0 + T[i]) mod q
// p = W[1, m] mod q
// t0 = T[1, m] mod q
#include <stdio.h>
#include <string.h>
#include <ctype.h>
54
Alfabet:
= { ,a, b, c, ...,z, A, B, C,...,Z }
// zwraca numer litery w alfabecie oraz postaw alfabetu d = 64
// spacja=0
a=1
b=2
c=3
d=4
e=5
f=6
g=7
z = 26
A = 27
B = 28
C = 29
D = 30
E = 31
F = 32
G = 33
Z = 52
int main()
// P63 RabinKarp
t0 = 5 + 64(9 + 64(26 + 64(4 + 64G)))= 554803781
{
t0 mod 121 = 26
int l;
char *t1 = "Gdzie jest nasz Cabec malowany",
*w = "Cabec";
l = RK_Search(w, t1, 64, 121);
if (l > 0) printf("\n Znaleziono na poz=%d", l);
else printf("\n Nie znaleziono ");
return 0;
}
Znaleziono na poz=16
55
7. HASZOWANIE (MIESZANIE)
Problem dotyczy wyszukiwania informacji w tablicach wedug pewnej specyficznej metody.
Technika wyszukiwania sekwencyjnego realizuje si w czasie O(n), za binarnego O(lg(n)).
Dla struktury danych jak jest tablica z haszowaniem czas wyszukiwania
informacji mona sprowadzi do teoretycznego poziomu O(1).
Istot metody jest wyznaczenie pozycji klucza w tablicy na podstawie jego wartoci.
Warto klucza jest podstaw okrelenia pooenia elementu. Znajc klucz mona dosta si
bezporednio do dowolnego miejsca w tablicy, pomijajc kosztowne operacje przeszukiwania.
Naley znale odpowiedni funkcj h(KEY), ktra przeksztaci klucz KEY (liczba, rekord,
acuch) w indeks tablicy przechowujcej wartoci tego samego typu co klucz.
Funkcja h(key) przeksztacajca klucz w indeks nosi nazw funkcji haszujcej (hash function).
Idealna funkcja haszujc: jeeli przeksztaca rne klucze w rne liczby.
Mieszanie klucza: operacja zastosowania funkcji haszujcej dla konkretnego klucza.
Tablice z haszowaniem s wygodn struktur danych, reprezentujc sowniki, na ktrych
wykonywane s tylko trzy operacje: INSERT, SEARCH, DELETE.
Przykadem mog by kompilatory jzykw programowania, ktre generuj tablice symboli o
kluczach, bdcych identyfikatorami zmiennych.
Idealna funkcja haszujca wymaga, aby na tablic zarezerwowano tyle miejsca ile moe
wystpi rnych elementw. Liczba elementw nie zawsze jest znana. Nie jest np. moliwe
okrelenie pojemnoci tablicy symboli generowanych przez kompilator, gdy zaley to od
wielkoci programu. Przyjcie wielkoci maksymalnie moliwej, czsto jest nierealne fizycznie.
Zamy, e jzyk programowania (np. C) dopuszcza dugo zmiennych do 31 znakw jako
30
zmiennej nie moe zaczyna si od cyfry), ktra fizycznie nie moe by podstaw do rezerwowania
takiego obszaru pamici, aby zapewni kadej zmiennej osobn pozycj.
W praktyce tworzone programy zawieraj z reguy nie wicej ni kilkaset rnych
zmiennych, tym samym zarezerwowanie tablicy o rozmiarze T[1000] jest na og
wystarczajce, jakkolwiek uniemoliwia to zbudowanie idealnej funkcji haszujcej.
Niech dla potrzeb testowania nowego kompilatora przyjto, e identyfikatory zmiennych bd
maksymalnie trzyznakowe i stanowi kombinacj 3 liter (a, b, c).
Otrzymujemy cig zmiennych: a, b, c, aa, bb, cc, ab, ba, ac,ca, bc, cb, aaa, aab, aac, abc, aba, baa, caa, cba,
2
:
Poszczeglnym literom przypiszemy cyfry a 1, b 2, c 3.
:1008
11
aa
Wwczas cigowi zmiennych mona przypisa liczby typu integer
:
( ab12, aab112 ,, ccc333), ktre staj si indeksami
:2300
121 abc
komrek tablicy symboli, przechowujcych np. adresy.
333 ccc
:1204
Zmienna ab ulokowana zostanie w komrce 13, ccc w komrce 333.
Dostp przez program do dowolnej zmiennej realizowany jest w czasie O(1), gdy klucz
do konwersacji tekstu na liczb integer staje si generatorem adresu tekstu.
Wymagane jest zarezerwowanie tablicy o wymiarze T[333] mimo, e maksymalna liczba
identyfikatorw zmiennych nie przekroczy 39.
Mwimy, e funkcj haszujc h jest algorytm zamieniajcy cigu znakw ASCII w liczb.
Jeeli przyjmiemy za identyfikatory zmiennych symbole np. 11sto znakowe (a k) i
przypiszemy im odpowiedniki liczbowe (1 11), to stosujc identyczn funkcje haszujac
otrzymamy wyniki niejednoznaczne.
Przykadowo h("ka") = 111 oraz h("aaa") = 111.
Jeeli bdziemy dodawa kody wszystkich znakw w nazwie, za sumy uyjemy jako
indeksu to te nie unikniemy niejednoznacznoci.
Kolizja funkcja haszujca generuje identyczne wartoci liczbowe dla rnych kluczy.
Jako funkcji haszujcej zaley od skutecznego unikania przez ni kolizji.
i =0
56
1
jednej licie wynosi: 100
100
100
10 198 .
57
58
Abstrakcyjny Typ Danych typ danych dostpny tylko za porednictwem stosownego interfejsu.
22 44 2 12
topS = 5
(a) Push(33, S), Push(54, S)
1
22 44 2 12 33 54
topS=7
(b) Pop(S), Pop(S), Pop(S)
1
22 44 2 12 33 54
topS = 4
Elementy 12, 33, 54 nie
nale ju do stosu.
9383*+4/6
abc + cos d ^ * ef *
0
1
2
3
yabc*+:=
EvalPost(char *bufor)
typeStack S;
// definicja stosu
Init(&S);
// inicjalizacja stosu
typElemS zn;
int n = strlen(bufor), wrk, i;
for (i=0; i<=n; zn=bufor[i++]) {
if (zn == '+') Push( (int)Pop(&S) + (int)Pop(&S), &S);
#include <iostream.h>
int sA[80],
topA;
void InitStack()
{ topA=0; }
void Push(int data)
{ sA[topA++] = data;
}
int Pop()
{ return (sA[--topA]);
}
59
main()
// P81 243+51+**
{ InitStack();
Push(2); Push(4); Push(3);
Push(Pop() + Pop());
Push(5); Push(1);
Push(Pop() + Pop());
Push(Pop() * Pop());
Push(Pop() * Pop());
cout <<"Wynik ="<<Pop()<<endl;
}
Wynik = 84
2*((4+3)*(5+1)) == 243+51+**
Stan stosu: 243+51+**
3
4 7
4
2 2
2
2
5
7
2
1
5
7
2
6
7
2
42
2
84
#include "stos.h1d"
// P8_3a
Stack S;
Init(&S);
// inicjalizacja stosu
typS zn;
int i, k = 0, n = strlen(We);
for (i=0; i <= n; zn = We[i++]) {
if (zn == ')') Wy[k++] = Pop(&S);
if ((zn == '+') || (zn == '*')) Push(zn, &S);
if ((zn >= '0') && (zn <= '9')) Wy[k++] = zn;
}
while (!Empty(S)) Wy[k++] = Pop(&S);
Wy[k] = '\0';
}
int Empty(Stack S)
{ return S == NULL; }
44
Head
Tail
Enq(33, Q), Enq(44, Q), Enq(55, Q)
Tail
Head
Enq(33, Q), Enq(44, Q)
1
44 55
6 33
Tail Head
struct NODES {
typS
Data;
NODES *top;
};
typedef NODES *Stack;
60
6 33
Tail Head
Enq(el, Q)
// wstawienie do kolejki
if Full(Q) then Return(Przepenienie)
Q[tail] el
if Q[tail] = length(Q) then tail 1
else tail tail +1
Deq(Q)
// wyjcie z kolejki
wrk Q[head]
if Q[head] = length(Q) then head 1
else head head + 1
return wrk
head=3
struct
int
int
typ
};
QUEUE {
head,
tail;
vec[max];
(a)
1
18 33 54
tail = 7
Enq(10,Q), Enq(11,Q),
Enq(12,Q), Deq(Q)
2
12
18 33 54 10 11
tail=2 head = 4
(b)
1
Enq(13,Q)
2
12 13 2
18 33 54 10 11
tail=3 head =4
61
int main()
// P8.2a test kolejki
{
// program wpisuje do Q1 cig liczb losowych,
//
do Q cig kolejnych liczb integer
int i = 0;
Queue Q, Q1;
InitQ(&Q); InitQ(&Q1);
62
8.3. Lista
// tail wskazuje ostatnio wstawiony el.
Czsto maksymalny rozmiar struktury nie jest znany w trakcie jej projektowania.
Przy zmiennym zapotrzebowaniu programu na pami Operator new zwraca adres przydzielonego bloku.
mona stosowa dynamiczny przydzia pamici.
typData *newNode; newNode = new(typData);
Jzyk C++ udostpnia stosowne funkcje.
Przydzielon pami zwraca na stert operator delete.
Mechanizm dynamicznego przydziau pamici, umoliwia tworzenie wizanych struktur danych,
w ktrych nastpny element nie musi bezporedni przylega do poprzedniego.
Mamy niecig struktur danych, rn od tradycyjnych cigych struktur tablicowych.
Jest to istotne w procesie wyszukiwaniu i wstawianiu elementu do struktury.
Dowizanie jest dodatkow informacj przechowywan w kadym wle, wskazujc gdzie jest
przechowywany nastpny element. Wstawiajc nowy element do listy (gdziekolwiek w pamici),
naley zmieni warto dowizania, aby nowy element wskazywa na nastpny.
Lista z dowizaniem jest struktur, w ktrej elementy uoone s liniowo, wedug
porzdku okrelonego przez wskaniki dowizania a nie przez indeksy danych.
8.3.1. Lista jednokierunkowa (singly linked list)
Kady element listy stanowi struktur o dwch polach:
pole danych (Dane),
dowizanie (next) wskanik nastpnego elementu listy.
Ostatni element listy zawiera pusty wskanik NULL.
Head
Q->tail = 0;
typ wrk;
if (Empty(Q)) cout << "Kolejka pusta\n";
else {
wrk = Q->vec[Q->head];
if (Q -> head == Q -> tail) Init(Q);
else if (Q -> head==maxQ-1)
Q -> head = 0;
else Q -> head++;
}
return wrk;
}
//_____________________________________
void DisQ(Queue q)
{
if (Empty(&q)) cout<<"Kolejka pusta\n";
void DisQht(Queue q)
{
cout <<"Head="<<q.head<<" Tail="<< q.tail<< "\n";
}
else {
for (int i=q.head; i != q.tail; i=(i+1) % maxQ)
cout <<q.vec[i]<<" ";
cout <<q.vec[i] << "\n";
}
}
next
s
dre
Dane
5
1-szy
next
Dane
12
poprzedni
next
NULL
Dane
Dane
2
biecy
ostatni
struct ListNode {
ListNode *next;
int numer;
float Data[1024];
};
R8.3
next
Dane
2
Dane
5
next
w1
w2
Del
12
nowe
next
Dane
22
w4
wn
nowe
next
Nowy
25
nowe
next
NULL
Dane
30
Dane
55
w5
ostatni
usuwanie wza w3
wrk = w2>next //adres w3
w2>next = wrk>next
idn = new(ListNode);
if (idn==NULL) { puts("Error memory"); exit(1); }
idn->Data = e;
Head
idn->next = NULL;
L
return idn;
}
Head
64
Del
Old links
idn
New links
List DelFirstList(List L)
{ List idn
If (emptyList(L)) { puts("Empty List"); exit(-1); }
idn = L->next
// new head in idn
delete(L);
// release space
// return new head
return (idn);
}
Aby znale miejsce na wstawienia elementu lub jego usuniecie naley przejrze list.
Przegldanie listy zwraca adres wza lub NULL
List searchList(typData e, List L)
{ int OK = 0;
while (!emptyList(L)) && !OK) {
if (e==headList(L) OK = 1;
else L = pointNextNode(L);
}
if (OK) return L; else return NULL;
}
List pointNextNode(List L)
{
if (emptyList(L)) {
puts("Lista pusta"); exit(1);
}
return (L->next);
}
int main()
// List1
List readList(List L)
{
{
List L, r, t, f, prev;
typData e;
int i,n;
while(readElement(&e))
L = newList();
L = AddBeforeList(e, L);
puts("Podaj elementy listy: ");
return L;
L = readList(L);
}
printf("Lista ="); printList(L);
printf("Wartosc szukanego elementy: "); scanf("%d", &i);
f = searchList(i, L);
if (f) printf ("Znaleziono %d\n",headList(f)); else puts("Nie znaleziono");
printf("Ktory element usunac: "); scanf("%d",&i);
prev= searchList(i, L);
DelBehindNode(prev);
printf("Po usunieciu="); printList(L);
printf("Po ktorym elemencie co dodac: "); scanf("%d %d", &i, &n);
prev= searchList(i, L);
AddAfterNode(n, prev);
printf("Po dodaniu=");
printList(L);
return 0;
}
struct ListNode {
typData
Data;
ListNode *next;
ListNode(typData x, ListNode *p)
{ Data = x; next = p }
};
typedef ListNode *List;
List NewAdr = new ListNode(2.3, Adr);
int lengthList(List L)
{
int len =0;
while (! emptyList(L)) {
len = len + 1;
L = pointNextNode(L);
}
return len;
}
63
R8_5d
65
next
next
next
next
Dane
5
Dane
12
Dane
44
Dane
48
Lidn
while (x != x->next) {
6, 3, 1, 9, 2, 5, 4, 8, Boss = 7
66
Szukaj(el, L)
wrk FirstL
while wrk NULL and key[wrk] el do
wrk next[wrk]
return wrk
NULL
then prev[next[el]] prev[el]
wskanika do usuwanego elementu.
Pesymistyczny czas usuwania elementu wynosi O(n) i determinowany jest czasem dziaania
procedury Szukaj.
Samo usuwanie realizowane jest w czasie O(1).
Usunicie wza z listy dwukierunkowej wymaga tylko wskanika do tego wza.
Wady: zwikszony rozmiar wza, podwojona liczby operacji na czach w kadym wle.
Wartownicy
Jeeli do listy dooymy dodatkowy wze WartL (bez pola Data) to mona wyeliminowa
operacje na zmiennej FirstL (wskazuje na pierwszy element listy).
Mamy cykliczn list dwukierunkow, w ktrej wze WartL znajduje si midzy gow a ogonem.
Pole next[WartL] wskazuje na gow
listy za pole prev[WartL] na ogon.
Lista pusta skada si tylko z wartownika i next[WartL] oraz prev[WartL] -wskazuj na NULL.
Wstaw(el, L)
Szukaj(el, L)
next[el] next[WartL]
prev[next[WartL]] el
next[WartL] el
prev[el] WartL
wrk next[WartL]
while wrk WartL and key[wrk] el do
wrknext[wrk]
return wrk
Usun(el, L)
next[prev[el]] next[el]
prev[next[el]] prev[el]
Wartownik nie zmienia klasy zoonoci operacji na danych, lecz upraszcza warunki brzegowe.
67
68
#include<iostream.h>
#include<stdlib.h>
void DispL(List L)
{
for (List w = L; w != NULL; w = w->next)
cout << w->Data <<" ";
cout << endl;
}
int main()
// SortLis1
{
ListNode We(-1, 0), Wy(-1, 0);
// wezly-Atrapy
List t = &We, x, u;
for (int i=0; i < n; i++) t = (t->next = new ListNode(rand() % 100, 0));
DispL(&We);
//---------------------------------------------------------------sortowanie
for (t = We.next; t != 0; t = u) {
u = t->next;
// nastpny wze z listy We czyli usunicie poprzedniego
for (x = &Wy; x->next != 0; x = x->next) if (x->next->Data > t->Data) break;
t->next = x->next; x->next = t;
// wstawianie zmiana adresw
}
DispL(&Wy);
return 0;
-1 46 30 82 90 56 17 95 15 48 26 4
}
-1 4 15 17 26 30 46 48 56 82 90 95
List MaxL(List L)
{ // zwraca adres wza poprzedzajcego wze max
typedef struct ListNode {
int max = L->Data; List prev;
int Data;
for (List x = L; x->next != 0; x = x->next)
ListNode *next;
if (x->next->Data > max)
ListNode(int x, ListNode *t)
{ max = x->next->Data; prev = x; }
return prev;
{ Data = x; next = t;}
}
}*List;
void DelNode(List prev)
void DisL(List);
{
void GenL(List, int);
List wrk;
void DelNode(List);
wrk = prev->next;
// wrk points to be deleted
List MaxL(List);
prev->next=wrk->next; // skip node to be deleted
const int n=11;
}
int main()
// SortLis2
void DisL(List L)
{
ListNode We(-1,NULL), Wy(-1,NULL); // wezly-Atrapy {
for (List w = L; w != NULL; w = w->next)
List in = &We, out = &Wy, prev, max, r;
cout << w->Data <<" "; cout << endl;
GenL(in, n);
}
DisL(&We);
while ( in->next) {
prev = MaxL(&We); max = prev->next;
DelNode(prev);
r = out->next;
out->next = max; max->next = r;
}
DisL(out);
return 0;
}
-1 46 30 82 90 56 17 95 15 48 26 4
-1 4 15 17 26 30 46 48 56 82 90 95
69
70
9. DRZEWA
Listy lub kolejki w niewielkim stopniu odzwierciedlaj
hierarchiczn struktur obiektu.
Uniemoliwia to ich liniowa i jednowymiarowa budowa.
Pokonanie tych ogranicze umoliwiaj struktury
zwane drzewami.
Drzewo skada si z wierzchokw (wzw), od ktrych odchodz krawdzie (gazie), zakoczone na dole limi.
Korze wze, ktry nie ma ojca (poprzednika), moe natomiast mie synw (nastpnikw).
Licie lub wzy kocowe wzy, ktre nie maj nastpnikw (synw).
Syn lub dzieci wzy znajdujce si bezporednio pod danym wzem.
Krawd linia, ktra czy dwa wierzchoki.
cieka jednoznacznie wyznaczony cig krawdzi, prowadzcy z korzenia do kadego wierzchoka.
Dugo cieki liczba krawdzi na ciece.
Poziom wza dugo cieki od korzenia do wza
Wysoko drzewa najwikszy poziom wzw w drzewie.
Wasno drzewa: dwa dowolne wzy moe czy dokadnie jedna cieka.
Drzewo uniwersalne ukorzenione, majce dowoln liczb synw, ulokowanych w dowolny sposb.
Drzewo uporzdkowane drzewo ukorzenione, w ktrym okrelona jest kolejno nastpnikw
kadego z wzw. Jeeli wze ma k nastpnikw to s to kolejno ponumerowane 1,2, .
Do kadego wza drzewa uporzdkowanego docza si jego synw w okrelonej kolejnoci.
Drzewo dokadnie wywaone jeeli dla kadego wza, liczba wzw w lewym i prawym
poddrzewie rni si co najwyej o 1.
Mdrzewo: kady wze posiada okrelon liczb
potomkw ( m), uporzdkowanych w
okrelony sposb.
Pene drzewo rzdu m wszystkie licie maj t sam
gboko a wszystkie wzy wewntrzne maj stopie m.
Liczba wzw wewntrznych o wysokoci h wynosi W w(m, h) =
mh 1
m 1
W w(3, 2) = 4
Niech drzewo ma wicej ni 2 nastpniki. Dla tego samego wza numerowane s one rnymi
liczbami cakowitymi. W drzewie brakuje itego nastpnika, jeeli nie ma nastpnika o etykiecie i.
Drzewo uporzdkowane moe by reprezentowane z wykorzystaniem list, zwizanych z wzami.
Lewe cze wza wskazuje na pierwszy wze listy jego synw (syna pierwszego z lewej).
Prawe cza wzw poprzez listy wskazuj na braci znajdujcych si po prawej stronie.
71
72
13
15
16
25
17
15
SearchTreeIter(wx, k)
12 20 35 1
if (left[node])
Enq(left[node], Qt);
if (right[node])
Enq(right[node], Qt);
}
}
Gwarantuje to, e synowie odwiedzeni zostan dopiero po przejciu wszystkich wzw z poziomu r.
W algorytmie Qt jest kolejk, przechowujc w polu danych adresy struktury typu drzewo.
Przechodzenie w gb (depth-first traversal) w celu odwiedzenia wszystkich wzw
inOrder LvR wze wizytowany jest po przejciu lewego poddrzewa a przed wejciem
do prawego.
preOrder vLR
wze wizytowany jest przed odwiedzeniem w obu poddrzewach.
postOrder LRv wze wizytowany jest po odwiedzeniu w obu poddrzewach.
Due znaczenie ma metoda InOrder, ktrej wywoanie InOrder(rootBST) wypisze
wszystkie elementy drzewa BST w porzdku rosncym.
73
InOrder
15 7
(1)
R9.12a
L-v-R
13 5
(1)
2
(1)
1
(1)
Disp(5)
10
(2)
17 8
25 10
14 6
11 3
9 20
40 12
35 11
12 4
InOrder(node)
PreOrder(node)
PostOrder(node)
(1)
(2)
74
MinimumTree(wx)
while left[wx] NULL do
wx left[wx]
return wx
Czas wykonania funkcji jest rzdu O(h), poniewa przegld nastpuje tylko w d drzewa.
Wstawianie
node key = a
Wstawianie elementu a polega na przekazaniu do stosownej
=
nw
node left = NULL
funkcji rekordu z nowym wzem nw o strukturze obok.
Ins(14) 15
10
3
5
10
25
13
20
14
R9.13
Ins(17) 15
35
30
25
13
20
17
35
30
Jeeli wze staje si prawym synem innego wza, to dziedziczy nastpnik po swoim nowym ojcu.
Jeeli wze staje si lewym synem innego wza, to jego nowy ojciec staje si jego nastpnikiem.
(a) Jeeli wza w ma prawe poddrzewo dla to nastpnikiem jest najbardziej na lewo
pooony wze w prawym poddrzewie (przykad nastpnik wza 15).
Nastpnik wza bez prawego syna znajduje si gdzie nad nim.
(b) Jeeli wze w nie ma prawego poddrzewa to nastpnikiem jest:
-najniszy przodek , ktrego lewy syn jest rwnie przodkiem wza w,
// (a)
InsertTree(nw, BST)
// y, x wskaniki robocze
x root[BST]
while x NULL do
// wyszukiwanie
yx
if key[nw] < key[x] then x left[x] else x right[x]
y NULL
W ptli while wskaniki x i y przesuwane s w d drzewa w lewo lub w prawo a do chwili gdy
napotkaj warto NULL.
Jest to miejsce, w ktre naley wstawi wskanik na nowy wze nw
75
Usuwanie
Moliwe s 3 przypadki usuwania wza uw
(1) jeeli wze uw nie ma syna to zostaje usunity i u jego ojca[uw] wstawiany jest wskanik
do uw rwny NULL,
(2) jeeli wze uw ma jednego syna to uw zostaje wycity przez uaktualnienie wskanikw
midzy jego ojcem a synem.
(3) jeeli uw ma dwch synw, to wyizolowany zostaje wze y, bdcy nastpnikiem uw,
nastpnie wze y zastpuje wze uw
Drzewo zrwnowaone: rnica wysokoci obu poddrzew kadego wza wynosi 0 lub 1.
Drzewo doskonale zrwnowaone:
0
drzewo zrwnowaone, w ktrym wszystkie licie znajduj si
+1
0
na jednym lub dwch poziomach.
9.3.1. Tablicowa metodyka rwnowaenia drzewa
Izolowany jest wze y = 70, bdcy nastpnikiem usuwanego wza 61.
Delete(uw, BST)
el
(3
5)
// kopiowanie wszystkich pl y
76
77
78
Operacja rotacji
Podstawow operacj przywracajc rwnowag w drzewach BST jest rotacja.
Rotacja polega na obrocie" wok krawdzi midzy wzami (O, S) (S, O).
Praw rotacj na wle O mona wykona tylko wtedy, gdy istnieje jego lewy syn S.
Lew rotacj (b) na wle S mona wykona tylko wtedy, gdy istnieje jego prawy syn O.
Operacja RotateRight() zamienia konfiguracj wzw przez zmian wartoci wskanikw.
Dziadek
RightRotate(D, O, S)
Ojciec
(a
a)
(b
b)
S
O
Lp2
Pp1
S Syn(wnuk)
Lp2
Pp2
Pp2 Pp1
Prawa Rotacja Syna wzgldem Ojca
wrk
10
wrk
20
15
wrk
20
30
25
30
40
wrk
30
wrk
25
23
28
(a
a)
23
40
28
25
23
40
28
(b
b)
23
28
20
10
20
23
(c
c)
40
28
25
wrk
(d
d)
40
15
10
25
28
30
40
Zauwamy, e m = 2
20
15
20
20
15
23
25
25
28
28
30
30
30
(b
b)
40
(e
e)
40
n=9
m=7
1-szy przebieg
30
25
15
23
20
30
28
(ff)
40
10
40
23
15
3-ci przebieg
2-gi przebieg
lg( n+1)
1 2
1 = n
lg( m ) 1
k =1
m
n+
2k
40
(c
c)
25
10
28
23
23
28
(e
e) 40
20
30
28
15
25
25
15
10
23
20
23
23
30
10
(a
a)
25
30
10
m = m/2;
LeftRotate(wza wzgldem jego Ojca);
(d
d)
25
while (m > 1)
15
20
20
20
Pp2
Lp1
Pp2 Pp2
Lewa Rotacja Syna wzgldem Ojca
10
15
15
15
15
floor(lg(n+1))
5
10
10
10
10
Przeksztacanie(Root, n)
lg(n+1)
m=2
1
for 1 = 1 to n - m step 2 do LeftRotate;
Prostowanie(Root)
wrk = Root;
while (wrk NULL)
if wrk "ma lewego syna" RightRotate (tego syna wzgldem wrk);
else wrk nastpny wze, ktry by prawym synem wrk;
Uwaga: wykonanie Rotacji wymaga zachowania ojca wza wrk.
lg( m ) 1
k =1
1
n
2k
dla duzych n
30
28
(g
g)
40
79
drzewo AVL
-1
-1
18
+1
0
11
19
14
0
12
20
17
S 9 +2
6
7
18
15
19
22
16 20
12
18 0
18 +1
7
LewaRotacja (18)
wzgldem Ojca (9)
22
19 +1
15
16 20
12
19 +1
15
22
7 12
20
22
16
44
44
S PrawaRotacja (15)
18
6
2
15
12
11
LewaRotacja (15)
wzgldem Ojca (9)
6
19
16 20
22
15
7
12
11
18
16
15 0
18 +1
9
6
19
20
12
7
11
16
19
20
inicjalizacja drzewa
testowanie czy drzewo puste
nowy wze drzewa
zwraca klucz wza drzewa
wstawia klucz do wza drzewa
zwraca lewe poddrzewo
zwraca prawe poddrzewo
wstawia lewe poddrzewo do nowego
wstawia prawe poddrzewo do nowego
22
22
struct TreeNode {
TreeNode *left;
typData
Data;
TreeNode *right;
};
typedef TreeNode *Tree;
Boolean emptyTree(Tree T)
{ // return TRUE if empty,
return (T == NULL);
}
TreeNode *createTreeNode(typData e)
{
// return new tnode containing typData e
TreeNode *node;
node = new(TreeNode);
if (node == NULL) {
puts("Error in new"); exit(1);
}
node->left = NULL;
node->right = NULL;
node->data = e;
return node;
}
Element getData(Tree T)
{ // return data contained at current Tree node
if (emptyTree(T)) {
puts("empty Tree "); exit(1);
}
return T->data;
}
+2
newTree()
emptyTree()
createTreeNode()
getData()
setData()
getLeft()
getRight()
setLeft()
setRight()
Tree newTree(Tree T)
{ // return new empty Tree T
return (T = NULL);
}
-1
+1 9
80
Tree getLeft(Tree T)
{
//return left child Tree of Tree T
if (emptyTree(t)) {
puts("empty Tree "); exit(1);
}
return T->left;
}
Tree getRight(Tree T)
{
// return reft child Tree of Tree T
if (emptyTree(t)) { puts("empty Tree ");exit(1); }
return T->right;
}
Tree setLeft(Tree newT, Tree T)
{ // set left child Tree of Tree T to Tree newT
T->left = newT;
return T;
}
Tree setRight(Tree newT, Tree T)
{ // set right child Tree of Tree T to Tree newT
T->right = newT;
return T;
}
81
82
ANEX 9.2.
...................
int main()
// P8.4a
Tree
{
Tree root = NULL;
int liczba;
puts("Wprowadz wartosci:");
while(scanf("%d",&liczba))
root = InsertTree(liczba, root);
puts("breadthFirst"); breadthFirst(root);
puts("InOrder");
InOrder(root);
void BreadthFirst(Tree T)
{
typeQueue Qt; // zmienna robocza-kolejka
InitQ(&Qt);
if (T) {
Enq(T, &Qt);
while (!EmptyQ(Qt)) {
T = Deq(&Qt);
NULL
printf("%3d",T->data);
if (T->left) Enq(T->left, &Qt);
if (T->right) Enq(T->right, &Qt);
}
} puts("");
}
void InOrder(Tree t)
{
if (! emptyTree(t)) {
InOrder(getLeft(t));
printf("%3d", getData(t));
InOrder(getRight(t));
}
}
NULL
<102>
<104>
NULL
1
1
1
1
1
4
<102> <102> <102> <102> <102>
<104>
4
4
4
4
4
4
4
4
4
20
<102> <102> <102> <102> <102> <102> <102> <102> <102>
<104>
15
15
15
15
15
15
15
15
15
15
15
15
<adr> <adr> <adr> <adr> <adr> <adr> <adr> <adr> <adr> <adr> <adr> <adr>
a
b
c
d
e
f
g
h
i
j
k
l
NULL
NULL
<102>
<104>
16
16
16
16
<102> <102> <102> <102>
20
20
20
20
<102> <102> <102> <102>
15
15
15
15
<adr> <adr> <adr> <adr>
m
n
o
p
L-v-R
InOrder
3 15
2 4
1 1
5 20
4 16
6 25
InOrder(typeTree node)
{
if (node) {
InOrder(node->left); <101>
Disp(node);
<102>
InOrder(node->right); <103>
}
<104>
}
(a) Pierwsze woanie wkada na stos adres powrotu do InOrder() <adr> oraz warto key=15.
Drzewo nie jest puste, nastpuje realizacja instrukcji if.
Funkcja InOrder() wywoywana jest dla node.key=4.
(b) Na stos trafia adres powrotu <102> oraz warto key=4.
Wze nie jest pusty, zatem InOrder() wywoywana jest dla lewego syna node.key=1.
(c) Na stos trafia adres powrotu <102> oraz warto zmiennej key=1.
InOrder() jest wywoywana dla lewego syna node.key=1.
(d) Na stos trafia adres<102> oraz warto NULL.
NULL natychmiast koczy wywoanie InOrder().
Automatycznie zdejmowany jest ze stosu rekord wywoania.
(e) System odtwarza warto zmiennej node.key=1.
Wykonuje instrukcj pod adresem<102>, co odpowiada wydrukowaniu wartoci 1.
Warto node.key=1 i adres <102> pozostaj na stosie, gdy przetwarzanie wza nie zostao
zakoczone.
(f) Rozpoczyna si wykonywanie instrukcji po adresem <103>, co odpowiada wywoaniu funkcji
InOrder() dla prawego syna node.key=1.
Zatem na stos trafia adres <104> oraz bieca warto node=NULL.
NULL koczy dziaanie InOrder(), usuwajc rekord wywoania.
(g) System odtwarza poprzedni warto zmiennej node.key=1 i wykonuje instrukcj <104>.
Jest to koniec wywoania InOrder().
(h) System odtwarza ze stosu warto node.key=4.
Wykonuje instrukcj <102>, co daje wydruk wartoci 4.
Nastpnie wywouje InOrder() dla prawego syna wza warto NULL.
83
Kopiec tworzony jest na bazie penego drzewa binarnego, std jego wysoko jest rzdu O(lg(n)).
Kopiec moe reprezentowa tablica K[1n], w ktrej kady wze drzewa
odpowiada elementowi tablicy o parametrach:
K[i] K[ 2i ] oraz K[i] K[ 2i + 1], gdzie 0 i < n/2.
Korzeniem drzewa jest K[1].
dla 60
dla 58
dla 54
K[i] 60 58 54 52 51 53 47 46 48 45
1
2
3
4
5
6
7
8
9 10
i
Atrybutami tablicy K reprezentujcej kopiec s:
n = length(K)
dugo tablicy
nK
liczba elementw kopca w tablicy K.
aden element tablicy K[1n], wystpujcy po indeksie nK nie jest elementem kopca K.
Dla itego wza kopca mona obliczy indeksy ojca oraz lewego i prawego syna.
Ojciec(i)
Lewy(i)
return i/2
return 2i
i/2
2i + 1
Prawy(i)
return 2i + 1
2i + 2
2i
2i+1
InsertHeap(w, K)
nK nK + 1 // miejsce na wstawienie
przesuwaj klucz w w gr po ciece, tak dugo K[nK] w
dopki nie dojdzie do korzenia lub nie natrafi na i nK
ojca o wartoci nie mniejszej od w,
while i > 1 and w > K[Ojciec(i)] do
przechodzc przez kolejne wzy, zamie nowy
Swap(K[i] ,K[Ojciec(i)])
wze z jego ojcem.
i Ojciec(i)
84
Po operacji na kopcu czsto w jakim wle warto klucza ojca jest mniejsza od
kluczy jego synw.
Drzewo binarne utracio wasno kopca.
Funkcja HeapDown przywraca j, powodujc spynicie klucza K[i] w d kopca,
tak aby poddrzewo zaczepione w wle i stao si kopcem.
HeapDown(i, K)
l Left(i);
r Right(i)
if l nk and K[l] > K[i] then Max l else Max i
if r nk and K[r] > K[Max] then Max r
if Max i then Swap(K[i], K[Max] )
HeapDown(Max, K)
W kadym kroku dziaania HeapDown() poszukiwana jest warto maksymalna pord
dzieci wza i.
Indeks wartoci maksymalnej pamitany jest w zmiennej Max.
Jeeli K[i] jest najwiksze to jest OK.
Jeeli nie to zamienia si K[i] z K[Max].
Teraz bada si czy poddrzewo doczepione do K[Max] spenia wasno kopca.
Jeeli nie to funkcja HeapDown() wykonywana jest rekurencyjnie na tym poddrzewie.
85
Budowanie kopca
Tablic K[1n] niebdc kopcem mona przeksztaci BuildH(K)
n length(K);
w kopiec, wykorzystujc funkcj HeapDown.
nK n
Podstaw dziaania jest fakt, e wszystkie elementy
for
i n/2 downto 1 do
podtablicy K[(n/2 + 1) n] s limi drzewa, a tym
HeapDown(i, K)
samym 1elementowym kopcem.
Funkcja BuildH, przechodzc przez wzy wewntrzne, wywouje funkcj HeapDown.
Zachodzi wstpujce (bottom-up) przeksztacenie tablicy K.
21 22 23 24 25 26 27 28 29 33
1
9 10
86
8
5
6
9
10
9
4
9
2 10
8
5
nK=nK-1
9
2 10
Swap(8,1)
(c)
1 8
7
(c1)
1 1 1
HeapDown(c1)
3
(b)
1 9
3
6
9 10 nK=nK-1
Wywoanie HeapDown realizowane jest w czasie O(lg(n)). Liczba wywoa moe sign O(n).
Pesymistyczny czas budowania kopca mona szacowa na O(nlg(n)).
(d)
7
8 9
10
10
10
Swap(7,4) (d1)
HeapDown(d1)
1 4
1
9
3
8
1
9
10
5
5
nK=nK-1
(e)
1 6
7
8
3
nK=nK-1
1
9
10
10
87
Praca wsadowa
Kolejka zawiera zdarzenia do symulacji, ktrych kluczem jest czas ich wystpowania.
Zdarzenia symulowane s w kolejnoci ich zajcia.
Wygodnie jest operowa funkcj DelMin, ktra wykorzystywana
jest przez stosowny program do wybierania zdarze do symulacji, w
poczeniu z funkcj Min.
Mamy wic do czynienia z kolejk o odwrconym liniowym porzdku.
Jeeli zachodz nowe zdarzenia to s dodawane do kolejki przez
funkcj Insert.
88
89
10. BDRZEWA
Realizuj obsug duych struktur danych, dziaajcych na pamiciach masowych (dyskowych).
B-drzewo root[B] 60
rzdu m=5
W kadym wle naley dokona wyboru poddrzewa, w ktrym bdzie kontynuowane poszukiwanie.
jeeli nx jest aktualn liczb kluczy w wle x,
to nx + 1 oznacza liczb moliwych poddrzew w wle.
CreateB(B)
x InitNode()
leaf(x) TRUE
n[x] 0
Funkcja CreateB dziaa w czasie O(1) zarwno dla operacji DiskWrite(x)
dyskowych jak i CPU.
root[B] x
key>60
7080
6163
Metodyka poszukiwania klucza w Bdrzewie jest podobna jak w BST (z definicji Bdrzewa).
7274
90
Podobny proces moe dotyczy wza wewntrznego, zwikszajc liczb wzw o jeden.
91
10.4.1c Usunicie klucza w liciu powoduje niedobr, a liczba kluczy u jego Braci jest nie
wiksza ni poowa moliwoci to:
li i Brat s sklejane (
Brat ulega zniszczeniu) .
rozdzielajcy je klucz u Ojca jest przesuwany do licia.
Jeeli teraz u Ojca wystpi niedobr to jest on taktowany jak li i czynno
powtarza si a do usunicie niedoboru poprzez scalanie.
Nastpuje dodanie dwch nowych wzw do Bdrzewa oraz wzrost jego wysokoci.
92
keyj+1[x] keyj[x]
Usuwanie wymaga O(h) operacji dyskowych oraz O(ph) = O(plogp(N)) czasu procesora.
Bdrzewo jest wypenione, co najmniej w 50%, za prawie nigdy w 100% pojemnoci.
redni poziom wypenienia przy losowych wstawieniach i usuniciach wynosi ok. 70%.
93
Czas dziaania procedury SplitB jest rzdu O(p) i determinowany jest dziaaniem 1-szej ptli for.
89
90
11. GRAFY
Graf definiuj wierzchoki V (vertices) i krawdzie E (edges), przy czym cieka midzy
dwoma wierzchokami nie musi by jednoznaczna, za sposb rysowania jest nieistotny.
Graf G = (V, E) skada si ze skoczonego zbioru wierzchokw oraz zbioru krawdzi.
Kad krawd E stanowi para wierzchokw, zatem E jest zbiorem par (u, v),
za (u),(v) stanowi elementy zbioru V.
Do oznaczania krawdzi E mona uywa zapisu (u, v) lub <u, v>.
Mwimy, e wierzchoek v jest ssiedni (adjacent) do wierzchoka u.
Krawd (u, v) jest incydentna (incident) wzgldem wierzchokw u i v, czyli
krawd czca dwa wierzchoki.
R11.2
Cykl w grafie skierowanym tworzy cieka, jeeli v0 = vk (a)
cykle
(b)
oraz graf zawiera co najmniej jedn krawd.
(c)
1
2
2
1
1
Cykl jest prosty, jeeli v1, v2,, vk s rne.
Ptla stanowi cykl o dugoci 1.
Cykl w grafie nieskierowanym tworzy cieka jeeli v0 = vk
4
3
4
3
4
oraz v1, v2,, vk s rne i k 2.
Spjny graf skierowany: kade dwa wierzchoki osigalne s jeden z drugiego (R11.2b).
Graf acykliczny: graf nie zawierajcy cykli.
Acykliczny graf skierowany(directed acyclic graph -DAG)-nie posiada cyklu skierowanego.
Spjny graf nieskierowany: kada para wierzchokw poczona jest ciek (R11.3a).
Kady wierzchoek dostpny jest z innego.
Graf rzadki: dla ktrego |V| >> |E| niewielka liczba krawdzi
Graf gsty: gdy |E| jest bliskie |V|2
wikszo par wzw poczona jest krawdzi
Graf regularny: kady wierzchoek ma taki sam stopie.
Graf planarny: tak przedstawiony na paszczynie, e adne dwie krawdzie nie przecinaj si.
k-graf: graf w ktrym stopie wierzchoka nie moe by wikszy ni k.
Niezmiennik grafu: cig liczb, zaleny tylko od struktury grafu a nie od sposobu jego etykietowania (np. liczba wierzchokw, liczba krawdzi).
Liczba chromatyczna grafu: najmniejsza liczba kolorw potrzebna do kolorowania wierzchokw
grafu tak, by adne dwa przylege wierzchoki nie byy tego samego koloru.
cieka dugoci k z wierzchoka u do wierzchoka ur jest cigiem wierzchokw <v0, v1,, vk>
takich, e u = v0, ur = vk oraz (vi-1, vi) E dla i = 1, 2,, k.
Dugo cieki to liczba krawdzi cieki.
r
(b)
7
3
6
7
Cykl Hamiltona
(a)
R11.
3
91
0
0
0
0
0
1
1
0
0
0
0
0
0
1
0
0
0
0
1
0
1
0
0
0
0
0
0
1
0
1
0
0
0
0
1
1
0
1
2
3
4
5
92
0
1
0
1
0
0
1
0
1
1
1
0
0
1
0
0
0
0
1
1
0
0
0
1
0
1
0
0
0
1
0
0
0
1
1
0
Dla grafu skierowanego G1 suma dugoci wszystkich list ssiedztwa wynosi |E|, gdy
krawd (u, v) jest reprezentowana przez wystpienia v w tablicy zawierajcej |N| list.
0: 1,3
1: 0,2,3,4
2: 1
3: 0,1,5
4: 1,5
5: 3,4
0
0
-1
0
0
0
1
-1
2
wygenerowany graf jest weryfikowany, czy spenia zaoenia wybranego modelu, grafy
ktre ich nie speniaj s odrzucane.
grafy speniajce zaoenia modelu sprawdza si dodatkowo czy s rne, tzn. czy nie
ma wrd nich grafw izomorficznych.
Graf izomorficzny: Dwa grafy G1(V1, E1) i G2(V2, E2) s izomorficzne, jeeli mona przenumerowa wierzchoki grafu G1 tak, aby stay si wierzchokami grafu G2,
zachowujc odpowiednie krawdzie w G1 i G2.
Przykad implementacji tablicowej
#define MAXN 10
struct Graph {
int TabG[MAXN][MAXN];
};
Graph NewG(Graph g);
Graph JoinG(Graph g, int a, int b);
Graph RemoveG(Graph g, int a, int b);
int AdjacentG(Graph g, int a, int b);
Graph newG(Graph g)
{
// Initialise and return graph g
int x, y;
for(x=0; x < MAXN; x++)
for(y=0; y < MAXN; y++)
g.TabG[x][y] = 0;
return g;
}
93
Niech Graf bdzie reprezentowany przez listy ssiedztwa pamitane w tablicy G[u].
Tablica G umoliwia dostp do wszystkich
wierzchokw ssiadujcych z u.
Na pocztku wszystkie wierzchoki s biae,
potem zmieniaj kolor na szary lub czarny.
Szary: wierzchoek odwiedzony, ktrego lista ssiedztwa
nie zostaa w caoci przejrzana.
W kolejce Q, pamitane s kolorowane na szaro,
odwiedzone wierzchoki.
Czarny: wierzchoek, ktrego lista ssiedztwa zostaa w caoci odwiedzona.
Jeeli wierzchoek u jest czarny, to wierzchoek v jest albo szary albo czarny.
Wierzchoki szare mog mie biaych ssiadw, tworz granic midzy odwiedzanymi i nieodwiedzanymi.
W kolejce umieszcza si rdo S.
Z kolejki pobiera si wierzchoek S, odwiedza si go,
i umieszcza w kolejce wierzchoki znajdujce si na jego licie ssiedztwa czyli: e, a.
Z kolejki pobiera si wierzchoek e, odwiedza si go, i umieszcza w kolejce wierzchoki z jego
listy ssiedztwa czyli: b, f (wierzchoek s nie jest pobierany, gdy by ju odwiedzony).
Z kolejki pobiera si wierzchoek a, odwiedza si go, i umieszcza w kolejce wierzchoki z jego
listy ssiedztwa czyli: d.
itd...
94
while (!Empty(Q)) do
u head[Q] // pobieranie
for v G[u] do
if C[v] = BIAY then
C[v] Szary
D[v]D[u] + 1
P[v] u
Enq(Q, v)
Deq(Q); C[u] Czarny
Q=0
R11.9
u
a: d,s
s: e,a
b: c,f,e
c: b,g
d: a
e: b,f,s
f: g,b,e
g: f,c
95
u1
u4 (3)
u1
(1)
u5
u2
u2 (1)
u6
u3 (2)
(2)
u3
u5
(3)
u4
u6
Lista
u1: u2,u3,u4,u5,u6
u2: u3,u4,u1
u3: u1,u2
u4: u2,u1
u5: u6,u1
u6: u5,u1
DFS1(G)
Tree NULL
for u V do u nowy
for u V do
if u = nowy then
VisitDFS1(u)
VisitDFS1(u)
u stary
for v Listy[u] do
if v = nowy then
Tree (u, v)
VisitDFS1(v)
Podgraf poprzednikw nazywany jest lasem przeszukiwa w gb, zoonym z jednego lub
kilku drzew przeszukiwa w gb, gdy przeszukiwanie moe by wykonywane z kilku rde
Dla grafu skierowanego na wierzchoku u wybieramy tylko krawdzie skierowane od u. Po wyczerpaniu tych krawdzi,
powracamy do u mimo, e mog istnie inne krawdzie skierowane do v, ktre nie s jeszcze przeszukane.
96
VisitDFS2(u)
C[u] Szary
D[u] krok krok + 1
for v Listy[u] do // badanie z listy ssiadw u
if C[v] = Biay then P[v] u
VisitDFS2(v)
C[u] Czarny
F[u] krok krok + 1
P[v] pamita poprzednika wierzchoka nie odwiedzonego v, nalecego do listy ssiedztwa odwiedzonego u.
Podgraf poprzednikw: GP = ( N, (T(v), v) )
G(u) Lista
a: b, d
b: c
c: d
d: b
e: f, c
f: f
b c d f
v
P(v) a b c e
u=a u=b u=c
u1=e
97
98
Waga cieki p = < v0, v1, ..., vk > -suma wag tworzcych j krawdzi: w( p ) = w( vi 1 , vi )
i =1
Najkrtsza cieka z wierzchoka u do v: -kada cieka p z u do v, dla ktrej w(p) = (u, v).
Warianty problemu najkrtszej cieki dla grafu spjnego:
1. Dla dwch dowolnych wzw A i B znale najkrtsza ciek od A do B.
2. Dla wza A znale najkrtsze cieki do pozostaych wzw w grafie.
3. Pomidzy kad par wzw w grafie znale najkrtsz ciek.
w
1
2
2
4
4
7
7
8
8
9
9
10
11
14
Krawd
(w6, w7)
(w2, w9)
(w5, w6)
(w2, w5)
(w1, w8)
(w2, w3)
(w7, w9)
(w7, w8)
(w1, w2)
(w3, w4)
(w6, w9)
(w4, w5)
(w1, w7)
(w3, w5)
Podzbiory
{w1}, {w2}, {w3}, {w4}, {w5},{w6, w7}, {w8}, {w9}
{w1}, {w2, w9}, {w3}, {w4}, {w5},{w6, w7}, {w8}
{w1}, {w2, w9}, {w3}, {w4},{w5, w6, w7}, {w8}
{w1}, {w2, w5, w6, w7w9}, {w3}, {w4}, {w8},
{w1, w8}, {w2, w5, w6, w7,w9}, {w3}, {w4}
{w1, w8}, {w2, w3, w5, w6, w7, w9}, {w4}
krawd istnieje - nie jest wstawiana
F = 0;
// inicjalizacja pustego zbioru krawdzi
Utwrz V rozcznych podzbiorw wzw
Sortuj(krawdzie E w porzdku niemalejcym)
while (liczba krawdzi w F < V - 1)
Wybierz kolejn krawd
if (krawd czy 2 wzy ze zbiorw rozcznych)
Scal(podzbiory)
Dodaj(krawd do F)
F = 0;
Wstaw krawdzie do Kolejki priorytetowej
while (liczba krawdzi w F < V - 1)
Pobierz krawd z Kolejki
if (krawd czy 2 wzy ze zbiorw rozcznych)
Scal(podzbiory)
Dodaj(krawd do F)
99
Najpierw odwiedzamy wze najbliszy wza S, potem 2-gi w kolejnoci i tak dalej (v1, v2,...).
Wze x1 najbliszy S musi by ssiadem. Wze v2 (2-gi) musi by ssiadem S lub v1.
Kiedy wze zosta ju odwiedzony, znamy dugo najkrtszej cieki od niego do S.
Obwdka -zbir nie odwiedzonych krawdzi (wzw), ssiadujcych z wzami odwiedzonymi.
Nastpny do odwiedzenia wybieramy wze z obwdki, ktrego cieka do S jest najkrtsza.
ADHEIJCBGF
100
// Graf1DFS
0
1
2
3
4
5
6
7
8
9
A: DBHE
B: ACE
C: BJG
D: HA
E: AHIFB
F: E
G: CJ
H: DAE
I: HEJ
J: ICG
101
102
Rozwamy plik o dugoci 100000 znakw, zawierajcy wycznie treci skadajce si z alfabetu
= {a, b, c, d, e, f}. Czstotliwo wystpie poszczeglnych znakw (w tys.) i przyjte sowa
kodowe dla kadego znaku zawiera tabela.
znak Liczba kod
Kod o zmiennej dugoci gwarantuje, e czciej wystpujce znaki bd
znakw
kodowane za pomoc krtszych sw kodowych
a
45
0
ni znaki o mniejszej czstoci wystpowania.
b
13
101
Cel kompresji: zmniejszy rozmiar bloku danych bez utraty jego zawartoci.
Metoda sownikowa.
Kolejno analizowane cigi symboli koduje si za pomoc pojedynczych odwoa do sownika,
w ktrym przechowywane s czsto powtarzajce si sekwencje znakw.
W strumieniowych metodach sownikowych budowane s dwa sowniki:
-jeden wykorzystywany jest dla kodowania czsto wystpujcych wzorcw,
-drugi dla pozostaych. Zysk metody zaley od tego, jak duo cigw kodowych
zostanie rozpoznanych jako czsto wystpujce.
BACKABWACKACACKA = 16 znakw 8 bitw = 128 bitw
Rozpoczyna si od zebrania statystyk wystpowania poszczeglnych znak Liczba kod
znakw
elementw w zbiorze. Przypisuje si kody o zmiennej liczbie bitw
6
0
(krtszy, gdy znak wystpuje czciej) poszczeglnym elementom na A
podstawie czstoci ich wystpowania w zbiorze.
4
11
C
Budowane jest drzewo binarne, w ktrym elementy zbioru K
3
100
umieszczane s w liciach. Kod kadego znaku jest wynikiem B
2
1011
przejcia od korzenia drzewa do odpowiedniego licia.
Algorytm Huffmana
1010
Komunikat zawiera tym wicej informacji, im mniejsze jest prawdopodobiestwo jego wystpienia
jest podstawowym zaoeniem teorii informacji, ktr stworzy Claude Shannon w 1948-1949.
Niech bdzie skoczonym zbiorem wszystkich moliwych wysanych komunikatw.
Odbiorca nie wie, ktry z moliwych komunikatw ze zbioru = {k1, k2, ... , kn} otrzyma.
Komunikaty o zdarzeniu pewnym (ktrych zajcia jest pewien) nie przeka odbiorcy adnej
informacji, gdy nie zmieni stanu jego wiedzy.
Komunikat, ktrego prawdopodobiestwo wystpienia wynosi p I ( p ) = log ( p )
2
zawiera I(p) jednostek informacji.
n
Entropia H rda informacji: rednia (waona) ilo informacji w
H ( p) = pk log2 ( pk )
komunikatach ze rda, ktre nadaje n rnych komunik =1
katw z prawdopodobiestwem wystpienia pk (k=1,2,...,n).
Sowo kodowe: kod komunikatu. Dugo sowa kodowego: liczba jego elementw (znakw).
N1
I p1 = 1.42
6
16
1
p2
N2
I p2 = 2
4
16
2
p3
N3
I p3 = 2.42
3
16
3
p4
N4
I p4 = 3
2
16
4
p5
N5
I p5 = 4
Lave = pk N k
k =1
1
16
4
H ( p , n ) = 2.11
L ave ( p , n , N ) = 2.19
p1
0.45
p2
0.13
p3
0.12
p4
0.16
p5
0.09
p6
0.05
N1
N2
N3
N4
N5
N6
I p1 = 1.15
I p2 = 2.94
Entropia: H ( p , n ) = 2.22
I p3 = 3.06
I p4 = 2.64
I p5 = 3.47
I p6 = 4.32
c
d
e
f
12
16
9
5
100
111
1101
1100
Po dokonaniu kompresji plik o dugoci 100000 znakw czyli 800 000 bitw zajmowa bdzie
(451 + (13 + 12 + 16)3 + (9 + 5 ) 4) 1000 = 224 000 bitw.
Kod prefiksowy: kod kadego znaku nie jest prefiksem innego znaku.
Pozwala uzyska maksymalny stopie kompresji.
Kodowanie prefiksowe wymaga tylko skonkatenowania kodw kolejnych znakw w pliku.
Na przykad plik zawierajcy znaki abcdef reprezentowany bdzie za pomoc cigu bitw:
010110011111011100 = 010110011111011100, znak " oznacza operacj konkatenacji.
Dekodowanie dla kodw prefiksowych jest stosunkowo proste.
Pierwszy kod znaku jest wyznaczony jednoznacznie, bo aden kod nie jest prefiksem innego.
Naley: -wyznaczy pierwszy kod w pliku, -przetumaczy go na znak,
-usun z zakodowanego pliku;
-powtrzy procedur dekodujc dla reszty pliku.
Cig 1111101101010011011100 rozkada si jednoznacznie na
111-1101-101-0-100-1101-1100, co daje sowo debacef.
103
Huffman()
Utwrz
jednowzowe drzewo dla kadego symbolu.
Uporzdkuj
wszystkie drzewa ze wzgldu na czsto wystpie symboli.
while zostao wicej ni jedno drzewo
we dwa drzewa T1 i T2 o najmniejszych czstociach f1 i f2 (f1 f2) wystpowania
symboli i utwrz drzewo o synach T1 i T2 i czstoci w korzeniu rwnej f1 + f2
Oznacz kad krawd skierowan w lewo jako 0, a skierowan w prawo jako 1.
Utwrz kod dla kadego symbolu, przechodzc drzewo od korzenia do licia odpowiadajcego
temu symbolowi i czc napotykane zera i jedynki.
W korzeniu otrzymanego drzewa czsto wystpie wynosi 1.
Budowanie drzewa algorytm kodowania Huffmana
Algorytm buduje drzewo w sposb wstpujcy rozpoczynajc od zbioru lici ||.
Kolejny etap to || - 1 operacji scalania, prowadzcych do powstania jednego drzewa.
104
105
NP
106
NP
NP
- zu
pe
ne
Pokazanie, e jaki problem jest NP-zupeny, jest sygnalizacj moliwych trudnoci w jego rozwizaniu.