Quick sort, spojové struktury BI-PA1 Programování a Algoritmizace 1 Miroslav Baĺık, Ladislav Vagner a Josef Vogel Katedra teoretické informatiky a Katedra softwarového inženýrství Fakulta informačních technologíı České vysoké učení technické v Praze xvagner@fit.cvut.cz, vogeljos@fit.cvut.cz 12., 21. a 22. prosince 2017
Přehled Datový typ struct (struktura, záznam) připomenutí. Algoritmus quick sort. Knihovní implementace quick sort, ukazatel na funkci. Spojové seznamy. Obousměrné spojové seznamy. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 2/39
Struktury opakování Deklarace struktury: struct jméno_struktury { položky seznam_proměnných; jméno struktury je jméno datového typu struct (nepovinné), seznam proměnných je seznam deklarovaných proměnných, proměnné odděleny čárkami (nepovinné), položky jsou jakéhokoli datového typu, s výjimkou struktury samé (rekurzivní deklarace struktury je zakázána), avšak položkou může být ukazatel na strukturu samou (struct jméno struktury * jmeno polozky). Deklarace proměnné: struct jméno_struktury seznam_proměnných; /* ok */ jméno_struktury seznam_proměnných; /* chybně v C */ M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 3/39
Struktury opakování Zkráceně může být datový typ struktura také deklarován: typedef struct jméno_struktury { položky jméno_typu; /* deklarace proměnných: */ jméno_typu seznam_proměnných; /* také ok: */ struct jméno_struktury jiný_seznam_proměnných; jméno typu je zkratka pro struct jméno struktury, jméno struktury je nepovinné ve výše uvedené deklaraci, jméno typu není povinné, ale použití typedef je podezřelé, když jméno typu není použito. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 4/39
Struktury příklad typedef struct TComplex { double real, imag; TCOMPLEX; TCOMPLEX a, *pa; struct TComplex b; Položky struktury jsou přístupné použitím dvou operátorů: tečka. se použije k přístupu k položce struktury, šipka -> se použije pro přístup k položce přes ukazatel na proměnnou typu struktura. a.real = 10; a.imag = a.real + 5; pa = &b; pa->real = 230; pa->imag = pa->real + a.imag; a = *pa; /* ok, kopie obsahu */ (*pa).real = (&a)->imag; /* ok, ale neobvyklé */ M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 5/39
Algoritmus Quick sort Merge sort má časovou složitost O(n log n). To je asymptoticky optimální, avšak má dvě nevýhody: skrytá multiplikativní konstanta není malá, algoritmus potřebuje další pamět ový prostor pole řazených prvků ještě jednou. Algoritmus Quick sort nemá tato omezení: skrytá multiplikativní konstanta je velmi malá, algoritmus nepotřebuje žádný další prostor, na druhé straně, v nejhorším případě je časová složitost O(n 2 ). Vstupní data musí mít speciální pořadí, aby se dosáhl nejhorší případ. Průměrná složitost je O(n log n). Empiricky je Quick sort nejrychlejší volbou. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 6/39
Algoritmus Quick princip Vybere se pivot. Ideálně má pivot hodnotu mediánu prvků pole. Algoritmus rozděĺı pole na dvě části a prohodí prvky tak, aby: dolní část obsahovala prvky menší než pivot, horní část obsahovala prvky větší nebo rovné pivotu. Obě části se řadí rekurzivně. Výběr pivota je rozhodující výběr je dobrý, když obě části jsou stejně velké. Při takové volbě je algoritmus nejrychlejší. Při špatném výběru je velikost pole zmenšena pouze o jeden prvek a algoritmus degraduje na kvadratickou složitost. Optimální hodnota pivota je medián, ale ten není znám, dokud pole neni seřazeno. Dobré implementace proto používají třeba medián z náhodně vybraných tří prvků pole. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 7/39
Algoritmus Quick sort příklad 5 11 14 7 9 4 20 1 Pivot: 7 5 1 4 7 9 14 20 11 Pivot: 5 Pivot: 14 4 1 5 7 9 11 20 14 Pivot: 4 Pivot: 11 Pivot: 20 1 4 5 7 9 11 14 20 Pivot: 7 1 4 5 7 9 11 14 20 M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 8/39
Knihovní funkce pro Quick sort Standardní knihovna C obsahuje implementaci algoritmu Quick sort. Prototyp funkce je obsažen v stdlib.h. Implementace je generická prvky pole, které jsou algoritmem řazeny, mohou být libovolného typu. Funkce qsort je kompletní implementací algoritmu Quick sort, až na vlastní porovnání: prvky pole musí být porovnány a zaměněny mezi sebou, záměna se provede bajt po bajtu, porovnání je různorodé, závisí na typu prvků pole a řadicím kritériu. Funkci, která porovnává dva prvky pole mezi sebou, dodává aplikační programátor v podobě ukazatele na funkci. Kdykoliv potřebuje algoritmus Quick sort porovnat nějaké prvky, zavolá tuto uživatelskou funkci. Volání probíhá prostřednictvím předaného ukazatele. Porovnávací funkci jsou dodány ukazatele na oba porovnávané prvky pole. Návratová hodnota je kladná, nulová nebo záporná pro hodnoty první větší než druhý, resp. rovné nebo menší. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 9/39
Knihovní funkce pro Quick sort #include <stdlib.h> int main( void ) { int a[9] = {1,5,9,3,6,4,89,23,11; int i; qsort( (void*)a, 9, sizeof(a[0]), intcompare ); /* intcompare je porovnávací funkce */ for(i = 0; i < 9 ; i ++ ) printf ( "%3d ",a[i] ); return 0; M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 10/39
Knihovní funkce pro Quick sort int intcompare ( const void *a, const void *b ) { int * aa, *bb; aa = (int*) a; bb = (int*) b; /* a b jsou ukazatele na porovnávané prvky pole, typ ukazatele - void * - musí být přetypován na typ ukazatele podle skutečného prvku pole, (zde int - ukazatele aa a bb) */ if ( *aa < *bb ) return -1; if ( *bb < *aa ) return 1; return 0; M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 11/39
Knihovní funkce pro Quick sort int intcompare ( const void *a, const void *b ) { int * aa, *bb; aa = (int*) a; bb = (int*) b; return *aa - *bb; /* kratší, často správné, ale není 100% OK */ M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 12/39
Knihovní funkce pro Quick sort int intcompare ( const void *a, const void *b ) { int * aa, *bb; aa = (int*) a; bb = (int*) b; return *aa - *bb; /* kratší, často správné, ale není 100% OK */ int intcompare ( const void *a, const void *b ) { return *(int*)a - *(int*)b; /* ještě kratší, ale opět není 100% OK */ M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 12/39
Knihovní funkce pro Quick sort /* Proč parametry const void *? Ty skutečně v naší implementaci ukazují na celá čísla */ int intcompare ( const int *a, const int *b ) { return *a - *b; /* pěkné - jednodušeji už nelze, ale stále není 100% OK */ /* Ale knihovní funkce qsort předpokládá jiné rozhraní funkce. Ok, přesvědčme kompilátor, že ten mišmaš funkčních ukazatelů je naše přání */... qsort( (void*)a, 9, sizeof(a[0]), ( int(*)(const void *, const void *) ) intcompare ); /* ukazatel na funkci je přetypován, aby se shodovaly očekávané typy */... M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 13/39
Quick sort porovnávací funkce, pro zvídavé Můžeme si dovolit přetypovat ukazatele na funkci dle libosti? M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 14/39
Quick sort porovnávací funkce, pro zvídavé Můžeme si dovolit přetypovat ukazatele na funkci dle libosti? Ne. Kompilátor naše přetypování sice akceptuje, ale při neuváženém přetypování je pravděpodobný pád programu. Proč tedy zde přetypování funguje? M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 14/39
Quick sort porovnávací funkce, pro zvídavé Můžeme si dovolit přetypovat ukazatele na funkci dle libosti? Ne. Kompilátor naše přetypování sice akceptuje, ale při neuváženém přetypování je pravděpodobný pád programu. Proč tedy zde přetypování funguje? Protože to není libovolné přetypování: funkce qsort připraví pro porovnávací funkci dva parametry typu ukazatel (odkazují na porovnávané prvky). Skutečně volaná funkce má také dva parametry typu ukazatel, liší se typy ukazatelů na prvky, ale jejich pamět ová reprezentace za běhu je stejná (4/8 B v paměti), funkce qsort zavolá funkci odkazovanou předaným ukazatelem na funkci, námi dodaný ukazatel odkazuje na platnou funkci, očekává se návratová hodnota typu int, námi dodaná funkce vrací typ int. Co tedy toto přetypování ukazatele na funkci fakticky udělá? M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 14/39
Quick sort porovnávací funkce, pro zvídavé Můžeme si dovolit přetypovat ukazatele na funkci dle libosti? Ne. Kompilátor naše přetypování sice akceptuje, ale při neuváženém přetypování je pravděpodobný pád programu. Proč tedy zde přetypování funguje? Protože to není libovolné přetypování: funkce qsort připraví pro porovnávací funkci dva parametry typu ukazatel (odkazují na porovnávané prvky). Skutečně volaná funkce má také dva parametry typu ukazatel, liší se typy ukazatelů na prvky, ale jejich pamět ová reprezentace za běhu je stejná (4/8 B v paměti), funkce qsort zavolá funkci odkazovanou předaným ukazatelem na funkci, námi dodaný ukazatel odkazuje na platnou funkci, očekává se návratová hodnota typu int, námi dodaná funkce vrací typ int. Co tedy toto přetypování ukazatele na funkci fakticky udělá? Nic. Ve výsledném programu se toto přetypování neprojeví žádným výkonným kódem. Přetypování pouze potlačí chybu/varování kompilátoru. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 14/39
Quick sort porovnávací funkce, pro zvídavé Opravdu lze v porovnávací funkci odečítat místo porovnání? M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 15/39
Quick sort porovnávací funkce, pro zvídavé Opravdu lze v porovnávací funkci odečítat místo porovnání? Obecně nelze. Pokud dochází k přetékání, výsledné pole nebude správně seřazené. Zkuste si pro pole celých čísel s hodnotami pobĺıž horního a dolního limitu typu int. Lze trik s odečítáním použít pro desetinná čísla? M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 15/39
Quick sort porovnávací funkce, pro zvídavé Opravdu lze v porovnávací funkci odečítat místo porovnání? Obecně nelze. Pokud dochází k přetékání, výsledné pole nebude správně seřazené. Zkuste si pro pole celých čísel s hodnotami pobĺıž horního a dolního limitu typu int. Lze trik s odečítáním použít pro desetinná čísla? Nelze. Pokud je výsledkem odečítání číslo v intervalu (-1,+1), bude v návratovém typu převedeno na hodnotu 0 int, tedy taková desetinná čísla budou pro porovnání shodná. Lze trik s odečítáním použít pro typy char / short int? M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 15/39
Quick sort porovnávací funkce, pro zvídavé Opravdu lze v porovnávací funkci odečítat místo porovnání? Obecně nelze. Pokud dochází k přetékání, výsledné pole nebude správně seřazené. Zkuste si pro pole celých čísel s hodnotami pobĺıž horního a dolního limitu typu int. Lze trik s odečítáním použít pro desetinná čísla? Nelze. Pokud je výsledkem odečítání číslo v intervalu (-1,+1), bude v návratovém typu převedeno na hodnotu 0 int, tedy taková desetinná čísla budou pro porovnání shodná. Lze trik s odečítáním použít pro typy char / short int? Obecně nelze, ale téměř jistě to bude vždy správně fungovat. Podle normy jazyka se před odečtením char / short int musí rozšířít na celé číslo (integer promotion) a v datovém typu int se provede vlastní operace. Pokud je rozsah char / short int menší než rozsah typu int, nemůže dojít k přetečení. I tak je ale lepší se triku vyhnout. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 15/39
Quick sort porovnávací funkce, pro zvídavé int intcmpwrong ( const int * a, const int * b ) { return *a - *b; int dblcmpwrong ( const double * a, const double * b ) { return *a - *b; int a[]={ 1000000000, -1000000000, 2000000000, -2000000000 ; qsort ( a, sizeof ( a ) / sizeof ( a[0] ), sizeof ( a[0] ), (int(*)(const void *, const void *)) intcmpwrong ); /* 2000000000-2000000000 -1000000000 1000000000 */ double b [] = { 1.2, 1.3, 1.8, 1.5, 1.7, 1.1, 1.4 ; qsort ( b, sizeof ( b ) / sizeof ( b[0] ), sizeof ( b[0] ), (int(*)(const void *, const void *)) dblcmpwrong ); /* 1.2 1.3 1.8 1.5 1.7 1.1 1.4 */ M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 16/39
Quick sort porovnávací funkce, pro zvídavé Proč se tedy trik s odečítáním ukazuje? M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 17/39
Quick sort porovnávací funkce, pro zvídavé Proč se tedy trik s odečítáním ukazuje? Kód lze často najít v existujícím software a literatuře. Vše funguje, dokud nedojde k přetečení. Tím je trik zákeřný většinou funguje, ale není 100% správný. Vyhněte se mu. Jak tedy správně? M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 17/39
Quick sort porovnávací funkce, pro zvídavé Proč se tedy trik s odečítáním ukazuje? Kód lze často najít v existujícím software a literatuře. Vše funguje, dokud nedojde k přetečení. Tím je trik zákeřný většinou funguje, ale není 100% správný. Vyhněte se mu. Jak tedy správně? int intcompare ( const int *a, const int *b ) { return ( *b < *a ) - ( *a < *b ); /* OK! */ int dblcompare ( const double *a, const double *b ) { return ( *b < *a ) - ( *a < *b ); /* OK! */ int shortcompare ( const short *a, const short *b ) { return ( *b < *a ) - ( *a < *b ); /* OK! */ M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 17/39
Seřazení studentů knihovní funkcí qsort () Seřazení pole studentů podle průměrného prospěchu: #include <stdlib.h> typedef struct TStudent { char name[name_max], surname [NAME_MAX]; double avg; TSTUDENT; int studentcompare ( TSTUDENT *a, TSTUDENT *b ) { return (b->avg < a->avg) - (a->avg < b->avg); void sortstudents (TSTUDENT a[], int n ) { qsort ( (void*)a, n, sizeof(a[0]), (int(*) (const void *, const void *)) studentcompare ); M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 18/39
Seřazení studentů knihovní funkcí qsort () Seřazení pole studentů podle průměrného prospěchu: #include <stdlib.h> typedef struct TStudent { char name[name_max], surname [NAME_MAX]; double avg; TSTUDENT; int studentcompare ( TSTUDENT *a, TSTUDENT *b ) { return (b->avg < a->avg) - (a->avg < b->avg); void sortstudents (TSTUDENT a[], int n ) { qsort ( (void*)a, n, sizeof(a[0]), (int(*) (const void *, const void *)) studentcompare ); Pořadí studentů se stejným průměrem není po seřazení definováno, může být stejné i obrácené než bylo v původním poli (Quick sort může pořadí zachovat, ale může i prvky prohodit). Takové řazení se nazývá nestabilní. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 18/39
Seřazení studentů knihovní funkcí qsort () Pokud bychom chtěli, aby dalším kritériem pro pořadí bylo navíc abecední pořadí příjmení a jména, museli bychom porovnání upravit třeba takto: #include <string.h> int studentcompare ( TSTUDENT *a, TSTUDENT *b ) { int res = (b->avg < a->avg) - (a->avg < b->avg); if (res) return res; if (( res = strncmp ( a->surname, b->surname, NAME_MAX ))) return res; return strncmp ( a->name, b->name, NAME_MAX ); M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 19/39
Spojové struktury seznam Problém: program čte celá čísla ze vstupu (až do EOF). Vstup je pak zobrazen v obráceném pořadí. Rekurzivní řešení (hloubka rekurze). Uložení do pole (musí být dána velikost pole). Uložení do zřetězeného seznamu: struktura je dynamicky alokována pro každé vstupující číslo, struktura obsahuje hodnotu čísla a ukazatel na strukturu obsahující předchozí číslo ze sekvence (následující v řetězu). struktura obsahující první číslo (poslední v řetězu) má prázdný ukazatel (hodnota NULL). Input sequence: 56, 8, 5 Head Dynamically allocated structures 5 8 56 M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 20/39
Spojový seznam typedef struct TElement { int val; struct TElement *next; TELEMENT; TELEMENT * createelement ( int val, TELEMENT * next ) { TELEMENT * n; n = (TELEMENT *)malloc ( sizeof (*n) ); n->val = val; n->next = next; return n; M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 21/39
Spojový seznam Problém obrácení vstupní sekvence: začátek s prázdným seznamem, čtení čísla ze vstupu, přidání čísla přidání do seznamu dopředu (na první pozici), opakuje se až do konce vstupu, zobrazí se hodnoty ze seznamu (seznam je už obrácený). M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 22/39
Spojový seznam int main ( void ) { TELEMENT * st = NULL, *p; int x; printf ( "Napis sekvenci cisel:\n" ); while ( scanf ( "%d", &x ) == 1 ) st = createelement ( x, st ); printf ( "Obracene:\n" ); p = st; while ( p ) { printf ( "%d ", p->val ); p = p->next; printf ( "\n" ); return 0; M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 23/39
Spojový seznam Potřebujeme další ukazatel (p) pro čtení hodnot?... printf ( "Obracene:\n" ); while (st) { printf ( "%d ", st->val ); st = st->next; /* jejda */... Když je hlava seznamu ztracena (ukazatel st je změněn), neexistuje žádný způsob, jak najít hlavu seznamu. Seznam je ztracen. Čtení spojového seznamu tedy vždy využívá pomocný ukazatel na prvek seznamu. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 24/39
Spojový seznam Jak uvolnit pamět alokovanou spojovým seznamem? Špatné řešení:... while ( st ) { free ( st ); st = st->next; /* může selhat - přistupuje k již uvolněné paměti */ Správné řešení:... while ( st ) { TELEMENT * p = st->next; /* ukazatel je zachován */ free ( st ); st = p; M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 25/39
Spojový seznam Jiný problém: Čtení sekvence celých čísel, jejich uložení do zřetězeného seznamu. Sekvence bude zakončena EOF. Duplikované hodnoty se neukládají. Hodnoty budou uloženy v obráceném pořadí. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 26/39
Spojový seznam void printlist ( TELEMENT *st ) { while ( st ) { printf ( "%d ", st->val ); st = st->next; /* ok - modifikujeme jen lokální proměnnou */ printf ( "\n" ); int isinlist (TELEMENT * st, int x) { while ( st && st->val!= x ) st = st->next; return st!= NULL; void freelist ( TELEMENT * st ) { while ( st ) { TELEMENT * p = st->next; free ( st ); st = p; M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 27/39
Spojový seznam int main ( void ) { TELEMENT *st = NULL, *p; int x; printf ( "Napis sekvenci:\n" ); while ( scanf ( "%d", &x ) == 1 ) if (! isinlist ( st, x ) ) st = createelement (x, st); printf ( "Obracene, duplicity odstraneny:\n"); printlist ( st ); freelist ( st ); st = NULL; /* trochu paranoidní */ return 0; M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 28/39
Spojový seznam Vytvořili jsme nový seznam a nový prvek jsme zařadili na začátek seznamu. Proto seznam obsahuje hodnoty v obráceném pořadí. Modifikujme program tak, abychom prvky přidávali na jeho konec (pořadí zachováno): Když je seznam prázdný, tak prvek nahradí hodnotu NULL, je-li neprázdný, musíme najít poslední prvek a zde připojit prvek s novou hodnotou. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 29/39
Spojový seznam TELEMENT * appendend ( TELEMENT *st, int x ) { TELEMENT * tmp; /* prázdný seznam */ if (! st ) return createelement ( x, NULL ); /* neprázdný - najít poslední prvek */ for ( tmp = st; tmp->next; tmp = tmp->next ) { tmp->next = createelement ( x, NULL ); return st;... while ( scanf ( "%d", &x ) == 1 ) if (! isinlist ( st, x ) ) st = appendend (st, x); M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 30/39
Spojový seznam Vložení prvého prvku potřebuje konstantní čas O(1). Připojení dalšího prvku vyžaduje nalezení konce seznamu O(n), kde n je délka seznamu. Když vynecháme hledání duplicit (které má lineární složitost O(n)), můžeme algoritmus optimalizovat na konstantní složitost O(1). K tomu použijeme další ukazatel, který ukazuje na poslední prvek seznamu: ukazatel bude na počátku nastaven na NULL pro prázdný seznam, ukazatel bude aktualizován při při každém přidání prvku na konec seznamu. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 31/39
Spojový seznam int main ( void ) { TELEMENT *st = NULL, *en = NULL, *p; int x; printf ( "Napis sekvenci:\n" ); while ( scanf ( "%d", &x ) == 1 ) { p = createelement ( x, NULL ); if ( en == NULL ) st = p; /* prázdný seznam */ else en->next = p; en = p; /* aktualizace ukazatele na poslední prvek */... M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 32/39
Spojový seznam zarážka Zarážka je jeden prvek navíc na konci spojového senzamu. Neobsahuje užitečná data, pouze zjednodušuje některé operace: např. lze vypustit if v cyklu. Tím se program trochu urychĺı (nikoli však z hlediska asymptotické složitosti) a také zcela zmizí problém prvého vložení do seznamu. Operace připojení, vkládání a mazání musí zachovat zarážku na konci seznamu. Čtecí operace musí zarážku přeskočit. Prázdný seznam je reprezentován jediným prvkem zarážkou. st Linked list with sentinel Input sequence: 5, 8 5 8?? en M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 33/39
Spojový seznam zarážka int main ( void ) { TELEMENT *st, *en; int x; /* inicializace seznamu se zarážkou */ st = en = malloc ( sizeof(*en) ); printf ( "Napis sekvenci cisel:\n" ); while ( scanf ( "%d", &x ) == 1 ) en = appendend ( x, en ); printlist ( st, en ); freelist ( st, en ); TELEMENT * appendend ( int x, TELEMENT * en ) { TELEMENT *p = en; en = malloc ( sizeof(*en) ); p->val = x; p->next = en; return en; M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 34/39
Spojový seznam zarážka void printlist ( TELEMENT * st, TELEMENT * en ) { while ( st!= en ) { printf ( "%d ", st->val ); st = st->next; printf ( "\n" ); void freelist ( TELEMENT * st, TELEMENT * en) { while ( st!= en ) { TELEMENT * p = st->next; free ( st ); st = p; free ( en ); M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 35/39
Spojový seznam Údržba seřazeného spojového seznamu vyžaduje vkládání prvků kamkoli v seznamu (na začátek, na konec, dovnitř) Tato operace vyžaduje čas O(n). Neexistuje žádný jednoduchý trik, jak tento algoritmus urychlit. Při vložení prvku budeme vždy modifikovat dva ukazatele. Když udržujeme ukazatel na poslední prvek, musíme modifikovat ještě tento ukazatel, je-li nový prvek je poslední v seznamu. M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 36/39
Spojový seznam int main ( void ) { TELEMENT *st = NULL, *en = NULL, *tmp, *prev; int x; printf ( "Napis sekvenci:\n" ); while ( scanf ( "%d", &x ) == 1 ) { prev = NULL; for ( tmp = st; tmp && tmp->val <= x; tmp = tmp->next ) prev = tmp; if ( prev == NULL ) { /* první v seznamu */ st = createelement ( x, st ); if (! en ) en = st; else { /* dovnitř nebo na konec */ prev->next = createelement ( x, prev->next ); if ( prev == en ) en = en->next;... M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 37/39
Spojové struktury Spojové seznamy jsou jednoduché spojové struktury. Spoje propojují prvky spojové struktury. Spoje vytvářejí relaci předchůdce následník. Spojové seznamy jsou lineární struktury každý prvek má nejvíce jednoho následníka. Single linked list Cyclic single linked list Double linked list M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 38/39
Otázky a odpovědi Otázky... M. Baĺık, L. Vagner a J. Vogel, ČVUT FIT Spojové struktury, BI-PA1 39/39