1. Zaprogramować zwykłe sortowanie przez wstawianie na
Transkrypt
1. Zaprogramować zwykłe sortowanie przez wstawianie na
1. Zaprogramować zwykłe sortowanie przez wstawianie na jednokierunkowej liście o nieznanej z góry długości. void sort_wstaw(wsk *P){ wsk *Q, *S, R; if (*P!=NULL){ Q = &((*P)->nast); while (*Q!=NULL){ S = P; while ((*S)->x < (*Q)->x) { S = &((*S)->nast); } if (S!=Q){ R = *Q; *Q = R->nast; R->nast = *S; *S = R; } else { Q = &((*Q)->nast); } } } } 2. Dla algorytmu Shella z h1 = 2 i h2 = 1, podać przykłady danych, na których ten algorytm działa (a) szybciej, (b) podobnie lub wolniej niż zwykłe sortowanie bąbelkowe. Pojedyncza zamiana elementów T [i] i T [i + h] wykonana w algorytmie Shella dla h > 1 odpowiada ciągowi h(h + 1)/2 kolejnych zamian w obrębie elementów T [i], T [i + 1], . . . , T [i + h − 1], T [i + h] w zwykłym sortowaniu bąbelkowym. Dlatego algorytm Shella działa szybciej na danych, których uporządkowanie prowadzi do zamian dla h > 1. W naszym przykładzie, gdy h = 2, np. dla [8, 7, 6, 5, 4, 3, 2, 1]. Z kolei dla danych [2, 1, 4, 3, 6, 5, 8, 7] faza algorytmu z h = 2 nie wykona żadnych zamian, a jedynie dodatkowe porównania. Stanowi więc dodatkowy nakład pracy ponad to, co wykona faza z h = 1, a więc zwykłe sortowanie bąbelkowe. 3. Algebraiczne operacje na wielomianach stopni nie większych niż n można wykonywać zapamiętując ich współczynniki w tablicach wg schematu w(x) = a0 + a1 x + a2 x2 + · · · + am xm −→ A[0] = a0 , A[1] = a1 , . . . , A[m] = am , A[m + 1] = · · · = A[n] = 0 i następnie działając na tych tablicach obliczać sumy, różnice, iloczyny i ilorazy wielomianów. Oszacować złożoność obliczeniową tablicowej realizacji tych 4 operacji? Niech mA i mB oznaczają stopnie wielomianów A[ ] i B[ ]. — Dodawanie i odejmowanie: C[i] = A[i] ± B[i], i = 0, 1, . . . , max{mA , mB } ¬ n; złożoność O(n). Pk — Mnożenie: C[k] = i=0 A[i]B[k − i], k = 0, 1, . . . , mA + mB ¬ n; złożoność O(n2 ). — Pisemne dzielenie A[ ] przez B[ ] wymaga mA − mB + 1 kroków, w każdym z nich mB mnożeń i odejmowań, a więc łącznie O(n2 ) działań. 4. Ekscentrycznością E wierzchołka v w grafie skierowanym G nazywamy długość najdłuższej wsród najkrótszych ścieżek z innych wierzchołków do v, E(v) = max dmin (x, v) , x∈V (G) dmin (x, v) = długość najkrótszej ścieżki z x do v . Centrum C grafu G jest to zbiór wierzchołków w G o minimalnej ekscentryczności. Wśród zastosowań można wymienić np. algorytm wyboru wsi na siedzibę gminnej przychodni zdrowia. Wybór wsi z centrum grafu połączeń drogowych jest najbardziej korzystny dla mieszkańców gminy. Wychodząc od macierzy najkrótszych ścieżek W = [wij ] w G utworzonej przez algorytm Warshalla-Floyda napisać fragment kodu wyznaczający centrum C(G). for (j=1; j<=n; j++){ M=W[1][j]; for (i=2; i<=n; i++){ if (M < W[i][j]) {M = W[i][j];} } E[j]=M; // E[j] = ekscentrycznosc wierzcholka j (max W[][j]) } for (j=1; j<=n; j++){ if (E[j]<M) {M=E[j];} } // M = najmniejsza ekscentrycznosc w G printf("Centrum grafu zawiera wierzcholki:\n"); for (j=1; j<=n; j++){ if (E[j]==M) {printf("%i, ", j);} } printf("\n"); 5. Gwarancją poprawnego działania algorytmu Dijkstry jest założenie, że długości krawędzi grafu są nieujemne wij 0. Załóżmy, że macierz W zawiera ujemne elementy. Naiwna metoda zaradzenia temu problemowi polega na dodaniu do wszystkich elementów W ustalonej liczby C > 0 tak, aby stały się one dodatnie. Po zastosowaniu algorytmu do tak zmodyfikowanej macierzy W , od długości każdej krawędzi w wyznaczonej najkrótszej ścieżce między wierzchołkami s i t należałoby odjąć liczbę C by odzyskać prawdziwą najmniejszą odległość z s do t. Wyjaśnić na prostym przykładzie dlaczego takie podejście jest błędne. u 2 v -3 2 s 5 t Dodanie 4 do wszystkich krawędzi powoduje, iż ich długości staną się dodatnie. Po tej modyfikacji najkrótsza ścieżka z s do t pokrywa się z krawędzią (s, t) o długości 9. Po odjęciu 4 otrzymujemy jej oryginalną długość 5. W rzeczywistości krótsza ścieżka to s − u − v − t o długości 1. W zmodyfikowanym grafie długość każdej jej krawędzi powiększona jest o 4, więc całkowita długość tej ścieżki — o 12. Nie będzie więc wykryta jako najkrótsza ścieżka z s do t. Błąd proponowanej metody polega na tym, iż nie bierze ona pod uwagę faktu, że liczba krawędzi w ścieżce nie ma na ogół nic wspólnego z jej długością mierzoną sumą wag tych krawędzi. Ścieżki zbudowane z wielu krawędzi podlegają znacznemu wydłużeniu w stosunku do ścieżek o małej liczbie krawędzi, więc początkowe relacje między ich długościami mogą łatwo zostać zmienione: najkrótsze ścieżki w oryginalnym grafie przestają nimi być w grafie zmodyfikowanym. Nota bene w powyższym przykładzie algorytm Dijkstry poprawnie wyznaczy najkrótszą ścieżkę z s do t na niezmodyfikowanym grafie, mimo obecności w nim ujemnej krawędzi, podczas gdy modyfikacja grafu psuje jego działanie.