Theorieblatt 12

Werbung
• Schlüssel sind geordnet gespeichert. Für jeden Knoten gilt :
– Schlüssel im linken Teilbaum von p sind kleiner als der Schlüssel von p
– Schlüssel im rechten Teilbaum von p sind grösser als der Schlüssel von p
Informatik I - Übung 12
Bäume und Rekursion
Daniel Hentzen
[email protected]
21. Mai 2014
• Aufwand bei der Suche nach einem Wert :
– best case : 1
– worst case : log2 (#Blattknoten)
1 Bäume
1.1 Allgemein
1.2.1 Implementierung : als array
• Bäume sind verallgemeinerte Listenstrukturen : Ein Element (node) hat eine endliche,
begrenzte Anzahl von Nachfolgern
Wir können die Elemente ebenenweise in einem Array abspeichern. Um die Positionen der
Arrayeinträge im Baum zu bestimmen, benutzen wir folgende Beziehungen :
• Jeder Knoten ausser dem Wurzelknoten (root) hat genau einen Vorgängerknoten
(parent)
parent(i) =
• Eine Kante drückt die Beziehung zwischen unmittelbarem Vorgänger und Nachfolger aus
• Der Grad eines Knoten ist die Zahl seiner unmittelbarer Nachfolger
i−1
2
(1)
lef tson(i) = 2 · i + 1
(2)
rightson(i) = 2 · i + 2
(3)
• Ein Blattknoten (leaf) ist ein Knoten ohne Nachfolger, also mit Grad 0.
• Höhe eines Baumes: Maximaler Abstand (Anzahl Kanten) eines Blattes von der Wurzel
Der obige Binärbaum ist ebenenweise im Array gespeichert :
27
0
10
1
32
2
3
3
Beispiel :
• Parent von 12 :
parent(4) =
4−1
= 1.5 → 1 → 10
2
• Linker Sohn von 10 :
1.2 Binärbäume
parent(4) = 2 · 1 + 1 = 3 → 3
• Spezialfall : Grad = 2, das heisst jeder Knoten hat maximal 2 Nachfolger
• Rechter Sohn von 10 :
• Vollständiger Binärbaum: Alle Blätter haben die gleiche Tiefe
→ Höhe = log2 (#Blattknoten)
parent(4) = 2 · 1 + 2 = 4 → 12
1
12
4
28
5
34
6
Beispiel : aufsteigend (rekursiv!)
v o i d traverse ( Node ∗ p ) // S t a r t p u n k t u e b e r g e b e n
{
i f ( p != NULL ) //wenn k e i n B l a t t k n o t e n
{
traverse ( p−>left ) ;
cout << p−>value << ” ” ;
traverse ( p−>right ) ;
}
}
1.2.2 Implementierung : als struct
s t r u c t element
{
i n t value ;
s t r u c t element ∗ left ; // p o i n t e r a u f r e c h t e n N a c h f o l g e r
s t r u c t element ∗ right ; // p o i n t e r a u f l i n k e n N a c h f o l g e r
};
t y p e d e f s t r u c t element Node ;
Node ∗ root = NULL ; // p o i n t e r a u f Wurzel
1.4 Suchen...
1.4.1 ...rekursiv (ungeordnet/geordnet)
1.3 Durchlaufordnungen
Wir suchen nach i und übergeben den Wurzelknoten als Argument. Rückgabe ist true falls sich
der Wert im Baum befindet, false falls nicht.
Man kann die Binärbäume in verschiedenen Reihenfolgen durchlaufen. Achtung : Man geht
rekursiv vor, das heisst auf die Teilbäume wendet man wieder die gleiche Reihenfolge an, bis
man bei den Blattknoten ankommt.
b o o l rec_search ( Node ∗ node , i n t i )
{
i f ( node == NULL )
return f a l s e ;
i f ( node−>value == i )
return true ;
r e t u r n rec_search ( node−>left , i ) | | rec_search ( node−>right , i ) ;
}
1.3.1 Hauptreihenfolge (“preorder”)
Wurzel, linker Teilbaum, rechter Teilbaum
Obiges Beispiel : 27, 10, 3, 12, 32, 28, 34
1.4.2 ...iterativ (Binärbaum geordnet!)
1.3.2 Nebenreihenfolge (“postorder”)
linker Teilbaum, rechter Teilbaum, Wurzel
Node ∗ next = root ;
i n t search = 6 ;
w h i l e ( next != NULL && next−>value != search )
{
i f ( next != NULL && next−>value != search )
{
i f ( next−>value < search )
next = next−>right ;
else
next = next−>left ;
}
}
Obiges Beispiel : 3, 12, 10, 28, 34, 32, 27
1.3.3 Symmetrische Reihenfolge (“inorder”)
linker Teilbaum, Wurzel, rechter Teilbaum
Beim geordneten Binärbaum gibt dies die Elemente aufsteigend aus.
(absteigend : rechter Teilbaum, Wurzel, linker Teilbaum)
Obiges Beispiel : 3, 10, 12, 27, 28, 32, 34
2
1.6 Gesamten Baum löschen
1.5 Einfügen (in geordneten Binärbaum)
Wir wollen einen Knoten mit dem Wert value in den geordneten Binärbaum einfügen (als
Blattknoten). Dafür müssen wir zuerst die richtige Position im Baum finden.
v o i d deleteTree ( Node ∗ node )
{
i f ( node != NULL )
{
deleteTree ( node−>right ) ;
deleteTree ( node−>left ) ;
d e l e t e node ;
}
}
2 Sortieralgorithmen
Sortieralgortihmen sortieren eine Liste von Werten (arrays). Es gibt viele verschiedene Varianten, die mehr oder weniger effizient sind.
v o i d insert ( Node ∗ node , i n t value )
{
i f ( node−>value == value )
return ;
i f ( value < node−>value )
{
i f ( node−>left != NULL ) // k e i n B l a t t k n o t e n
insert ( node−>left , value ) ;
e l s e // B l a t t k n o t e n e r r e i c h t
{
Node ∗ newNode = new Node ;
newNode−>left = NULL ;
newNode−>right = NULL ;
newNode−>value = value ;
node−>left = newNode ;
}
}
else
{
i f ( node−>right != NULL )
insert ( node−>right , value ) ;
else
{
Node ∗ newNode = new Node ;
newNode−>left = NULL ;
newNode−>right = NULL ;
newNode−>value = value ;
node−>right = newNode ;
}
2.1 Selection Sort
Vorgehen : Das kleinste Element wird gesucht und mit dem ersten Element der Liste vertauscht. Der Algorithmus wird ohne die bereits geordneten Elemente wiederholt, solange bis
man am Ende der Liste ankommt.
Algorithmus :
Die Liste wird als array übergeben. length ist die Anzahl Elemente in der Liste.
v o i d selectionSort ( i n t ∗ array , i n t length )
{
i n t i , j , min , minat ;
f o r ( i =0; i<(length −1) ; i++)
{
minat = i ; // Index vom Minimum
min = array [ i ] ; // Wert vom Minimum
f o r ( j=i +1; j<length ; j++) // a k t u e l l e s Minimum v e r g l e i c h e n
{
i f ( min>array [ j ] ) // f a l l s n e u e s Minimum g e f u n d e n
{
minat = j ; // Index w e c h s e l n
min = array [ j ] ; // Wert w e c h s e l n
}
}
//Minimum mit 1 . Wert d e r u n g e o r d n e t e n L i s t e v e r t a u s c h e n
i n t temp = array [ i ] ;
array [ i ] = array [ minat ] ;
}
}
3
array [ j ] = array [ j − 1 ] ;
array [ j −1] = tmp ;
j−−;
array [ minat ] = temp ;
}
}
}
}
}
2.2 Bubble Sort
Vorgehen : Sortieren durch wiederholtes Vergleichen und eventuelles Vertauschen benachbarter array-Elemente. Nach dem ersten Durchlauf ist das grösste Element an der richtigen Position.
Algorithmus :
2.4 Merge Sort
Die Liste wird als array übergeben. length ist die Anzahl Elemente in der Liste.
Vorgehen : “Divide and conquer”: Mergesort zerlegt die zu sortierende Liste in kleinere Listen
(solange bis die Sublisten nur noch 1 Element haben = sortiert), die jede für sich sortiert
werden. Die sortierten kleinen Listen werden dann im Reissverschlussverfahren zu grösseren
Listen zusammengefügt, bis wieder eine sortierte Gesamtliste erreicht ist.
v o i d bubbleSort ( i n t ∗ array , i n t length )
{
f o r ( i n t i=length −1; i >0; i−−) // o u t e r l o o p
{
f o r ( i n t j =1; j<=i ; j++) // i n n e r l o o p
{
i f ( array [ j −1] > array [ j ] )
{
i n t t = array [ j − 1 ] ;
array [ j −1] = array [ j ] ;
array [ j ] = t ;
}
}
}
}
Pseudo-Code :
funktion mergesort ( liste ) ;
falls ( Groesse von liste <= 1 ) dann antworte liste
sonst
halbiere die liste in linkeListe , rechteListe
linkeListe = mergesort ( linkeListe )
rechteListe = mergesort ( rechteListe )
antworte merge ( linkeListe , rechteListe )
funktion merge ( linkeListe , rechteListe ) ;
neueListe
solange ( linkeListe und rechteListe nicht leer )
|
falls ( erstes Element der linkeListe <= erstes Element der ←rechteListe )
|
dann fuege erstes Element linkeListe in die neueListe ←hinten ein und entferne es aus linkeListe
|
sonst fuege erstes Element rechteListe in die neueListe ←hinten ein und entferne es aus rechteListe
solange_ende
solange ( linkeListe nicht leer )
|
fuege erstes Element linkeListe in die neueListe hinten ein←und entferne es aus linkeListe
solange_ende
solange ( rechteListe nicht leer )
|
fuege erstes Element rechteListe in die neueListe hinten ←ein und entferne es aus rechteListe
solange_ende
antworte neueListe
2.3 Insertion Sort
Vorgehen : Ein Element nach dem anderen in der Liste wird betrachtet. Man sucht seine
Position in der bereits sortierten Liste der vorhergehenden Elemente und fügt es ein. Die nachfolgenden Elemente müssen verschoben werden.
Algorithmus :
Die Liste wird als array übergeben. length ist die Anzahl Elemente in der Liste.
v o i d insertionSort ( i n t ∗ array , i n t length )
{
i n t i , j , tmp ;
f o r ( i =1; i<length ; i++)
{
j=i ;
w h i l e ( j>0 && array [ j−1]> array [ j ] )
{
tmp = array [ j ] ;
4
2.5 Quick Sort
Vorgehen : “Divide and conquer” : ein Element als Pivot wählen, alle Elemente kleiner als
der Pivot vor dem Pivot, alle Elemente grösser als der Pivot nach dem Pivot. (Pointer i ganz
links, pointer j ganz rechts, Durchlaufe array von links (i++) bis a[i]>=Pivotelement. Durchlaufe array von rechts (j–) bis a[j]<=Pivotelement. Vertausche Elemente i und j. )Nach dieser
Partitionierung ist das Pivotelement an der richtigen Position in der sortierten Liste. Rekursiv
die linke und rechte Subliste nach dem gleichen Prinzip partitionieren.
2.6 Effizienz
Algorithm
Selection
Bubble
Merge
Quick
Worst Case
Best Case
Average Case
O(n2 )
O(n2 )
O(n2 )
O(n2 )
O(n)
O(n2 )
O(n · log(n))
O(n · log(n))
O(n · log(n))
O(n2 ))
O(n · log(n))
O(n · log(n))
5
Herunterladen