Integraler Datenyp - oth

Werbung
Algorithmen und Datenstrukturen
Übung 13: Heap
Die Klasse BinaryHeap in Java1
Heap-Ordnung
Falls das kleinste Element am schnellsten gefunden werden soll, steht sinnvollerweise das kleinste
Element an der Spitze des Heap. In einem Heap sind dann immer die Daten der Vorgängerknoten
kleiner als die der Nachfolgeknoten.
Grundlegende Heap-Operationen
Einfügen
Eine Lücke wird geschaffen. Wenn das einzufügende Element ohne Verletzung der Heap-Ordnung in
die Lücke in die Lücke passt, dann ist alles bereits erledigt. Anderenfalls ist das Element, das den
Vorgänger zur Lücke markiert, mit dem Element, das in die Lücke aufgenommen werden soll, zu
tauschen, z.B. Aufnahme des Schlüssels mit dem Wert 14 in den Heap.
13
13
21
16
24
65
31
26
32
19
21
68
24
14
65
16
14
26
32
19
68
31
13
14
16
24
65
21
26
32
19
68
31
Die Strategie, die bei dem vorliegenden Einfügen verfolgt wird, ist ein aufwärts gerichtetes
Durchdringen bis ein geeigneter Punkt gefunden wird.
public void insert( Comparable x ) throws Overflow
{
if (istVoll( ) ) throw new Overflow( );
// Durchdringen nach oben
int luecke = ++aktGroesse;
for ( ; luecke > 1 && x.compareTo(feld[luecke / 2 ] ) < 0; luecke /= 2 )
1
Pr13228
1
Algorithmen und Datenstrukturen
feld[ luecke ] = feld[ luecke / 2 ];
feld[ luecke ] = x;
}
Der zeitliche Aufwand liegt bei O(log N), falls das einzufügende Element das Minimum ist und bis zur
Wurzel durchdringen muß.
Löschen des kleinsten Werts
Das Löschen des kleinsten Element an der Spitze bedingt ein ähnliches Vorgehen wie beim Einfügen.
Falls der kleinste Schlüsselwert entfernt wird, entsteht eine Lücke, die von den nachfolgenden
Schlüsseln geschlossen werden muß, z.B.:
13
31
14
16
24
65
21
26
32
14
19
68
24
31
65
16
21
26
32
14
31
16
65
21
26
68
13
14
24
19
19
21
68
24
32
65
16
31
26
19
68
32
14
21
16
24
65
31
26
19
68
32
Die Strategie ist ein abwärts gerichtetes Durchdringen. Im schlimmsten Fall liegt der Aufwand bei
O(log N).
public Comparable loescheMin( )
2
Algorithmen und Datenstrukturen
{
if ( isEmpty( ) ) return null;
Comparable minItem = findMin( );
feld[ 1 ] = feld[ aktGroesse-- ];
durchdringeRunter( 1 );
return minItem;
}
private
{
/* 1*/
/* 2*/
/* 3*/
/*
/*
/*
/*
/*
/*
4*/
5*/
6*/
7*/
8*/
9*/
/*10*/
/*11*/
void durchdringeRunter( int luecke )
int kind;
Comparable tmp = feld[ luecke ];
for( ; luecke * 2 <= aktGroesse; luecke = kind )
{
kind = luecke * 2;
if ( kind != aktGroesse &&
feld[ kind + 1 ].compareTo( feld[ kind ] ) < 0 )
kind++;
if( feld[ kind ].compareTo( tmp ) < 0 )
feld[ luecke ] = feld[ kind ];
else
break;
}
feld[ luecke ] = tmp;
}
}
Andere Heap-Operationen
macheHeap(): N Elemente werden in einen leeren Heap platziert. Das kann über N
aufeinanderfolgende Einfügevorgänge geschehen. Da jedes Einfügen im Schnitt zwischen O(1) und
O(log N) Aufwendungen benötigt, liegt die totale Laufzeit des Algorithmus bei O(N), im schlechtesten
Fall bei O(NlogN).
Generell sind N Elemente in dem Baum gemäß der einem Heap zugeordneten Struktur-Eigenschaft
anzuordnen. Bewerkstelligen tut dies durchdringeRunter(i). Die Methode durchdringt vom
Knoten i aus das Feld und erzeugt einen im Sinne eines Heap geordneten Baum.
private void macheHeap( )
{
for (int i = aktGroesse / 2; i > 0; i-- )
{
durchdringeRunter( i );
}
}
Bsp.: Im folgenden Beispiel ist der Baum nicht im Sinne eines Heap geordnet. Die aktuelle Belegung
des Felds mit Elementen liegt bei 15. Aufgerufen wird im ersten Schritt zur Herstellung der HeapEigenschaft durchdringeRunter(7). Es ergibt sich bis zum Erreichen eines vollständig
eingerichteten Heap:
3
Algorithmen und Datenstrukturen
150
80
30
100
40
10
20
90
70
60
50
110
120
140
130
Abb.: Ausgangsituation
150
80
30
100
40
10
20
90
70
60
50
110
120
140
130
Abb.: Nach Aufruf von durchdringeRunter(7)
150
80
30
100
40
10
20
90
50
60
70
110
120
140
130
Abb.: Nach Aufruf von durchdringeRunter(6)
4
Algorithmen und Datenstrukturen
150
80
30
100
40
10
20
90
50
60
70
110
120
140
130
Abb.: Nach Aufruf von durchdringeRunter(5)
150
80
20
100
40
10
30
90
50
60
70
110
120
140
130
Abb.: Nach Aufruf von durchdringeRunter(4)
150
80
20
100
40
10
30
90
50
60
70
110
120
140
130
Abb.: Nach Aufruf von durchdringeRunter(3)
5
Algorithmen und Datenstrukturen
150
10
20
100
40
60
30
90
50
80
70
110
120
140
130
Abb.: Nach Aufruf von durchdringeRunter(2)
10
20
30
100
40
60
150
90
50
80
70
110
120
140
130
Abb.: Nach Aufruf von durchdringeRunter(1)
Zur Berechnung der Laufzeit von macheHeap() muß die Anzahl der gestrichelten Linien festgestellt
werden. Das kann durch Bestimmung der Summe aller Höhen der Knoten im Heap geschehen. Das
wäre die maximale Größe von gestrichelten Linien.
Ein perfekt ausgeglichener Binärbaum der Höhe h umfasst 2 h+1-1 Knoten, die Summe der Höhen der
Knoten liegt bei 2h-1-(h+1). Aus dieser Summe folgt für das Leistungsverhalten O(N), wobei N die
Anzahl der Knoten ist.
6
Algorithmen und Datenstrukturen
Lösungen
// BinaryHeap class
//
// Erzeugung mit optionaler Angabe zur Kapazitaet (Defaultwert: 100)
//
// ******************PUBLIC OPERATIONEN**********************************
// void insert( x )
--> Einfuegen x
// Comparable loescheMin( )--> Rueckgabe und entfernen des kleinsten
//
Elements
// Comparable findMin( )
--> Rueckgabe des kleinsten Elements
// boolean isEmpty( )
--> Rueckgabe: true, falls leer; anderenfalls
//
false
// boolean isFull( )
--> Rueckgabe true, falls voll; anderenfalls
// false
// void makeEmpty( )
--> Entfernen aller Elemente
// ******************ERRORS*******************************************
// Auswurf Overflow, Falls die Kapazitaet ueberschritten wird
public class BinaryHeap
{
/*
* Implementiert einen binaeren Heap.
* Vergleiche benutzen die Methode compareTo.
*/
// Konstante, Variable
private static final int DEFAULT_CAPACITY = 100;
private int aktGroesse;
// Anzahl der elemente im Heap
private Comparable [ ] feld;
// Der Heap-Container
// Konstruktoren
/*
* Erzeugen binaeren Heap.
*/
public BinaryHeap( )
{
this(DEFAULT_CAPACITY);
}
/*
* Erzeugen binaeren Heap.
* Parameter: Kapazitaet des binaeren heap.
*/
public BinaryHeap(int capacity )
{
aktGroesse = 0;
feld = new Comparable[ capacity + 1 ];
}
public BinaryHeap(int [] x)
{
this(x.length);
for (int i = 0; i < x.length; i++)
{
feld[++aktGroesse] = new Integer(x[i]);
}
System.out.println("Feld ohne Heap-Ordnung");
for (int i = 1; i < aktGroesse; i++)
System.out.println(feld[i] + " ");
macheHeap();
System.out.println("Feld mit Heap-Ordnung");
for (int i = 1; i < aktGroesse; i++)
System.out.println(feld[i] + " ");
}
// Methoden
/*
* Einfuegen in die prioritaetsgesteuerte Warteschlange
7
Algorithmen und Datenstrukturen
* unter Wahrung der Heap-Order.
* Duplikate sind zugelassen.
* Parameter x enthaelt das einzufuegende Element.
* Exception, falls der Behaelter voll ist.
*/
public void insert( Comparable x ) throws Overflow
{
if (istVoll( ) ) throw new Overflow( );
// Durchdringen nach oben
int luecke = ++aktGroesse;
for ( ; luecke > 1 && x.compareTo(feld[luecke / 2 ] ) < 0; luecke /= 2 )
feld[ luecke ] = feld[ luecke / 2 ];
feld[ luecke ] = x;
}
/*
* Finde das kleinste Element in der prioritaetsgesteuerten
* Warteschlange.
* Rueckgabe: kleinstes Element, oder null, falls leer.
*/
public Comparable findMin( )
{
if( isEmpty( ) ) return null;
return feld[ 1 ];
}
/*
* Entferne das kleinste Element aus der prioritaetsgest. Warteschlange.
* Rueckgabe: das kleinste Element, oder null, falls leer.
*/
public Comparable loescheMin( )
{
if ( isEmpty( ) ) return null;
Comparable minItem = findMin( );
feld[ 1 ] = feld[ aktGroesse-- ];
durchdringeRunter( 1 );
return minItem;
}
/*
* Einrichten der Heap-Ordnung aus einer gegebenen Menge von Merkmalen
* Ablaufgeschw. linear mit der Zeit.
*/
private void macheHeap( )
{
for (int i = aktGroesse / 2; i > 0; i-- )
{
durchdringeRunter( i );
}
}
/*
* Test ob die prioritaetsgesteuerte Schlange logisch leer ist the
priority queue is logically empty.
* Rueckgabe: true, falls leer, andernfalls false.
*/
public boolean isEmpty( )
{
return aktGroesse == 0;
}
/*
* Test, ob die prioritaetsgest. Schlange voll ist.
* Rueckgabe true, falls voll, anderenfalls false.
*/
public boolean istVoll( )
{
return aktGroesse == feld.length - 1;
8
Algorithmen und Datenstrukturen
}
/*
* Mache die prioritaetsgest. Schlange leer
*/
public void makeEmpty( )
{
aktGroesse = 0;
}
// Private Methoden
/*
* Interne Methode zum Durchdringen nach unten in den Heap.
* Parameter luecke: Index an dem das Durchdringen beginnt.
*/
private void durchdringeRunter( int luecke )
{
/* 1*/ int kind;
/* 2*/ Comparable tmp = feld[ luecke ];
/* 3*/ for( ; luecke * 2 <= aktGroesse; luecke = kind )
{
/* 4*/
kind = luecke * 2;
/* 5*/
if ( kind != aktGroesse &&
/* 6*/
feld[ kind + 1 ].compareTo( feld[ kind ] ) < 0 )
/* 7*/
kind++;
/* 8*/
if( feld[ kind ].compareTo( tmp ) < 0 )
/* 9*/
feld[ luecke ] = feld[ kind ];
else
/*10*/
break;
}
/*11*/ feld[ luecke ] = tmp;
}
}
9
Herunterladen