一
.2-3-4树的特点
1.名称特点
2、3、4的含义是指一个节点可能含有的子节点的个数
对于非叶节点,假如子节点连接的个数是L,数据项的个数是D,那么L=D+1,非叶节点的子节点数总是比它含有的数据项多1.
2.数据组织特点
节点中的数据项按关键字值升序排列,习惯上从左到右升序
二:节点的分裂
在2-3-4树的数据添加时,在往下寻找要插入位置的路途中,如果节点已经满了,就该节点就必须分裂,分裂保证了树的平衡
1.不分裂的添加情况
针对这种不分裂的情况,像常规树节点的添加一样,先查找合适的节点,然后在该节点添加数据即可
2.非根节点满的分裂情况
针对这种非根节点满的情况,需要将该节点分裂,分裂情况如下:
* B上移到其父节点
* 创建该节点的兄弟节点(在右侧),将C移到该兄弟节点
* A保留在原位
* 该节点最右边的两个子节点分裂开,连接到新节点上
、
、
3.根的分裂
* 创建新的根节点,将B上移到该根节点
* 创建当前节点的兄弟节点,在其右侧,将C移到该兄弟节点
* 将原子节点分裂开,重新连接到新的节点
三:源码
1.创建参数项类
class DataItem {
public long dData; // one data item
public DataItem(long dd) // constructor
{
dData = dd;
}
public void displayItem() // display item, format "/27"
{
System.out.print("/" + dData);
}
}
2.创建节点类
class Node {
private static final int ORDER = 4;
private int numItems;
private Node parent;
private Node childArray[] = new Node[ORDER];
private DataItem itemArray[] = new DataItem[ORDER - 1];
// connect child to this node
public void connectChild(int childNum, Node child) {
childArray[childNum] = child;
if (child != null)
child.parent = this;
}
// disconnect child from this node, return it
public Node disconnectChild(int childNum) {
Node tempNode = childArray[childNum];
childArray[childNum] = null;
return tempNode;
}
public Node getChild(int childNum) {
return childArray[childNum];
}
public Node getParent() {
return parent;
}
public boolean isLeaf() {
return (childArray[0] == null) ? true : false;
}
public int getNumItems() {
return numItems;
}
public DataItem getItem(int index) // get DataItem at index
{
return itemArray[index];
}
public boolean isFull() {
return (numItems == ORDER - 1) ? true : false;
}
public int findItem(long key) // return index of
{ // item (within node)
for (int j = 0; j < ORDER - 1; j++) // if found,
{ // otherwise,
if (itemArray[j] == null) // return -1
break;
else if (itemArray[j].dData == key)
return j;
}
return -1;
} // end findItem
public int insertItem(DataItem newItem) {
// assumes node is not full
numItems++; // will add new item
long newKey = newItem.dData; // key of new item
for (int j = ORDER - 2; j >= 0; j--) // start on right,
{ // examine items
if (itemArray[j] == null) // if item null,
continue; // go left one cell
else // not null,
{ // get its key
long itsKey = itemArray[j].dData;
if (newKey < itsKey) // if it's bigger
itemArray[j + 1] = itemArray[j]; // shift it right
else {
itemArray[j + 1] = newItem; // insert new item
return j + 1; // return index to
} // new item
} // end else (not null)
} // end for // shifted all items,
itemArray[0] = newItem; // insert new item
return 0;
} // end insertItem()
public DataItem removeItem() // remove largest item
{
// assumes node not empty
DataItem temp = itemArray[numItems - 1]; // save item
itemArray[numItems - 1] = null; // disconnect it
numItems--; // one less item
return temp; // return item
}
public void displayNode() // format "/24/56/74/"
{
for (int j = 0; j < numItems; j++)
itemArray[j].displayItem(); // "/56"
System.out.println("/"); // final "/"
}
}
节点最多包含4个子节点,数据项最多有三个,一个父节点
节点包含常规操作:添加数据项、删除数据项、关联子节点
3.234树类的创建
参考:Java数据结构和算法(第二版)
class Tree234 {
private Node root = new Node(); // make root node
public int find(long key) {
Node curNode = root;
int childNumber;
while (true) {
if ((childNumber = curNode.findItem(key)) != -1)
return childNumber; // found it
else if (curNode.isLeaf())
return -1; // can't find it
else // search deeper
curNode = getNextChild(curNode, key);
} // end while
}
// insert a DataItem
public void insert(long dValue) {
Node curNode = root;
DataItem tempItem = new DataItem(dValue);
while (true) {
if (curNode.isFull()) // if node full,
{
split(curNode); // split it
curNode = curNode.getParent(); // back up
// search once
curNode = getNextChild(curNode, dValue);
} // end if(node is full)
else if (curNode.isLeaf()) // if node is leaf,
break; // go insert
// node is not full, not a leaf; so go to lower level
else
curNode = getNextChild(curNode, dValue);
} // end while
curNode.insertItem(tempItem); // insert new DataItem
} // end insert()
public void split(Node thisNode) // split the node
{
// assumes node is full
DataItem itemB, itemC;
Node parent, child2, child3;
int itemIndex;
itemC = thisNode.removeItem(); // remove items from
itemB = thisNode.removeItem(); // this node
child2 = thisNode.disconnectChild(2); // remove children
child3 = thisNode.disconnectChild(3); // from this node
Node newRight = new Node(); // make new node
if (thisNode == root) // if this is the root,
{
root = new Node(); // make new root
parent = root; // root is our parent
root.connectChild(0, thisNode); // connect to parent
} else // this node not the root
parent = thisNode.getParent(); // get parent
// deal with parent
itemIndex = parent.insertItem(itemB); // item B to parent
int n = parent.getNumItems(); // total items?
for (int j = n - 1; j > itemIndex; j--) // move parent's
{ // connections
Node temp = parent.disconnectChild(j); // one child
parent.connectChild(j + 1, temp); // to the right
}
// connect newRight to parent
parent.connectChild(itemIndex + 1, newRight);
// deal with newRight
newRight.insertItem(itemC); // item C to newRight
newRight.connectChild(0, child2); // connect to 0 and 1
newRight.connectChild(1, child3); // on newRight
} // end split()
// gets appropriate child of node during search for value
public Node getNextChild(Node theNode, long theValue) {
int j;
// assumes node is not empty, not full, not a leaf
int numItems = theNode.getNumItems();
for (j = 0; j < numItems; j++) // for each item in node
{ // are we less?
if (theValue < theNode.getItem(j).dData)
return theNode.getChild(j); // return left child
} // end for // we're greater, so
return theNode.getChild(j); // return right child
}
public void displayTree() {
recDisplayTree(root, 0, 0);
}
private void recDisplayTree(Node thisNode, int level, int childNumber) {
System.out.print("level=" + level + " child=" + childNumber + " ");
thisNode.displayNode(); // display this node
// call ourselves for each child of this node
int numItems = thisNode.getNumItems();
for (int j = 0; j < numItems + 1; j++) {
Node nextNode = thisNode.getChild(j);
if (nextNode != null)
recDisplayTree(nextNode, level + 1, j);
else
return;
}
} // end recDisplayTree()
}