Programowanie komputerów

Transkrypt

Programowanie komputerów
Programowanie komputerów
Jacek Lach
Zakład Oprogramowania
Instytut Informatyki
Politechnika Śląska
Plan
• Dynamiczne struktury danych
• Lista jednokierunkowa
Jacek Lach. Programowanie komputerów
• Lista dwukierunkowa
• Lista podwieszana
• Graf
• Drzewa BST
Dynamiczne struktury danych
Jacek Lach. Programowanie komputerów
• Struktura dynamiczna – struktura tworzona w trakcie działania programu
• Najczęstsze zastosowanie – tworzenie abstrakcyjnych typów/struktur danych (szczególnie przydatne w zastosowaniach algorytmicznych)
Dynamiczne struktury danych
• Przykłady struktur:
• lista jednokierunkowa
Jacek Lach. Programowanie komputerów
• lista dwukierunkowa
• drzewo
• graf
• UWAGA: każdą z w/w struktur można zrealizować stosując struktury statyczne (np. tablice)
Lista jednokierunkowa
• Składa się z pewnej liczby węzłów (elementów), z których każdy zawiera
Jacek Lach. Programowanie komputerów
• pewien zestaw danych
• adres następnego elementu
• Dodatkowo należy zapamiętać adres pierwszego węzła (tzw. głowa)
• Ostatni element wyróżnia się poprzez nadanie adresowi określonej wartości, którą można odpowiednio zinterpretować, tzn. inaczej niż adres (w języku C: NULL)
Lista jednokierunkowa
Jacek Lach. Programowanie komputerów
Głowa
Dane
Dane
Dane
Adres
Adres
Adres (NULL)
Definicja elementu
Jacek Lach. Programowanie komputerów
• Elementem listy jest najczęściej zmienna typu struktura
#define MAX_ROZMIAR 50;
/* definicja elementu listy */
struct elem_tag {
char wyraz[MAX_ROZMIAR+1];
struct elem_tag *nast;
};
/* korzystamy ze zmiennych wskaźnikowych
glowa – jeśli jest zmienna statyczna, jest
inicjowana wartością 0, czyli NULL */
struct elem_tag *glowa;
Wyświetlanie listy
• Styl Pascala:
void wypisz ( char *Akomunikat ) {
Jacek Lach. Programowanie komputerów
struct elem_tag *p;
printf("\n%s \n", Akomunikat);
}
/* wersja odpowiadająca programowi w "pascalu" */
p = glowa;
while (p) {
printf ( "kolejny wyraz to: %s\n", p->wyraz );
p = p->nast;
}
Wyświetlanie listy
• Styl C:
void wypisz (char *Akomunikat) {
Jacek Lach. Programowanie komputerów
struct elem_tag *p;
printf("\n%s \n", Akomunikat);
}
for(p=glowa;p;p=p->nast)
printf ( "kolejny wyraz to: %s\n", p->wyraz );
Jacek Lach. Programowanie komputerów
Metoda fifo
glowa
ostatni
nowy
Jacek Lach. Programowanie komputerów
Metoda fifo
glowa
ostatni
nowy
Metoda fifo
Jacek Lach. Programowanie komputerów
/* wczytanie wyrazow i utworzenie listy
zgodnie z zasadą nowy na końcu listy */
void wczytaj_fifo ( void ) {
char buf[MAX_ROZMIAR+1];
struct elem_tag *nowy, *ostatni;
printf ( "wpisz [%s] aby zakonczyc\n", KONIEC );
// jesli wczytywanym wyrazem nie jest 'koniec'
while (wczytaj_jeden_wyraz(buf)) {
/* próba przydzielenia pamięci */
nowy = ( struct elem_tag* )malloc( sizeof(struct
elem_tag) );
/* prawdopodobnie brak pamięci */
if (!nowy) exit(1);
Metoda fifo
Jacek Lach. Programowanie komputerów
/* kopiowanie wczytanego wyrazu */
strcpy( nowy->wyraz, buf );
nowy->nast=NULL;
/* pierwszy element listy - głowa wskazuje NULL */
if (!glowa)
glowa=nowy;
else
ostatni->nast=nowy;
}
}
/* nowo utworzony element listy jest jej ostatnim
elementem */
ostatni=nowy;
Jacek Lach. Programowanie komputerów
Metoda lifo
glowa
ostatni
nowy
Jacek Lach. Programowanie komputerów
Metoda lifo
glowa
nowy
ostatni
Metoda lifo
Jacek Lach. Programowanie komputerów
/* wczytanie wyrazow i utworzenie listy
zgodnie z zasadą nowy na początku listy */
void wczytaj_lifo ( void ) {
char buf[MAX_ROZMIAR+1];
struct elem_tag *poprzednia_glowa;
printf ( "wpisz [%s] aby zakonczyc\n", KONIEC );
/* jesli wczytywanym wyrazem nie jest 'koniec' */
Metoda lifo
Jacek Lach. Programowanie komputerów
while (wczytaj_jeden_wyraz(buf)) {
/* zapamietuje poprzednia glowe */
poprzednia_glowa=glowa;
/* próba przydzielenia pamięci */
glowa = ( struct elem_tag* )
malloc( sizeof(struct elem_tag) );
/* prawdopodobnie brak pamięci */
if (!glowa) exit(1);
/* wczytanego wyrazu */
strcpy( glowa->wyraz, buf );
/* lacze z poprzednia glowa */
glowa->nast=poprzednia_glowa;
}
}
Usuwanie listy z pamięci
/* usunięcie listy z pamięci */
void kasuj ( void ) {
Jacek Lach. Programowanie komputerów
struct elem_tag *p;
}
/* zaczynamy od pierwszego elementu */
while (glowa)
{
/* zapamiętanie adresu następnego elementu */
p = glowa->nast;
/* zwolnienie pamięci */
free (glowa);
/* glowa wskazuje na kolejny element */
glowa = p;
}
Lista jednokierunkowa
• Wstawianie elementu na pewnej pozycji Jacek Lach. Programowanie komputerów
• Dane: • wartości atrybutów
• numer pozycji na której należy wstawić element
Wstawianie
Jacek Lach. Programowanie komputerów
Dane
Głowa
Dane
Dane
Dane
Adres
Adres
Adres (NULL)
Wstawianie
• Metoda 1
• Przesuń się na element o danym numerze
Jacek Lach. Programowanie komputerów
• Wstaw element do listy
• Uzupełnij połączenia
Wstawianie
Jacek Lach. Programowanie komputerów
Dane
Adres
Głowa
Dane
Dane
Dane
Adres
Adres
Adres (NULL)
Dodawanie – v1
void dodaj_po_elem_v1(struct elem_tag *q, char *wyraz)
{
Jacek Lach. Programowanie komputerów
struct elem_tag *nowy;
/* próba przydzielenia pamięci kolejnemu elementowi */
nowy = ( struct elem_tag* )
malloc( sizeof(struct elem_tag) );
/* prawdopodobnie brak pamięci */
if (!nowy) exit(1);
/* przepisanie wczytanego wyrazu */
strcpy( nowy->wyraz, wyraz );
Dodawanie – v1
Jacek Lach. Programowanie komputerów
}
/* połączenie listy */
/* na koncu listy zostanie skopiowany NULL */
nowy->nast = q->nast;
q->nast = nowy;
void dodaj_na_poz_v1(int poz, char *wyraz) {
/* wyszukaj wskaźnik na kolejną pozycję */
for(p = glowa ;p && poz; p=p->nast, poz--);
}
if (p)
dodaj_po_elem_v1(p, wyraz);
Dodawanie – v1
• Wady Jacek Lach. Programowanie komputerów
• Trudno dodać element na pozycji 0
• By dodać element na pozycji 0 należałoby modyfikować wskaźnik na pierwszy element, co skomplikowałoby dodatkowo kod
• Rozwiązanie
• Wykorzystać wskaźniki na wskaźniki
Dodawanie
Dane
Jacek Lach. Programowanie komputerów
Adres
Głowa
Dane
Dane
Dane
Adres
Adres
Adres (NULL)
Dodawanie
Jacek Lach. Programowanie komputerów
struct elem_tag
*
Głowa
Głowa
Głowa
Dane
Dane
Dane
Adres
Adres
Adres
Dane
Dane
Adres
Adres
Dane
Dane
Dane
Adres
Adres
Adres
Dane
Dane
Dane
Adres
Adres
Adres
struct
elem_tag **
Dodawanie – v2
void dodaj_po_v2(struct elem_tag **pp, char *wyraz){
Jacek Lach. Programowanie komputerów
/* próba przydzielenia pamięci kolejnemu elementowi */
p = ( struct elem_tag* )malloc( sizeof(struct elem_tag) );
if (!p) exit(1); /* prawdopodobnie brak pamięci */
/*przepisanie wczytanego wyrazu */
strcpy( p->wyraz, wyraz );
// p jest wskaznikiem na nowoutworzony rekord, zawierajacy już slowo
/* połączenie listy */
p->nast = *pp;
// pp jest adresem glowy, *pp jest glowa, a zatem wskaznikem
// na pierwszy element listy; operacja powoduje, ze kopiujemy glowe
// do pola nast
*pp = p; // *pp jest glowa, kopiuje wskazania na nowoutworzony
// element do glowy
}
Dodawanie – v2
void dodaj_na_pozycji_v2(int poz, char *wyraz) {
Jacek Lach. Programowanie komputerów
struct elem_tag **pp;
/* wyszukaj wskaźnik na kolejną pozycję */
for (pp = &glowa ;*pp && poz; pp=&((*pp)->nast),
poz--);
// pp jest adresem glowy w PAO
}
// jeżeli glowa istniala
if (pp)
dodaj_po_v2(pp, wyraz);
Lista dwukierunkowa
• Elementem listy dwukierunkowej jest najczęściej zmienna typu struktura
Jacek Lach. Programowanie komputerów
#define MAX_STR 50;
struct elem_tag
{
/* dane */
int pole1;
char [MAX_STR+1] pole2;
double pole3;
/* adres następnego elementu listy */
struct elem_tag *nast, *poprz;
} glowa_1, glowa_2
Lista dwukierunkowa
• Składowe listy wielokierunkowej • Dane: wartości atrybutów
Jacek Lach. Programowanie komputerów
• Wskaźnik na następny element
• Wskaźnik na poprzedni element
• Dwa wskaźniki na elementy (pierwszy i ostatni)
Lista dwukierunkowa
Jacek Lach. Programowanie komputerów
Głowa_2
Głowa_1
Dane
Dane
Dane
Adres
Adres
Adres (NULL)
Adres (NULL)
Adres
Adres
Wstawianie elementu
Dane
Adres
Jacek Lach. Programowanie komputerów
Adres
Głowa_2
Głowa_1
Dane
Dane
Dane
Adres
Adres
Adres (NULL)
Adres (NULL)
Adres
Adres
Tworzenie listy
void dodaj_do_listy(char *buf)
{
/* próba przydzielenia pamięci elementowi */
struct elem_tag *p= (struct elem_tag* )
malloc( sizeof(struct elem_tag) );
Jacek Lach. Programowanie komputerów
if (!p) exit(1); /* prawdopodobnie brak pamięci */
strcpy( p->wyraz, buf ); /*przepisanie wczytanego wyrazu */
if (!glowa_1)
/* jesli lista nie istniała */
{
glowa_1=glowa_2=p;
p->nast=p->poprz=0;
}
else
/* jeśli był choć jeden element listy */
{
glowa_2->nast = p;
p->poprz = glowa_2;
glowa_2 = p;
}
}
Metoda wstawiania
• Mamy trzy przypadki
Jacek Lach. Programowanie komputerów
• Dodawanie elementu na pozycji 0
• Dodawanie elementu na pozycji n, gdzie 0<=n<liczba elementów listy
• Dodawanie elementu jako ostatniego elementu listy
• Pominiemy jawne rozróżnianie tych warunków (ćwiczenie) i przejdziemy do bardziej wyrafinowanego rozwiązania
Wstawianie na danej pozycji
void dodaj_na_pozycji_v2(int poz, char *wyraz) {
Jacek Lach. Programowanie komputerów
struct elem_tag **pp;
for(pp = &glowa_1 ;*pp && poz; pp=&((*pp)->nast),
poz--);
if (pp)
dodaj_po_v2(pp, wyraz);
}
Wstawianie na danej pozycji
Jacek Lach. Programowanie komputerów
void dodaj_po_v2(struct elem_tag **q, char *wyraz)
{
struct elem_tag *p = (struct elem_tag* )
malloc( sizeof(struct elem_tag) );
if (!p) exit(1);
/* prawdopodobnie brak pamięci */
}
strcpy( p->wyraz, wyraz );
p->nast = *q;
if(*q)
{
p->poprz=(*q)->poprz;
(*q)->poprz = p;
}
else
{
p->poprz=glowa_2;
glowa_2 = p;
}
*q = p;
Wstawianie na danej pozycji – inny zapis
Jacek Lach. Programowanie komputerów
void dodaj_po_compact(struct elem_tag **q, char *wyraz)
{
/* n - adres elementu, który ma stać się następny */
/* p - adres pola poprz do zmodyfikowania, adres pola poprz
następnego rekordu lub głowa */
struct elem_tag *n = *q, **p = n ? &(n->poprz):&glowa_2;
/* q jest adresem (głowy lub pola nast) do zmodyfikowania */
/* wartość wskazania *q została zapamiętana w n, tak więc
if(!(*q = ( struct elem_tag* )malloc( sizeof(struct elem_tag))))
exit(1);
strcpy( (*q)->wyraz, wyraz );
(*q)->nast = n;
/* połączenie z następnym */
(*q)->poprz = *p; /* pole poprz kopiujemy z rekordu, który stał
się następny */
*p = *q;
/* pole poprz z następnego rekordu na nasz */
}
Wstawianie na danej pozycji – inny zapis
Jacek Lach. Programowanie komputerów
• Uwaga: • Jak widać na poprzednim przykładzie, pewne konstrukcje języka C mogą prowadzić do zmniejszania się czytelności kodu. Należy raczej unikać takiego stylu programowania, chyba że istnieją ku temu obiektywne powody (wielkość kodu, szybkość działania itp.)
Struktury danych implementowane jako listy
• Stos (Stack)
• Last In First Out (LIFO)
Jacek Lach. Programowanie komputerów
• Operacja push
• Operacja pop
• Kolejka (Queue)
• First In First Out (FIFO)
• Operacja put
• Operacja get
Jacek Lach. Programowanie komputerów
Listy podwieszane: definicje
• Mamy to do czynienia z sytuacją, gdy jedna z list o organizacji liniowej, zawiera wskaźniki do innych list, niejako podwieszonych pod listą główną
• Elementem listy dwukierunkowej jest najczęściej zmienna typu struktura
Listy podwieszane
Jacek Lach. Programowanie komputerów
struct elem_item
{
/* jakieś dane */
}
/* adres elementu */
struct elem_item *dol;
struct elem_tag
{
/* jakieś dane */
/* następny element */
struct elem_tag * nast;
/* lista podwieszana */
struct elem_item *dol;
} glowa;
Listy podwieszane
Jacek Lach. Programowanie komputerów
Głowa_1
Dane
Dane
Dane
Adres
Adres
Adres (NULL)
AdresD
AdresD (NULL)
AdresD
Dane
Dane
AdresD
AdresD (NULL)
Dane
AdresD
Dane
AdresD (NULL)
Szczególny przypadek: graf
Głowa_1
Jacek Lach. Programowanie komputerów
Dane o
wierzchołku
1
1
2
3
Dane o
wierzchołku
2
Dane o
wierzchołku
3
Adres
Adres
Adres (NULL)
AdresD
AdresD (NULL)
AdresD
Adres
Adres
AdresD
AdresD (NULL)
Adres
AdresD
Adres
AdresD (NULL)
Zalety reprezentacji
• Mała objętość w porównaniu do metod opartych o tablice, takich jak macierz sąsiedztwa, tablice wierzchołków wchodzących, wychodzących itp.
Jacek Lach. Programowanie komputerów
• Wady – zmniejszenie czytelności algorytmów grafowych
• Dany wierzchołek grafu G=(N,E) można znaleźć w O(|N|) krokach
• Wierzchołek można dodać w O(1) krokach
• Krawędź można znaleźć, dodać lub usunąć w O(|N|+|E|) krokach (znalezienie wierzchołka, a następnie znalezienie, dodanie lub usunięcie krawędzi wychodzących)
• Reprezentacja zalecana dla grafów dla których |N|2>>|E| ; w przypadku grafów o dużej liczbie krawędzi w stosunku do kwadratu liczby wierzchołków lepiej zastosować struktury statyczne Drzewa binarne
Jacek Lach. Programowanie komputerów
korzeń
Data
Data
Data
Data
Data
Data
liście
Data
Drzewo BST (Binary Search Tree)
• Każdy element zawiera klucz
Jacek Lach. Programowanie komputerów
• Klucze są takiej postaci, że można je uporządkować liniowo
• Wszystkie elementy w lewym poddrzewie mają klucz mniejszy niż klucz rodzica
• Wszystkie elementy w prawym poddrzewie mają klucze większe niż klucz poddrzewa
BST – przykład
Jacek Lach. Programowanie komputerów
korzeń
15
lewe poddrzewo;
wszystkie klucze
lewego poddrzewa są
mniejsze niż klucz
korzenia
6
4
12
9
prawe poddrzewo;
wszystkie klucze
prawego poddrzewa są
większe niż klucz
korzenia
19
17
liść
22
20
25
BST
• Operacje:
• Poszukiwanie elementu
Jacek Lach. Programowanie komputerów
• Poszukiwanie poprzednika
• Poszukiwanie następcy
• Poszukiwanie minimum
• Poszukiwanie maksimum
• Wstawianie elementów
• Usuwanie elementów
• Wypisywanie elementów
Rekord drzewa BST
typedef
struct bstel
Jacek Lach. Programowanie komputerów
{
int data;
struct bstel *left;
struct bstel *right;
} bn;
Jacek Lach. Programowanie komputerów
Poszukiwanie elementu
int find(int key, struct bstel *node)
{
if (!node)
return 0;
/* nie znaleziono */
if (key == node->data)
return 1;
/* znaleziono */
if (key < node->data)
return find(key, node->left);
else
return find(key, node->right);
}
Jacek Lach. Programowanie komputerów
Wstawianie elementu
int insert(bn *p, bn **root)
{
if (*root && (*root)->data == p->data)
return 1;
/* już jest w drzewie */
if (!*root) {
/* drzewo bez elementów */
*root = p;
return 0;
}
else
if (p->data > (*root)->data)
insert(p,&((*root)->right));
else
insert(p,&((*root)->left));
return 0;
}
Jacek Lach. Programowanie komputerów
Wstawianie elementu
int ins_el(int key, bn **root)
{
bn *p;
p = (bn*)malloc(sizeof(bn));
if (!p) return -1;
/* brak pamięci */
p->data = key;
p->left = p->right = NULL;
return insert(p,root);
}
Jacek Lach. Programowanie komputerów
Poszukiwanie minimum
struct bstel* min(struct bstel *node)
{
if (!node) return NULL; /* błąd */
while (node->left)
node = node->left;
return node;
}
Jacek Lach. Programowanie komputerów
Poszukiwanie maksimum
struct bstel* max(struct bstel *node)
{
if (!node) return NULL; /* błąd */
while (node->right)
node = node->right;
return node;
}
Znajdowanie poprzednika i następnika
•
Wartość succ(17)=19
•
Jak znaleźć taki węzeł:
Jacek Lach. Programowanie komputerów
•
jeśli węzeł ma prawe poddrzewo, zwróć minimalny wierzchołek w prawym poddrzewie
•
jeśli węzeł nie ma prawego poddrzewa, znajdź wierzchołek, do którego lewego poddrzewa należy dany węzeł
•
jeśli warunki nie są spełnione, węzeł nie posiada następnika (posiada maksymalną wartość)
15
6
4
19
12
9
17
22
20
25
Jacek Lach. Programowanie komputerów
Znajdowanie następnika
struct bstel* succ(struct bstel *node)
{
bn* y;
if (!node) return NULL;
/* błąd */
if (node->right)
return min(node->right);
y = parent(node); /* zaimplementować */
while (y && node==y->right) {
node = y;
y = parent(y);
}
return y;
}
Usuwanie elementów
•
Załóżmy, że należy usunąć x
Jacek Lach. Programowanie komputerów
• Jeśli x ma obydwa poddrzewa, znajdź succ(x) i przesuń go na miejsce x
15
• Jeśli x ma tylko jedno poddrzewo, zastąp x tym poddrzewem
• Jeśli x jest liściem, po prostu usuń x
6
4
19
12
9
17
22
20
25
Jacek Lach. Programowanie komputerów
Wydruk całego drzewa
void print_tree(bn *node)
{
if (node) {
print_tree(node->left);
printf("%d ",node->data);
print_tree(node->right);
}
}
Wykorzystanie
Jacek Lach. Programowanie komputerów
main()
{
bn *root = NULL;
/* na początku puste drzewo */
ins_el(10,&root);
ins_el( 6,&root);
...
ins_el( 7,&root);
ins_el(23,&root);
ins_el(11,&root);
/* budowanie drzewa */
print_tree(root);
/* powinno być posortowane! */
printf(”\n %d \n", min(root)->data);
printf("find(%d)=%d\n",13,find(13,root));
return 0;
}

Podobne dokumenty