Notatki informatyka od wielomianów - Notatki - Informatyka - Część 2, Notatki'z Informatyka. Opole University
Kowal_86
Kowal_868 March 2013

Notatki informatyka od wielomianów - Notatki - Informatyka - Część 2, Notatki'z Informatyka. Opole University

PDF (786.6 KB)
10 strona
918Liczba odwiedzin
Opis
Notatki dotyczące tematów z dziedziny informatyki: informatyka od wielomianów;
20punkty
Punkty pobierania niezbędne do pobrania
tego dokumentu
Pobierz dokument
Podgląd3 strony / 10
To jest jedynie podgląd.
Zobacz i pobierz cały dokument.
To jest jedynie podgląd.
Zobacz i pobierz cały dokument.
To jest jedynie podgląd.
Zobacz i pobierz cały dokument.
To jest jedynie podgląd.
Zobacz i pobierz cały dokument.

Przykład: Pojedynczy przebieg – porównujemy parami od dołu: 1 2 44 44 44 44 44 44 12 55 55 55 55 55 12 44 12 12 12 12 12 55 55 42 42 42 18 18 18 18 94 94 18 42 42 42 42 18 18 94 94 94 94 94 67 67 67 67 67 67 67 Sortowanie bąbelkowe

Przykład: 0 1 2 3 4 5 6 7 44 06 06 06 06 06 06 06 55 44 12 12 12 12 12 12 12 55 44 18 18 18 18 18 42 12 55 44 42 42 42 42 94 42 18 55 44 44 44 44 18 94 42 42 55 55 55 55 06 18 94 67 67 67 67 67 67 67 67 94 94 94 94 94 Złożoność czasowa O(n2). Jeśli w danym przebiegu nie dokonano żadnej zamiany, to można kończyć. Czyli w

najlepszym przypadku (wartości posortowane na wejściu) mamy złożoność O(n).

Sortowanie szybkie (ang. quicksort)

⋆ Typowe podejście typu dziel i zwyciężaj.

⋆ Dążymy do podziału na dwie części takie, że każdy element pierwszej części jest nie większy niż każdy element drugiej części.

⋆ Wybieramy wartość graniczną (między dwiema częściami) i szukamy od obu stron wartości, które mogłyby być po drugiej stronie

◮ kiedy znajdziemy z obu stron kandydatów do przeniesienia na drugą stronę zamieniamy ich miejscami i kontynuujemy poszukiwania.

◮ miejsce, w którym poszukiwania z obu ston „spotkają się”, to miejsce podziału

⋆ Dokonujemy podziału na dwie części i każdą sortujemy tym samym algorytmem (rekurencja!).

⋆ Jeśli do posortowania jest tylko jeden element (lub zero), to nic nie trzeba robić.

Sortowanie szybkie

Przykład:

docsity.com

0 44 55 12 42 94 18 06 67

1 06 18 12 42 94 55 44 67

2 06 12 18 42 44 55 94 67

3 06 12 18 42 44 55 67 94

⋆ Średnia złożoność czasowa O(nlogn).

⋆ Złożoność w najgorszym przypadku O(n2) (gdy z jednej strony podziału zawsze zostaje tylko jeden element).

⋆ Wybór wartości podziału – różne warianty, różne efekty

◮ losowo – kiepsko, bo można w ogóle nie podzielić tablicy,

◮ środek zakresu kluczy z tablicy,

◮ pierwszy/ostatni/środkowy element z tablicy.

Sortowanie przez łączenie (scalanie, ang. mergesort ) Idea: Dzielimy tablicę na dwie części, sortujemy każdą z osobna a potem łączymy

dwie uporządkowane tablice w jedną.

Przykład: 0 44 55 12 42 94 18 06 67 1 44 55 12 42 94 18 06 67 2 44 55 12 42 94 18 06 67 3 44 55 12 42 94 18 06 67 4 44 55 12 42 18 94 06 67 5 12 42 44 55 06 18 67 94 6 06 12 18 42 44 55 67 94

Algorytm łączenia dwóch posortowanych tablic

A B 12 42 44 55 06 18 67 94 C 06 12 18 42 44 55 67 94 Idea: Zaczynamy od początku posortowanych tablic i przesuwamy się do końca

przepisując do docelowej tablicy dane o niższej (lub równej) wartości klucza.

Uwagi:

⋆ Żeby wyznaczyć wartość minimalną wystarczy sprawdzić minimalne (pierwsze) wartości łączonych tablic.

⋆ Po przepisaniu wartości z jednej z tablic następna wartość jako minimalna z pozostałych jest kandydatem do kolejnego miejsca.

docsity.com

Algorytm łączenia dwóch posortowanych tablic

A B 12 42 44 55 06 18 67 94 C 06 12 42 44 55 06 18 67 94 12 12 42 44 55 06 18 67 94 18 12 42 44 55 06 18 67 94 42 12 42 44 55 06 18 67 94 44 12 42 44 55 06 18 67 94 55 12 42 44 55 06 18 67 94 67 12 42 44 55 06 18 67 94 94 Sortowanie przez łączenie

Algorytm łączenia: Dane: posortowane tablice A i B o długościach dA i dB.

Wynik: Tablica C długości dA+dB.

iA ←1; iB ←1; iC ←1;

⋆ Tak długo jak iA 6 dA lub iB 6 dB:

◮ Jeśli iA 6 dA oraz (iB > dB lub A[iA] 6 B[iB]), to

C[iC]←A[iA]

iA iA+1,

w przeciwnym przypadku:

C[iC]←B[iB]

iB iB+1.

iC iC +1

Interpretacja symboli:

iA, iB, iC – indeksy elementów poszczególnych tablic, którym się aktualnie zajmujemy, A[iA],B[iB],C[iC] – owe elementy,

iA 6 dA – „nie skończylismy jeszcze z tablicą A”,

iB > dB – „skończylismy już z tablicą B”. Algorytm główny: SortujPrzezŁączenie(A) Dane: tablica A o długości dA.

Wynik: Tablica A posortowana.

B← kopia A;

⋆ wynik ← SortujPrzezŁączenie(A,1,dA,B);

Funkcja pomocnicza: SortujPrzezŁączenie(A, pocz, kon, B) Dane: tablice A i B identyczne w zakresie do posortowania (pocz, kon). Wynik: Tablica A posortowana.

docsity.com

⋆ Jeśli pocz = kon to koniec;

kon1←

j

pocz+kon 2

k

;

SortujPrzezŁączenie(B, pocz, kon1, A);

SortujPrzezŁączenie(B, kon1+1, kon, A);

Złożoność sortowania przez łączenie

Procedura łączenia:

⋆ Złożoność czasowa: O(n).

⋆ Złożoność pamięciowa: O(n). Nie w miejscu. Całość sortowania przez łączenie:

⋆ Złożoność czasowa: O(nlogn). Również w najgorszym przypadku.

⋆ Złożoność pamięciowa: O(n). Nie w miejscu.

Sortowanie przez zliczanie

⋆ Bardzo atrakcyjna złożoność O(n), ale metoda stosowalna tylko w specyficznych okolicznościach.

⋆ Tylko dla kluczy naturalnych (np. w przedziale 1, . . . ,n).

⋆ Nie w miejscu, choć można w miejscu, ale niestabilnie. Sortowanie stabilne to takie, które zachowuje porządek pomiędzy obiektami o tym

samym kluczu.

Przykład: Mamy tablicę danych o studentach posortowanych wg. nazwisk i chcemy posortować ją według przynależności do grup laboratoryjnych tak, by w każdej grupie

pozostał alfabetyczny porządek nazwisk.

Idea algorytmu:

⋆ Liczymy ile kolejnych kluczy występuje w tablicy – w ten sposób wyznaczamy, które części tablicy będą zajmowane przez dane o kolejnych wartościach kluczy.

⋆ Przepisujemy wartości po kolei, od końca, na odpowiednie miejsca w kopii tablicy.

Sortowanie przez zliczanie – formalnie

1 function SortujPrzezZliczanie(A : array[1..n] of Typ, k : integer) 2 : array[1..n] of Typ 3 var B : array[1..n] of Typ, { posortowana tablica } 4 C : array[1..k] of integer, { tablica pomocnicza } 5 i, j : integer; 6 begin 7 { zerujemy tablice˛ pomocnicza˛ (nie trzeba w pewnych je˛zykach) } 8 for i := 1 to k do 9 C[i] := 0;

docsity.com

10 { zliczamy wysta˛pienia } 11 for i := 1 to n do 12 begin 13 j := Klucz(A[i]); 14 C[j] := C[j] + 1; 15 end; 16 { liczymy indeksy ko´ncowe grup }

17 for i := 2 to k do

18 C[i] := C[i] + C[i−1];

19 { przepisujemy od ko´nca z tablicy A do tablicy B }

20 for i := n downto 1 do

21 begin

22 j := Klucz(A[i]);

23 B[C[j]] := A[i];

24 C[j] := C[j] − 1;

25 end;

26 SortujPrzezZliczanie := B;

27 end

Struktury danych

⋆ Algorytmy + struktury danych = programy.

⋆ Struktury statyczne mają stały rozmiar (zajmują stały obszar pamięci) przez cały czas pracy.

tablice – kolekcje elementów tego samego typu z dostępem swobodnym Deklaracja w Pascalu: C : array[1..n] of integer; Dostęp do elementów: C[i]

rekordy (struktury) – grupują nazwane elementy różnych typów Przykład w Pascalu:

1 type Osoba = record 2 Imie : string; 3 Nazwisko : string; 4 DataUr : Data; 5 RokUkonczenia : integer; 6 end; Korzystanie:

1 var ja : Osoba; 2 begin 3 ja.Imie := ’Krzysztof’; 4 ja.DataUr.Dzien := 7; 5 ... 6 end;

⋆ Struktury dynamiczne – zmienna „objętość” danych w trakcie pracy.

Pliki

Pliki lub strumienie (ang. file, stream) to struktury o dostępie sekwencyjnym a nie

swobodnym (jak np. tablice)

⋆ różnica jak pomiędzy zapisem na taśmach a zapisem na dyskach,

⋆ w danej chwili dostęp tylko do jednego miejsca,

docsity.com

⋆ zmiana bieżącego elementu z pierwszego na ostatni wymaga przejścia przez wszystkie elementy pliku.

Specyfika struktury danych narzuca odpowiednie rozwiązania algorytmiczne i

odwrotnie: dla sprawnego wykonania stosownych algorytmów należy dysponować

odpowiednimi strukturami danych.

Dynamiczne kolekcje

Przeróżne kolekcje na różne okoliczności - warto znać ich specyfikę, by trafnie

dobierać struktury danych do rozwiązywanych problemów.

Najważniejsze funkcje kolekcji: wstawianie (dodawanie), usuwanie, wyszukiwanie.

Kolekcje:

⋆ kolejki (FIFO - first-in-first-out)

⋆ stosy (LIFO - last-in-first-out)

⋆ listy

◮ jednokierunkowe i dwukierunkowe

◮ cykliczne

⋆ drzewa

◮ binarne i niebinarne (wielokierunkowe)

◮ zbalansowane (wyważone, AVT), dokładnie wyważone

◮ czerwono-czarne

⋆ grafy

Specyfika kolekcji, złożoności operacji

Dla sprawnej obsługi kolekcji, zwykle z każdym elementem pamiętamy wskaźnik do

następnego/poprzedniego elementu.

⋆ dodawanie i usuwanie w O(1),

⋆ wyszukiwanie w zasadzie nie stosowane, ale jeśli trzeba to O(n).

Operacje na kolejkach i stosach

Wstawienie obiektu o do kolejki

1 nowy.obiekt := o;

2 nowy.nast ˛epny := nil;

3 koniec.nast ˛epny := nowy;

4 koniec := nowy;

Odłożenie obiektu o na stos

1 nowy.obiekt := o;

2 nowy.nast ˛epny := wierzchołek;

3 wierzchołek := nowy;

Pobranie obiektu z kolejki

1 pobrany := pocza˛tek.obiekt;

2 pocza˛tek := pocza˛tek.naste˛pny;

Pobranie obiektu ze stosu

1 pobrany := wierzchołek.obiekt;

2 wierzchołek := wierzchołek.nast ˛epny;

Wyszukiwanie w kolejkach i stosach (i listach jednokierunkowych) jest jak

poruszanie się po pomieszczeniach muzeum zgodnie z określonym kierunkiem

docsity.com

zwiedzania. Żeby wrócić po poprzedniego pomieszczenia, trzeba dojść do końca i

zacząć od nowa.

Listy jednokierunkowe

Listy jednokierunkowe mają strukturę wewnętrzną taką jak kolejki (i stosy), ale z

założenia pozwalają na dodatkowe operacje. Przede wszystkim:

⋆ dodawanie za wskazanym elementem,

⋆ usuwanie wybranego elementu. Dodanie elementu za wskazanym

1 nowy.nast ˛epny := wskazany.nast ˛epny; 2 wskazany.nast ˛epny := nowy; Dodanie elementu przed wskazanym kosztuje O(n) (za dużo, by używać), bo najpierw

trzeba znaleźć element poprzedzający. Jeśli wstawiamy przed pierwszy, to trzeba

również zweryfikować wskaźnik początku listy.

Usunięcie elementu wymaga wskazania elementu poprzedniego w stosunku do

usuwanego

1 usuwany := wskazany.nast ˛epny; 2 wskazany.nast ˛epny := usuwany.nast ˛epny; Listy dwukierunkowe Dodanie o za wskazanym elementem

1 nowy.obiekt := o; 2 nowy.poprzedni := wskazany; 3 nowy.nast ˛epny := wskazany.nast ˛epny; 4 wskazany.nast ˛epny := nowy; 5 nowy.nast ˛epny.poprzedni := nowy; Usunięcie elementu e

1 if e.poprzedni != nil 2 then e.poprzedni.nast ˛epny := e.nast ˛epny; 3 else pocza˛tek := e.naste˛pny; 4 if e.nast ˛epny != nil 5 then e.nast ˛epny.poprzedni := e.poprzedni; 6 else koniec := e.poprzedni;

⋆ dodawanie i usuwanie w O(1),

⋆ wyszukiwanie w O(n) - nie po to ta struktura.

Drzewa

Podstawowe definicje/założenia:

⋆ Drzewa to rekurencyjne kolekcje (podobnie jak kolejki, stosy czy listy), składające się z węzłów etykietowanych kolekcjonowanymi obiektami.

⋆ Drzewo jest puste bądź składa się z korzenia (węzeł główny) i kolekcji jego poddrzew.

⋆ Drzewa binarne to takie, w których każdy węzeł ma co najwyżej dwa podwęzły (lewy, prawy) a dokładniej poddrzewa.

⋆ Drzewa uporządkowane (inaczej niezbyt użyteczne): obiekt w danym węźle ma klucz nie mniejszy niż jakikolwiek węzeł jego lewego poddrzewa i nie większy niż

jakikolwiek z prawego poddrzewa.

⋆ Drzewo jest dokładnie wyważone wtw, gdy dla każdego węzła, liczby węzłów w

docsity.com

jego lewym i prawym poddrzewie różnią się co najwyżej o 1.

⋆ Drzewo jest wyważone wtw, gdy dla każdego węzła, wysokości dwóch jego poddrzew różnią się co najwyżej o 1.

Podstawowe definicje:

⋆ Graf skierowany (digraf) – para (V,E), gdzie V to dowolny zbiór (zbiór wierzchołków) a E to zbiór krawędzi (par wierzchołków, E ⊆V ×V).

⋆ Graf nieskierowany – jak skierowany, ale w krawędziach nieistotna jest kolejność wierzchołków.

⋆ Graf ważony – graf, w którym dodatkowo każdej krawędzi przypisana jest wartość rzeczywista zwana wagą: (V,E,w), w : E →R.

Ważony graf skierowany G = (V,E,w) najczęściej reprezentujemy tablicą W (macierzą przyległości, ang. adjacency matrix) taką, że:

W[i, j] = 0 jeśli i = j,

w(vi, vj) jeśli (vi, vj) ∈ E (w grafie istnieje krawędź (vi, vj)),

jeśli (vi, vj) /∈ E.

Graf nieskierowany można traktować jako skierowany o symetrycznych połaczeniach.

Grafy – definicji c.d.

⋆ Droga – ciąg wierzchołków taki, że istnieje krawędź z każdego wierzchołka do jego następnika, droga prosta – droga, która nie przechodzi dwa razy przez ten

sam wierzchołek.

⋆ Długość drogi – suma wag krawędzi, z których składa się droga. Dla grafów nieważonych – liczba krawędzi.

⋆ Cykl – droga, w której wierzchołek początkowy jest również końcowym, cykl prosty – cykl będący drogą prostą.

⋆ Graf cykliczny/acykliczny – graf, w którym istnieje/nie istnieje cykl.

⋆ Graf nieskierowany jest spójny, gdy między każdymi dwoma wierzchołkami istnieje droga.

⋆ Drzewo – graf nieskierowany, acykliczny i spójny.

⋆ Drzewo rozpinające graf (ang. spanning tree) – drzewo, które zawiera wszystkie wierzchołki grafu.

⋆ Minimalne drzewo rozpinające – drzewo rozpinające o minimalnej sumie wag krawędzi.

Problemy grafowe

Najpopularniejsze problemy:

⋆ Szukanie drogi o minimalnej długości.

⋆ Wyznaczanie minimalnego drzewa rozpinającego:

◮ jak najmniej asfaltu, by połączyć określone miasta,

◮ jak najmniej kabli w sieci komputerowej, itp. Podstawowe algorytmy:

docsity.com

⋆ Szukanie dróg:

◮ Dijkstry,

◮ Floyda (alg. programowania dynamicznego).

⋆ Wyznaczanie minimalnego drzewa rozpinającego:

◮ Prima,

◮ Kruskala. Algorytm Prima

Dane: Graf nieskierowany G = (V,E,w).

Wynik: Minimalne drzewo rozpinające D = (V,F,w|F) grafu G.

⋆ Y ←{v1}

⋆ F ← /0

⋆ Aż do uzyskania drzewa rozpinającego (Y =V):

◮ wyznaczamy wierzchołek y z V −Y o minimalnej odległości do Y (jednocześnie zapamiętujemy krawędź f , która dała minimalną odległość),

◮ Y ←Y ∪{y} tzn. dodajemy wierzchołek y do zbioru Y,

◮ F ←F ∪{ f } tzn. dodajemy krawędź f do zbioru F,

⋆ Wynik: drzewo D = (V,F,w|F). Złożoność czasowa: O(n2) (n to liczba wierzchołków).

Algorytm Kruskala

Dane: Graf nieskierowany G = (V,E,w).

Wynik: Minimalne drzewo rozpinające D = (V,F,w|F) grafu G.

Vi ←{vi} – rozłączne jednoelementowe zbiory (dla każdego wierzchołka),

F ← 

⋆ Sortujemy krawędzie ze zbioru E niemalejąco względem wag,

⋆ Aż do uzyskania drzewa rozpinającego (wszystkie podzbiory V zostały scalone):

◮ bierzemy kolejną krawędź f E wg. ustalonego porządku,

◮ jeśli krawędź f łączy wierzchołki z różnych podzbiorów to:

• scalamy podzbiory zawierające wierzchołki,

F F ∪{ f } tzn. dodajemy krawędź f do zbioru F,

⋆ Wynik: drzewo D = (V,F,w|F).

Złożoność czasowa: (n – liczba wierzchołków, m – liczba krawędzi)

Inicjowanie zbiorów: O(n), sortowanie: O(mlogm), pętla w najgorszym przypadku O(mlogm) (każda krawędź).

W najgorszym przypadku m n2, więc mlogm = n2 logn2 = n22logn, czyli O(n2 logn).

Algorytm Dijkstry

Dane: Graf G = (V,E,w).

Wynik: Najkrótsze (dokładniej: minimalnej długości) drogi z wierzchołka v V do

wszystkich pozostałych, długości dróg; drzewo rozpinające zawierające najkrótsze

drogi z wierzchołka v.

docsity.com

Y ←{v}

F ← 

⋆ Tak długo jak Y 6=V:

◮ wyznaczamy wierzchołek y V Y, o minimalnej długości najkrótszej drogi z v

poprzez wierzchołki z Y.

Y Y ∪{y},

F F ∪{ f }.

Złożoność czasowa: O(n2) (n to liczba wierzchołków).

Algorytm Floyda

Idea: Najkrótsza droga z vi do vj wiodąca przez vk składa się z najkrótszej drogi z vi

do vk oraz najkrótszej drogi z vk do vj.

Dane: Graf G = (V,E,w). Wynik: Długości najkrótszych (dokładniej: minimalnej długości) dróg pomiędzy każdą

parą wierzchołków.

1 function Floyd(W : array [1..n,1..n] of real) : array [1..n,1..n] of real 2 var D : array [1..n,1..n] of real; i, j, k : integer; 3 begin 4 D := W; 5 for k := 1 to n do 6 for i := 1 to n do 7 for j := 1 to n do 8 D[i,j] := minimum(D[i,j], D[i,k] + D[k,j]; 9 Floyd := D; 10 end; Złożoność czasowa: O(n3) (n to liczba wierzchołków).

docsity.com

komentarze (0)
Brak komentarzy
Bądź autorem pierwszego komentarza!
To jest jedynie podgląd.
Zobacz i pobierz cały dokument.
Docsity is not optimized for the browser you're using. In order to have a better experience we suggest you to use Internet Explorer 9+, Chrome, Firefox or Safari! Download Google Chrome