Wykład czwarty: Jednokierunkowa lista liniowa

Transkrypt

Wykład czwarty: Jednokierunkowa lista liniowa
Podstawy Programowania
semestr drugi
Wykład trzeci
1.
Jednokierunkowa lista liniowa
Jednokierunkowa lista liniowa jest abstrakcyjną strukturą danych przechowującą elementy w określonym liniowym porządku. Elementy można
dodawać i usuwać z listy w dowolnym jej miejscu. Czas wykonania tych operacji jest stały i niezależny od ilości elementów w liście 1,
natomiast czas wyszukania elementu jest proporcjonalny do ilości elementów umieszczonych na liście.
2.
Implementacja listy jednokierunkowej jako dynamicznej struktury danych
Lista jednokierunkowa zostanie opisana na przykładzie programu, który wstawia do listy kolejne elementy, tak aby znajdowały się w porządku
niemalejącym. Można również użyć listy w inny sposób. Definicja typu pojedynczego elementu listy jest taka sama jak w przypadku stosu i
kolejki (nazwy pól nie mają większego znaczenia). W przypadku list stosuje się zazwyczaj jeden wskaźnik, wskazujący na element
początkowy listy. Podstawowymi operacjami dotyczącymi listy są operacje wstawiania i usuwania elementu, i usuwanie listy. Można również
zdefiniować operację wyszukiwania elementu na liście spełniającego ustalone kryterium. Oto kod wspomnianego wyżej programu:
Operacja wstawiania elementu na listę została „rozbita” na
dwie procedury: zadaniem procedury „crate” sprawdza, czy
istnieje pierwszy element na tej liście. Jeśli nie to tworzy go
(wiersze 69
74) i kończy swoje działanie. Jeśli takowy
element istnieje wywoływana jest procedura „insert_node”.
Zadaniem tej procedury jest utworzenie nowego elementu
listy i umieszczenie go w odpowiednim miejscu na tej liście.
Procedura ta pobiera dwa parametry. Pierwszym jest
wskaźnik na pierwszy element listy, a poprzez drugi
przekazywana jest wartość, która zostanie umieszczona w
elemencie. Posiada ona również trzy lokalne zmienne
wskaźnikowe. Zmienna „p” będzie służyła do poruszania się
po liście. Zmienna „prev” będzie wskazywała na element
poprzedzający ten, który wskazuje „p”. Zmienna „nowy”
wskazuje na nowy, utworzony przez procedurę element listy.
W wierszach 21 26 obsługiwany jest przypadek, kiedy ze
względu na przechowywaną wartość nowy element powinien
zostać elementem początkowym listy. Wówczas w wierszu
23 w jego polu „next” zapisywany jest adres bieżącego
pierwszego elementu listy, a następnie w wierszu 24 do
wskaźnika „f” wskazującego na pierwszy element listy
zapisywany jest adres nowego elementu. Jeśli jednak nowy
element nie może być wstawiony przed pierwszym
elementem, wówczas w pętli (wiersze 28
37)
przeszukiwana jest cała lista, w celu znalezienia miejsca dla
niego. Jeśli w całej liście nie ma elementu o większej
wartości, to jest on dodawany na końcu listy i procedura
kończy swoje działanie. W przeciwnym przypadku jest on
wstawiany przed elementem o większej wartości. Ilustruje to
następujący rysunek:
1 program single_linked_list;
2 uses crt;
3 type
4 wskaznik=^element;
5 element=record
6
dana:integer;
7
next:wskaznik;
8
end;
9 var
10 first:wskaznik;
11 ne:integer;
12
13
14 procedure insert_node(var f:wskaznik; a:integer);
15 var
16 prev,p,nowy:wskaznik;
17 begin
18
19
nowy^.dana:=a;
20
nowy^.next:=nil;
21
if f^.dana > a then
22
begin
23
nowy^.next:=f;
24
f:=nowy;
25
end;
27
p:=f;
28
while (p^.dana <= a) do
29
begin
dana
NIL
31
p:=p^.next;
if p=nil then
nowy
prev
p
dana
next
dana
next
2. Po wykonaniu wiersza 38
dana
next
prev
dana
next
3. Po wykonaniu wiersza 39
prev:=p;
32
33
1. Przed wykonaniem wiersza 38
exit;
26
30
1
new(nowy);
dana
next
prev
nowy
p
dana
next
nowy
p
begin
34
prev^.next:=nowy;
35
exit;
dana
next
Tak też jest w przypadku stosu i kolejki, które implementowane są na bazie listy liniowej.
1
dana
next
Podstawy Programowania
36
37
Operacja usuwania elementu przechowującego określoną
wartość jest zrealizowana w procedurze „delete_node”
posiada ona dwa parametry. Przez pierwszy pobiera
wskaźnik na pierwszy element na liście, natomiast przez
drugi wartość, którą powinien przechowywać element do
usunięcia. Procedura ma również zmienne lokalne, które
pełnią taką samą rolę jak w procedurze „insert_node”. W
wierszu 47 następuje sprawdzenie, czy nie powinien być
usunięty pierwszy element. Jeśli tak, to odbywa się to w
taki sam sposób, jak w przypadku procedury „dequeue”, w
kolejce FIFO. W przeciwnym przypadku, podobnie jak w
„insert_node” przeszukiwana jest iteracyjnie cała lista
(wiersze 55 58). Po wyjściu z pętli zmienna „p” może
mieć wartość „NIL”. Wówczas kończymy procedurę,
bowiem na liście nie ma elementu, który chcielibyśmy
usunąć. Jeśli jednak „p” jest różne od „NIL”, wówczas w
wierszach 60 i 61 element wskazywany przez tę zmienną
jest usuwany z listy. Można to zilustrować następującym
rysunkiem:
end;
end;
38
nowy^.next:=prev^.next;
39
prev^.next:=nowy;
semestr drugi
40 end;
41
42 procedure delete_node(var f:wskaznik; a:integer);
43 var
44 p,prev,tmp:wskaznik;
45 begin
46
47 if f^.dana=a then
48
1. Przed wykonaniem wiersza 60
prev
begin
49
tmp:=f^.next;
50
dispose(f);
51
f:=tmp;
52
exit;
53
end;
54
p:=f;
55
repeat
56
57
p:=p^.next;
until (p=nil) or (p^.dana=a);
59
if p=nil then exit;
60
prev^.next:=p^.next;
61
dispose(p);
dana
next
2. Po wykonaniu wiersza 60
prev
prev:=p;
58
dana
next
dana
next
dana
next
p
dana
next
dana
next
3. Po wykonaniu wiersza 61
prev
dana
next
p
dana
next
p
dana
next
62 end;
63
64 procedure create(var f:wskaznik; a:integer);
65 var
66 nowy:wskaznik;
Procedura „remove” ma tylko jeden parametr. Do jej
wnętrza przekazywany jest przez zmienną wskaźnik na
pierwszy element listy. Usuwa ona wszystkie elementy
listy, z pierwszym elementem włącznie.
67 begin
68
if f=nil then
69
begin
70
new(nowy);
71
nowy^.next:=nil;
72
nowy^.dana:=a;
73
W bloku głównym programu tworzona jest lista, przy
pomocy procedury „create” zawierająca elementy o
wartościach od 1 do 5. Następnie dodawane są do niej
(nadal przy użyciu „create”) elementy o wartościach od 10
do 15. Po wyświetleniu zawartości listy dodawany jest
pojedynczy element o wartości 16, a więc on powinien się
znaleźć na końcu listy, co można sprawdzić po kolejnym
wypisaniu jej na ekran. Następnie do listy dodawany jest
element o wartości 6, który powinien zostać umieszczony
pomiędzy elementami 5 i 10. Lista jest ponownie
wypisywana na ekran i dodawany jest do niej element o
wartości 0, czyli musi on zostać wstawiony przed
pierwszym elementem na liście. W wierszu 112 lista jest
wypisywana na ekran. Powyższe czynności mają na celu
pokazanie, że procedura „insert_node” jest napisana
poprawnie.
Następnie wywoływana jest procedura
„delete_node”, która usuwa z listy element o wartości 0.
f:=nowy;
74
75
Zasada działania procedury „show” jest taka sama, jak
procedury „print_queue”, która służyła do wypisania
zawartości kolejki.
end
else
76
insert_node(f,a);
77 end;
78
79 procedure remove(var f:wskaznik);
80 var
81 tmp:wskaznik;
2
Podstawy Programowania
Aby być pewnym jej działania należałoby dopisać jeszcze jej
wywołania dla ostatniego elementu i elementu znajdującego się
„wewnątrz” listy. Na koniec usuwana jest cała lista i wypisywana
jest dostępna pamięć, celem weryfikacji poprawności działania
procedury „remove”.
81 tmp:wskaznik;
82 begin
83 while f <> nil do
84
begin
85
tmp:=f^.next;
86
dispose(f);
87
f:=tmp;
88
semestr drugi
end;
89 end;
90
91 procedure show(f:wskaznik);
92 begin
93 while f <> nil do
94
begin
95
write(f^.dana:3);
96
f:=f^.next;
97
end;
98
writeln;
99 end;
100
101 begin
102 clrscr;
103 writeln('Dostępna pamięć: ',MemAvail);
104 for ne:=1 to 5 do create(first,ne);
105 for ne:=10 to 15 do create(first,ne);
106 show(first);
107 insert_node(first,16);
108 show(first);
109 insert_node(first,6);
110 show(first);
111 insert_node(first,0);
112 show(first);
113 delete_node(first,0);
114 show(first);
115 remove(first);
116 writeln('Dostępna pamięć: ',MemAvail);
117 readln;
118 end.
3.
Uwagi końcowe
Listy jednokierunkowe można również implementować jako listy z wartownikami. W takim wypadku tworzy się na początku działania
programu jeden lub dwa elementy, które tworzą atrapy. Dzięki nim nie trzeba w procedurach usuwania i wstawiania elementów sprawdzać
dodatkowych warunków. Możliwa jest również implementacja listy jednokierunkowej w oparciu o tablicę dwuwymiarową. W górnym wierszu
tej tablicy mogą być zapisywane wartości elementów, natomiast w dolnym indeksy następnych w kolejności elementów. Taka implementacja
jest jednak mniej wydajna niż implementacja tablicowa, szczególnie trudna do oprogramowania jest w niej operacja wstawiania elementu.
Trzeba też ustalić jaka wartość indeksu będzie oznaczała, że dany element został usunięty z listy lub że jest elementem ostatnim na liście.
Listy mają szerokie zastosowania. Są na przykład, podobnie jak kolejki, używane w systemach operacyjnych przez mechanizmy szeregowania
zadań.
3

Podobne dokumenty