als Word-Dokument - oth

Werbung
Algorithmen und Datenstrukturen
Übung 9: Bayer-Bäume
Bayer-Baumknoten
Die Klasse CBNode zeigt die Implementierung eines Bayer-Baumknotens1. Der Konstruktor dieser
Klasse leistet die Arbeit und bekommt dazu einen Übergabeparameter (M) geliefert, der die Ordnung
des (M-ären) Bayer-Baums beschreibt.
import java.util.*;
/*
* The CBNode class represents on single Node of the Bayer Tree.
*/
public class CBNode
{
// Instanzvariable
protected Vector key, nodeptr, initkey, initvec;
int count;
// Konstruktor
/*
* constructs a single Bayer Tree node with M references to subnodes.
*/
CBNode(int M)
{
// System.out.println("CBNode(): constructor invoked!");
nodeptr = new Vector();
initvec = new Vector();
initvec = null;
key = new Vector();
initkey = new Vector();
initkey = null;
for(int i = 0; i <= M; i++)
{
nodeptr.addElement((Object)initvec);
// System.out.println(i +
"ter Nodepointer erzeugt. Wert: " + nodeptr.elementAt(i));
}
for(int j = 0; j <= M - 1; j++)
{
key.addElement((Object)initkey);
// System.out.println(j + "ter Key erzeugt. Wert: " + key.elementAt(j));
}
count = 0;
// System.out.println("count Wert: " + count);
}
}
Bayer-Baum
Die Klasse CBayerTree umfaßt den Algorithmus2 zum Einfügen und Löschen von Bayer-Baumknoten
1
2
Vgl. PR44200, CBNode.java
Vgl. PR44200, CbayerTree.java
1
Algorithmen und Datenstrukturen
class CBayerTree
{
// protected Instanzvariablen
/*
* Die Variable MAERER definiert die Ordnung des Bayer-Baums,
* d.h. der Nachfolgerknoten bzw. Teilbäume eines Knoten
* (max. MAERER). Jeder Knoten besteht aus MAERER Referenzen
* auf Subknoten und MAERER - 1 Schlüsseln.
*/
protected int MAERER = 5;
/*
* contains the reference to the CVisualizeTree class. It will be set in
* the constructor.
*/
protected CVisualizeTree mpCVisualizeTree;
/*
* contains the reference to the CExtendedCanvas class. It will be set in
* the constructor.
*/
protected CExtendedCanvas mpCExtendedCanvas;
/*
* Referenz zum Wurzelknoten während der Lebensdauer des Baums.
*/
protected CBNode root;
/*
* speichert den neuen Schluessel, der in den baum eingefügt werden soll.
*/
protected Integer newValue;
/*
* Indikator fuer den Wurzelknoten.
*/
private int rootflag = 0;
// Public Operationen
// Der Konstruktor umfasst zwei Paramerte zur Visualisierung
/*
public CBayerTree(CExtendedCanvas pCExtendedCanvas,
CVisualizeTree pCVisualizeTree);
Einfuegen
public int Insert(Integer value);
;
Rueckgabe: -1, falls Fehlversuch; anderenfalls 0
Parameter: einzufuegender Schluessel
Loeschen
public int Delete(Integer delValue);
;
Rueckgabe: -1, falls Fehlversuch; anderenfalls 0
Parameter: einzufuegender Schluessel
public boolean searchkey(int key);
Rueckgabe: true, falls der Schlüssel gefunden wurde; anderenfalls
false
Geschuetzte Methoden
protected CBNode insrekurs(CBNode tempnode, Integer insValue);
Private Methoden
private int delrekurs(CBNode delNode, Integer value);
.....
*/
}
Operationen für Bayer-Bäume
2
Algorithmen und Datenstrukturen
Suchen eines Schlüssels
Gegeben ist der folgende Ausschnitt eines B-Baums mit N Schlüsseln:
+-------------------------------+
|
|
| Z0S1Z1S2 .......... ZN-1SNZN |
|
|
+-------------------------------+
Beim Suchen eines Schlüssels X können folgende Situationen auftreten:
1) S[I] < X < S[I + 1] für 1 <= I <= N
Die Suche wird fortgesetzt in dem Knoten, auf den Z[I]^ zeigt.
2) S[N] < X
Die Suche wird im Knoten fortgesetzt, auf den Z[N]^ zeigt.
3) X < S[1]
Die Suche wird im Knoten fortgesetzt, auf den Z[0]^ zeigt.
Hat einer der angegebenen Referenzen den Wert „null“, dann existiert in dem vorliegenden Baum
kein Teilbaum, der diesen Schlüssel enthält (d.h. die Suche ist beendet).
/*
*Rueckgabe ist true, falls der Schlüssel gefunden wurde;
* anderenfalls false.
*/
public boolean searchkey(int key)
{
boolean found = false;
int i = 0, n;
CBNode node = root;
while(node != null)
{
i = 0;
n = node.count;
// search key in actual node
while(i < n && key > ((Integer)node.key.elementAt(i)).intValue())
{
i++;
}
// end while
if(i < n && key == ((Integer)node.key.elementAt(i)).intValue())
{
found = true;
}
if(node.nodeptr.elementAt(i) != null)
{
mpCVisualizeTree.MoveLevelDown(i);
}
node = (CBNode) node.nodeptr.elementAt(i);
}
// end while (node != null)
return found;
}
// end searchkey Methode
Einfügen
Bsp.: Der Einfügevorgang in einem Bayer-Baum der Klasse 2
1) Aufnahme der Schlüssel 1, 2, 3, 4 in den Wurzelknoten
3
Algorithmen und Datenstrukturen
1
3
2
4
2) Zusätzlich wird der Schlüssel mit dem Wert 5 eingefügt
1
3
2
4
5
Normalerweise würde jetzt rechts von "4" ein neuer Knotem erzeugt. Das würde aber zu einem
Knotenüberlauf führen. Nach dieser Erweiterung enthält der Knoten eine ungerade Zahl von
Elementen ( 2  M  1 ). Dieser große Knoten kann in 2 Söhne zerlegt werden, nur das mittlere
Element verbleibt im Vaterknoten. Die neuen Knoten genügen wieder den B-Baum-Eigenschaften und
könnem weitere Daten aufnehmen.
3
1
2
4
5
Beschreibung des Algorithmus für das Einfügen
Ein neues Element wird grundsätzlich in einen Blattknoten eingefügt. Ist der Knoten mit
Schlüsseln voll, so läuft bei der Aufnahme eines weiteren Schlüssels der Knoten über.
2M
+---------------------------------------------+
|
|
| ...........
SX-1ZX-1SXZX
.....
|
|
|
+---------------------------------------------+
+----------------------------------------------+
|
|
| Z0S1Z1 .... ZM-1SMZMSM+1 ....... Z2MS2M+1
|
|
Überlauf
|
+----------------------------------------------+
Der Knoten wird geteilt:
Die vorderen Schlüssel verbleiben im alten Knoten, der Schlüssel mit der Nummer M+1 gelangt als
Trennschlüssel in den Vorgängerknoten. Die M Schlüssel mit den Nummern M+2 bis 2  M  1
kommen in den neuen Knoten.
+-----------------------------+
| ....SX-1ZX-1SM+1ZYSXZX .... |
+-----------------------------+
+------------------+
|Z0S1 .... ZM-1SMZM|
+------------------+
+-----------------------------+
|ZM+1SM+2ZM+2 ..... S2M+1Z2M+1|
+-----------------------------+
Die geteilten Knoten enthalten genau M Elemente. Das Einfügen eines Elements in der
vorangehenden Seite kann diese ebenfalls zum Überlaufen bringen und somit die Aufteilung
fortsetzen. Der B-Baum wächst demnach von den Blättern bis zur Wurzel.
/*
*Einfügen eines neuen Schlüssels. Rückgabe ist –1, falls
* es misslingt, anderenfalls 0
4
Algorithmen und Datenstrukturen
* Parameter: value
einzufuegender Wert
*/
public int Insert(Integer value)
{
if(root == null)
{
root = new CBNode(MAERER);
root.key.setElementAt((Object)value, 0);
root.count = 1;
} // end if
else
{
if(searchkey(((Integer)value).intValue()) == true)
{
//System.out.println("double key found and will be ignored!");
return -1;
} // end if
CBNode result;
result = insrekurs(root, value);
if(result != null)
{
CBNode node = new CBNode(MAERER);
node.key.setElementAt(newValue, 0);
node.nodeptr.setElementAt(root, 0);
node.nodeptr.setElementAt(result, 1);
node.count = 1;
root = node;
}
// end if(result)
}
// end else
mpCVisualizeTree.DeleteRootKnot();
mpCExtendedCanvas.repaint();
rootflag = 0;
this.drawTree(root);
mpCExtendedCanvas.repaint();
return 0;
}
// end Insert() Methode
Die Methode „Insert“ ruft „insrekurs()“ auf. Diese rekursive Methode leistet die eigentliche Arbeit
/*
* der neue Wert wird rekursiv in den Baum eingebracht
*/
protected CBNode insrekurs(CBNode tempnode, Integer insValue)
{
CBNode result;
result = null;
newValue = insValue;
if(tempnode.nodeptr.elementAt(0) != null) // kein Blatt -> Rekursion
{
int pos = 0;
while(pos < tempnode.count && newValue.intValue() >
((Integer)tempnode.key.elementAt(pos)).intValue())
{
pos++;
} // end while
result = insrekurs((CBNode)tempnode.nodeptr.elementAt(pos), newValue);
if(result == null)
{
return null; // if result = null: nothing has to be inserted into
// node-> finished!
} // end if(resul == null)
} // end if(tempnode.nodeptr.elementAt(0) != null)
5
Algorithmen und Datenstrukturen
// insert a element
CBNode node = null;
int flag = 0;
int s = tempnode.count;
if(s >= MAERER - 1)
// split the knot
{
tempnode.count = (MAERER - 1) / 2;
node = new CBNode(MAERER);
node.count = (MAERER - 1) / 2;
for(int d = ((MAERER - 1) / 2); d > 0;)
{
if(flag != 0 || ((Integer)tempnode.key.elementAt(s - 1)).intValue() >
newValue.intValue())
{
node.nodeptr.setElementAt(tempnode.nodeptr.elementAt(s), d);
node.key.setElementAt(tempnode.key.elementAt(--s), --d);
} // end if(flag != 0 ...
else
{
node.nodeptr.setElementAt(result, d);
node.key.setElementAt(newValue, --d);
flag = 1;
} // end else
}
// end if(s >= MAERER - 1)
if(flag != 0 || ((Integer)tempnode.key.elementAt(s - 1)).intValue() >
newValue.intValue())
{
node.nodeptr.setElementAt(tempnode.nodeptr.elementAt(s), 0);
} // end if
else
{
node.nodeptr.setElementAt(result, 0);
} // end else
} // end if(s >= MAERER - 1)
else
{
tempnode.count++;
} // end else
// shift
for(; s > 0 && ((Integer)tempnode.key.elementAt(s - 1)).intValue() >
newValue.intValue(); s--)
{
tempnode.nodeptr.setElementAt(tempnode.nodeptr.elementAt(s), s + 1);
tempnode.key.setElementAt(tempnode.key.elementAt(s - 1), s);
} // end for
tempnode.key.setElementAt(newValue, s);
tempnode.nodeptr.setElementAt(result, s + 1);
newValue = (Integer) tempnode.key.elementAt((MAERER - 1) / 2);
return node;
} // end insrekurs() methode
Löschen
Grundsätzlich ist zu unterscheiden:
1. Das zu löschende Element ist in einem Blattknoten
2. Das Element ist nicht in einem Blattknoten enthalten.
In diesem Fall ist es durch eines der benachbarten Elemente zu ersetzen. Entlang des rechts
stehenden Zeigers Z ist hier zum Blattknoten hinabzusteigen und das zu löschende Element durch
das äußere linke Element von Z zu ersetzen.
6
Algorithmen und Datenstrukturen
Auf jeden Fall darf die Anzahl der Schlüssel im Knoten nicht kleiner als M werden.
/*
* loescht einen Schlüssel. Rückgabe ist
* anderenfalls 0.
*
* Parameter: zu löschender Wert
*/
public int Delete(Integer delValue)
-1, falls es misslingt;
{
mpCVisualizeTree.MoveToRoot();
if(searchkey(((Integer)delValue).intValue()) == false)
{
return -1;
} // end if
if(delrekurs(root, delValue) == 0)
{
CBNode temp = root;
root = (CBNode)root.nodeptr.elementAt(0);
temp = null;
} // end if
mpCVisualizeTree.DeleteRootKnot();
mpCExtendedCanvas.repaint();
rootflag = 0;
this.drawTree(root);
mpCExtendedCanvas.repaint();
return 0;
} // end Delete() Methode
Die eigentliche Arbeit beim Löschen übernimmt die rekursive Methode „delrekurs()“
/*
* deletes the value rekursively from the tree.
*
*/
private int delrekurs(CBNode delNode, Integer value)
{
int result = 0, pos, found = 0;
Vector nullVec = new Vector();
nullVec = null;
if(delNode != null)
{
CBNode node;
for(pos = 0; pos < delNode.count; pos++)
{
if(value.intValue() <=
((Integer)delNode.key.elementAt(pos)).intValue())
{
if(value.intValue() <
((Integer)delNode.key.elementAt(pos)).intValue())
{
result = delrekurs((CBNode)delNode.nodeptr.elementAt(pos),
(Integer)value);
} // end if
else
{
if(delNode.nodeptr.elementAt(0) == null)
// leaf node!
{
for(; (pos + 1) < delNode.count; pos++)
{
7
Algorithmen und Datenstrukturen
delNode.key.setElementAt(delNode.key.elementAt(pos + 1), pos);
} // end for
//System.out.println("Ende weil Blatt!");
return --delNode.count;
} // end if
else
{
node = (CBNode)delNode.nodeptr.elementAt(pos + 1);
while(node.nodeptr.elementAt(0) != null)
{
node = (CBNode)node.nodeptr.elementAt(0);
} // end while
delNode.key.setElementAt(node.key.elementAt(0), pos);
//System.out.println("Tausch, loesche rechts");
pos++;
result = delrekurs((CBNode)delNode.nodeptr.elementAt(pos),
(Integer)delNode.key.elementAt(pos - 1));
} // end else
} // end else
found = 1;
break;
} // end if
} // end for
if(found == 0)
{
result = delrekurs((CBNode)delNode.nodeptr.elementAt(pos),
(Integer)value);
} // end if
if(result < ((MAERER - 1) / 2))
{
CBNode temp;
int l = 0, r = 0;
if(pos > 0)
{
l = ((CBNode)delNode.nodeptr.elementAt(pos - 1)).count;
} // end if
if(pos < delNode.count)
{
r = ((CBNode)delNode.nodeptr.elementAt(pos + 1)).count;
} // end if
node = (CBNode)delNode.nodeptr.elementAt(pos);
if(l > (MAERER - 1) / 2)
// steal a key from left
{
//System.out.println("klaue einen Schluessel von links");
temp = (CBNode)delNode.nodeptr.elementAt(pos - 1);
for(int i = node.count; i > 0; i--)
{
node.key.setElementAt(node.key.elementAt(i - 1), i);
node.nodeptr.setElementAt(node.nodeptr.elementAt(i), i + 1);
} // end for
node.nodeptr.setElementAt(node.nodeptr.elementAt(0), 1);
node.count++;
node.key.setElementAt(delNode.key.elementAt(pos - 1), 0);
node.nodeptr.setElementAt(temp.nodeptr.elementAt(temp.count), 0);
delNode.key.setElementAt(temp.key.elementAt(--temp.count), pos - 1);
} // end if
else
{
if(r > (MAERER - 1) / 2)
// steal a key from right
{
// System.out.println("klaue Schluessel von rechts");
temp = (CBNode)delNode.nodeptr.elementAt(pos + 1);
node.key.setElementAt(delNode.key.elementAt(pos), node.count);
8
Algorithmen und Datenstrukturen
node.count++;
node.nodeptr.setElementAt(temp.nodeptr.elementAt(0), node.count);
delNode.key.setElementAt(temp.key.elementAt(0), pos);
for(int i = 1; i < temp.count; i++)
{
temp.key.setElementAt(temp.key.elementAt(i), i - 1);
temp.nodeptr.setElementAt(temp.nodeptr.elementAt(i), i - 1);
} // end for
temp.nodeptr.setElementAt(temp.nodeptr.elementAt(temp.count),
temp.count - 1);
temp.count--;
} // end if
else
{
//System.out.println("Knoten verschmelzen !");
if(r == 0)
{
pos--;
} // end if
node = (CBNode)delNode.nodeptr.elementAt(pos);
temp = (CBNode)delNode.nodeptr.elementAt(pos + 1);
node.key.setElementAt(delNode.key.elementAt(pos),
node.count++);
node.nodeptr.setElementAt(temp.nodeptr.elementAt(0), node.count);
for(int i = 0; i < temp.count; i++)
{
node.key.setElementAt(temp.key.elementAt(i), node.count++);
node.nodeptr.setElementAt(temp.nodeptr.elementAt(i + 1),
node.count);
} // end for
// System.out.println(
//
"Jetzt kann der Knoten rechts daneben entfernt werden!");
delNode.count--;
// delNode.nodeptr.elementAt(pos + 1) = null; // geht das so?
delNode.nodeptr.setElementAt((Object)nullVec, pos + 1);
while(pos < delNode.count)
{
delNode.key.setElementAt(delNode.key.elementAt(pos + 1), pos);
pos++;
delNode.nodeptr.setElementAt(delNode.nodeptr.elementAt(pos + 1),
pos);
} // end while
} // end else
} // end else
} // end if
return delNode.count;
} // end if
else
{
return 0;
} // end else
} // end delrekurs() Methode
9
Herunterladen