📄 bplustree.java
字号:
/** By Kristi Thompson and Sean McLaughlin
*
* April 10 & 11, 1997
*
* CISC 235 - Information Structures (Winter 1997) taught by David Alex Lamb
*
* Dept of Computer Science
* Queen's University, Kingston
*
* kristi@Seanster.com , Seanster@Seanster.com
*
* http://www.Seanster.com/BplusTree
*
* Feel free to copy and modify this applet, but please give credit
* to the original author where appropriate
*
* This applet was inspired by and partly based upon the BST Search Tree Applet
* by James Stewart.
*
* http://www.dgp.toronto.edu/people/JamesStewart/applets/bst/bst-property.html
*
*/
import java.util.*;
import java.awt.*;
/** A BplusTree
Uses the following classes:
INode (Inner nodes, which hold keys and pointers to other nodes
Bucket (leaf nodes that keys and their associated data)
Node (abstract superclass for INode and Bucket)
*/
public class BplusTree extends java.applet.Applet
{
// DEFINES
static final int N = 2;
static final int Ndiv2 = 1;
boolean PROMOTION = true;
boolean NO_PROMOTION = false;
final String TREENAME = "BplusTree";
final Color LineColor = Color.black;
final Color HighlightColor = Color.orange;
final Color BackgroundColor = Color.lightGray;
static final int BRANCH_SPACING = (int) (Math.max( Bucket.BUCKET_HEIGHT, INode.INODE_HEIGHT) * 1.5); // When a new level is added to the tree, move everyone down this much
boolean GetParms = true;
int root_X_pos = 100;
int root_Y_pos = 50;
// DEFINES
Node root = null; // root of tree
Bucket first_Bucket = null; // For sequential Access (Fully Implemented)
Insert_T insert; // thread to handle insertions
int newKey;
String newData;
// Things having to do with appearance:
Font f = new Font( "TimesRoman", Font.PLAIN, 14 );
FontMetrics fm = getFontMetrics( f );
TextField keyField;
TextField dataField;
Button insertB;
Button repaintB;
Button clearB;
Label keyLabel;
Label dataLabel;
Image buffer_image;
Graphics buffer_graphics;
Dimension buffer_size;
///////////////////////////////////////////////////////
/**
* Init the applet. Just insert elements into the tree and
* compute the tree's initial position.
*/
public void init() {
setLayout(null);
root_X_pos = bounds().x + ((bounds().width - Bucket.BUCKET_WIDTH - 25) / 2);
root_Y_pos = 60;
// Set the Applett Background
setBackground(BackgroundColor);
keyField = new TextField("", 5);
dataField = new TextField("", 15);
// Get the applet parameters/build tree
if ( GetParms != false)
{
parse_parameters();
}
// First, set up text fields and buttons
//setLayout(new FlowLayout(FlowLayout.LEFT));
insertB = new Button("Insert");
add(insertB);
insertB.setForeground(Color.yellow);
repaintB = new Button("Repaint");
add(repaintB);
repaintB.setForeground(Color.green);
clearB = new Button("Clear");
add(clearB);
clearB.setForeground(Color.orange);
add( keyLabel = new Label("Key:", Label.RIGHT));
add(keyField);
keyLabel.setForeground(Color.red);
add( dataLabel = new Label("Data:", Label.RIGHT));
add(dataField);
dataLabel.setForeground(Color.black);
insertB.reshape( 20, 20, 50, 25 );
keyLabel.reshape( 70, 20, 40, 25 );
keyField.reshape( 110, 20, 40, 25 );
dataLabel.reshape( 150, 20, 40, 25 );
dataField.reshape( 190, 20, 50, 25 );
repaintB.reshape( 20, 50, 50, 25 );
clearB.reshape( bounds().x + bounds().width - 70, 20, 50, 25 );
Layout_Tree();
}
/**
* Upon starting, start any threads that were previously stopped
*/
public void start()
{
if (insert != null && insert.isAlive()) insert.resume();
}
/**
* Upon stopping, stop all active threads
*/
public void stop()
{
if (insert != null && insert.isAlive()) insert.suspend();
}
/**
* Upon a `paint', just redraw the tree.
*/
public void paint( Graphics g )
{
// Draw A nice border
g.setColor(Color.black);
g.drawRect( 0, 0, bounds().width - 1, bounds().height - 1);
g.setColor(Color.blue);
g.drawRect( 1, 1, bounds().width - 3, bounds().height - 3);
g.drawRect( 2, 2, bounds().width - 5, bounds().height - 5);
g.setColor(Color.black);
g.drawRect( 3, 3, bounds().width - 7, bounds().height - 7);
}
/**
* Use update() to do double buffering.
*/
public synchronized void update( Graphics g )
{
if (buffer_image == null)
{
buffer_size = size();
buffer_image = createImage( buffer_size.width, buffer_size.height );
buffer_graphics = buffer_image.getGraphics();
buffer_graphics.setFont( getFont() );
}
buffer_graphics.setColor( getBackground() );
buffer_graphics.fillRect( 0, 0, buffer_size.width, buffer_size.height );
paint( buffer_graphics );
g.drawImage( buffer_image, 0, 0, null );
}
/**
* Upon a mouseDown, colour the two nodes involved, but
* don't rotate.
*/
public boolean mouseDown( Event evt, int x, int y )
{
/*
// Ignore mouse if rotations are not allowed
if (! allow_rotation)
return false;
// Ignore mouse if still rotating
if (rotate_thread != null && rotate_thread.isAlive())
return false;
// Find node closest to mouse
rotation_node = find_closest_node( root, x, y );
// Highlight rotation node and its parent
if (rotation_node != null) {
rotation_node.highlight_node = true;
rotation_node.parent.highlight_node = true;
repaint();
}
*/
return true;
}
/**
* Upon a mouseUp, rotate the two nodes. This involves spawning
* a thread that will slowly move the nodes.
*/
public boolean mouseUp( Event evt, int x, int y )
{
/*
// Ignore mouse if still rotating
if (rotate_thread != null && rotate_thread.isAlive())
return false;
// Ignore mouse no node selected for rotation
if (rotation_node == null)
return true;
// Start rotating
rotation_node.highlight_node = false;
rotation_node.parent.highlight_node = false;
rotate_thread = new rotate(this);
rotate_thread.start();
*/
return true;
}
/**
* Handle an action from one of the UI components.
*/
public boolean action( Event evt, Object arg )
{
if (arg.equals("Insert"))
{
try
{
newKey = Integer.parseInt( keyField.getText().trim() );
newData = dataField.getText().trim();
}
catch (NumberFormatException e)
{
play(getCodeBase(), "audio/error.au");
System.out.println( "ERROR in " + TREENAME + " applet: `Key' value must be an integer" );
return true;
}
// Ignore if still inserting
if (insert != null && insert.isAlive())
return true;
// Start rotating
insert = new Insert_T (this);
insert.start();
play(getCodeBase(), "audio/insert.au");
return true;
}
else if (arg.equals("Clear"))
{
// Ignore if still inserting
if (insert != null && insert.isAlive())
return true;
// Wipe Tree
play(getCodeBase(), "audio/clear.au");
removeAll(); // Heh heh must implelemt children deleting themselves :)
root = null;
GetParms = false;
init();
GetParms = true;
return true;
}
else if (arg.equals("Repaint"))
{
play(getCodeBase(), "audio/repaint.au");
Layout_Tree();
return true;
}
else
{
System.out.println( "ERROR in BST applet: Unrecognized UI event" );
}
return true;
}
/**
* Parse the parameters supplied with the applet. These are listed at the
* top of this file.
*/
void parse_parameters()
{
int tempInt;
String tempStr;
String elements;
String defaults;
defaults = getParameter( "defaults" );
if (defaults != null)
{
StringTokenizer t = new StringTokenizer( defaults, " \t\n\r," );
if (t.hasMoreTokens()) keyField.setText( t.nextToken() );
if (t.hasMoreTokens()) dataField.setText( t.nextToken() );
}
// Read elements - int key, string data
elements = getParameter( "elements" );
if (elements != null)
{
StringTokenizer t = new StringTokenizer( elements, " \t\n\r," );
while (t.hasMoreTokens())
{
try
{
tempInt = Integer.parseInt(t.nextToken());
}
catch (NumberFormatException e)
{
System.out.println( "ERROR in " + TREENAME + " applet: Invalid Element List: Key value must be an integer." );
return;
}
if ( t.hasMoreTokens() )
{
Insert( tempInt, t.nextToken() );
}
else
{
System.out.println( "Error in " + TREENAME + " applet: Invalid Element List: expected pairs of integers and strings. Some elements could have been added before this." );
return;
}
}
}
}
/**
* Layout_Tree
*/
void Layout_Tree()
{
if (first_Bucket == null || root == null) return; // No tree!!
Bucket curBucket = first_Bucket;
int numBuckets = 0;
int wanted = 0;
while ( curBucket != null )
{
// Get # buckets
numBuckets++;
// Get Sum of buckets preferred widths
wanted = wanted + curBucket.preferredSize().width;
curBucket = curBucket.next_Bucket;
}
// Get screen width
int screenwidth = bounds().width - 16;
int leftedge = bounds().x;
// Get space needed
int needed = wanted + ( (numBuckets - 1)* Bucket.PREFERRED_BUCKET_SPACING );
if ( numBuckets == 1) needed = needed + Bucket.PREFERRED_BUCKET_SPACING;
// Determine slack space
int slack;
slack = screenwidth - needed;
if ( slack < 0 )
{
// We don't have room for adequate bucket spacing
// so shrink the bucket gap
// (this will cause buckets to overlap on an insertion)
// See if we could shrink the bucket spacing to make
// all the buckets fit
// if not we have to chop off the sides of the tree
Bucket.BUCKET_SPACING = Bucket.PREFERRED_BUCKET_SPACING - (Math.abs(slack) / numBuckets);
if ( Bucket.BUCKET_SPACING < Bucket.MIN_BUCKET_SPACING )
{
// there is not enough pixels inbetween buckets for useful display
Bucket.BUCKET_SPACING = Bucket.MIN_BUCKET_SPACING;
// So instead of really squishing buckets, Chop sides of tree
// by moving the left edge of the tree over
int nowneed = wanted + ( numBuckets * Bucket.BUCKET_SPACING );
leftedge = (bounds().x - ( (nowneed - screenwidth) / 2)) + Bucket.MIN_BUCKET_SPACING;
slack = 0;
}
else // < MAX but > MIN We at least have some slack space available
{
needed = wanted + ( (numBuckets - 1) * Bucket.BUCKET_SPACING );
// Determine NEW slack space
slack = screenwidth - needed;
}
} // Slack < 0
else
{
// We have lots of space, and all butkets may be layed out at the preferred sizes
Bucket.BUCKET_SPACING = Bucket.PREFERRED_BUCKET_SPACING;
}
// Now the left edge, and slack space needed are known
leftedge = leftedge + ( slack / 2 ) + 25;
root.Reshape_Tree( leftedge );
}
/**
* Insert a Key and it's Data into the Tree
*/
public void Insert( int k, String d )
{
NodeInfo promo_d = new NodeInfo(); // Create a class to hold return parameters
if ( root == null ) // We are a lame kinda tree!
{
// Create a Bucket and put our Data into it
first_Bucket = new Bucket();
root = first_Bucket;
// Set it's Position & Attributes!
add ( first_Bucket );
//first_Bucket.reshape( root_X_pos, root_Y_pos, Bucket.BUCKET_WIDTH, Bucket.BUCKET_HEIGHT );
first_Bucket.move( root_X_pos - 25, root_Y_pos);
if ( isShowing() )
{
try
{
Thread.sleep( 500 );
}
catch (InterruptedException e)
{}
}
Layout_Tree(); // Resize, Reposition, and Repaint
}
// Now we are sure we have a root, tell it to insert the data
if ( root.Insert( k, d, promo_d ) == NO_PROMOTION )
{
// The data fit into the tree just fine, no hassle :)
if ( isShowing() )
{
try
{
Thread.sleep( 3000 );
}
catch (InterruptedException e)
{}
}
//Layout_Tree(); // Resize, Reposition, and Repaint
return;
}
else // Damn, the insert caused our root to split
{
// Root split, so we need to make a new root and
// Put the given key into it, and then
// Make it point to our old root on the left, and the
// newly returned pointer on the right
INode rootINode = new INode();
// Setup the new node
rootINode.child[0] = root; // Set left Child
rootINode.Keys[0] = promo_d.key; // Set Key
rootINode.child[1] = promo_d.rc; // Set right Child
rootINode.nKeys = 1;
// Set it's Position & Attributes!
add ( rootINode );
// Draw root node in exact position it was in the inode
int Keypos = rootINode.child[0].bounds().x + ((Ndiv2 ) * ( Pointer.POINTER_WIDTH + Key.KEY_WIDTH ));
rootINode.movekids = false;
rootINode.reshape( Keypos, rootINode.child[0].bounds().y, INode.NEW_INODE_WIDTH, INode.INODE_HEIGHT);
rootINode.resize(); // Paint it
root = rootINode;
// move kids down
// Move old Root out of the way
rootINode.child[0].dmove_A( 0, BRANCH_SPACING ); // x,y
// Move New Right Child Down one
rootINode.child[1].dmove_A( 0, BRANCH_SPACING ); // x,y
// move root to centre
rootINode.speed = INode.SPLIT_SPEED;
// We will just move the old tree down a ways, then
// Draw our new root directly between the old root and the new
// node's rightmost edge.
int left = rootINode.child[0].bounds().x;
int right = rootINode.child[1].bounds().x + rootINode.child[1].bounds().width;
root_X_pos = left + ((right - left - INode.NEW_INODE_WIDTH) / 2);
rootINode.move_A( root_X_pos, root_Y_pos );
rootINode.speed = INode.DEF_SPEED;
rootINode.movekids = true;
// Give user some time to see what happened before we jump the whole tree
// to a new position
if ( isShowing() )
{
try
{
Thread.sleep( 3000 );
}
catch (InterruptedException e)
{}
}
//Layout_Tree(); // Resize, Reposition, and Repaint
} // Promotion
} // Insert
} // BplusTree
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -