《Java数据结构和算法》第二版 Robert lafore 编程作业 第十章
/*
编程作业
10.1 这个作业很容易。编写一个方法能返回2-3-4树中的最小值.
10.2 编写方法中序遍历2-3-4树。它能有序地显示所有的数据项。
10.3 2-3-4树可以用于排序。编写sort()方法,从main()方法中传入关键字的数组
并在排序后把它们写回数组中去。
10.4 修改tree234.java程序(清单10.1),使它可以创建并操作2-3树。要求显示
树并可以查找。还要能够插入数据项,但是只限在叶节点(要分裂的)的父节
点不需要再分裂的情况。这就是说split()方法不需要递归。编写insert()方
法,记得在找到要插入合适的叶节点之前不需要节点分裂。找到后,如果叶节
点满了就要分裂。也需要能够分裂根,但只限它是叶节点的情况下。根据这个
限制,只能插入少于9个数据项,否则程序就会起冲突了。
10.5 扩展编程作业10.4的程序,使split()方法是递归的并可以处理一个满的子节
点的满父节点的情况。这就允许不限个数地插入节点。注意在递归的split()
例程中,在决定数据项要转向何处和子节点要连接到哪个里之前要先分裂父节
点。
*/
package chap10;
// tree234.java
// demonstrates 234 tree
// to run this program: C>java Tree234App
import java.io.*;
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);
}
// --------------------------------------------------------------
} // end class DataItem
// //
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 "/"
}
// -------------------------------------------------------------
} // end class Node
// //
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()
// -------------------------------------------------------------
// ==============================================================
// 编程作业 10.1
// 这里没有考虑空树的情况,调用前确保树不为空
public long getMinValue() {
Node parent = root;
Node current = root;
while (current != null) {
parent = current;
current = current.getChild(0);
}
return parent.getItem(0).dData;
}
// ==============================================================
public void traverse(int traverseType) {
switch (traverseType) {
case 1:
System.out.print("\nPreorder traversal: ");
// preOrder(root);
break;
case 2:
System.out.print("\nInorder traversal: ");
inOrder(root);
break;
case 3:
System.out.print("\nPostorder traversal: ");
// postOrder(root);
break;
}
System.out.println();
}
// ==============================================================
// 编程作业 10.2
private void inOrder(Node root) {
if (root == null) {
return;
}
int i = 0;
for (; i < root.getNumItems(); i++) {
inOrder(root.getChild(i));
System.out.print(root.getItem(i).dData + " ");
}
if (i != 0) {
inOrder(root.getChild(i));
}
}
// ==============================================================
// 编程作业 10.3
public void sort(long[] array) {
this.root = new Node();
for (int i = 0; i < array.length; i++) {
this.insert(array[i]);
}
inOrderForSort(array, root, 0);
}
// ==============================================================
private int inOrderForSort(long[] array, Node root, int arrayIndex) {
if (root == null) {
return arrayIndex;
}
int i = 0;
for (; i < root.getNumItems(); i++) {
arrayIndex = inOrderForSort(array, root.getChild(i), arrayIndex);
array[arrayIndex++] = root.getItem(i).dData; // arrayIndex只在这里增加
}
if (i != 0) {
arrayIndex = inOrderForSort(array, root.getChild(i), arrayIndex);
}
return arrayIndex;
}
// ==============================================================
} // end class Tree234
// //
public class Tree234App {
public static void main(String[] args) throws IOException {
long value;
Tree234 theTree = new Tree234();
theTree.insert(10);
theTree.insert(20);
theTree.insert(50);
theTree.insert(40);
theTree.insert(60);
theTree.insert(30);
theTree.insert(70);
// ========================================
// 编程作业 10.1
System.out.println(theTree.getMinValue());
// ========================================
// 编程作业 10.2
theTree.traverse(2);// 中序遍历
// ========================================
// 编程作业 10.3
long[] array = { 1, 2, 3, 4, 8, 7, 6, 5 };
theTree.sort(array);
for (long l : array) {
System.out.print(l + " ");
}
// ========================================
} // end main()
// --------------------------------------------------------------
public static String getString() throws IOException {
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
String s = br.readLine();
return s;
}
// --------------------------------------------------------------
public static char getChar() throws IOException {
String s = getString();
return s.charAt(0);
}
// -------------------------------------------------------------
public static int getInt() throws IOException {
String s = getString();
return Integer.parseInt(s);
}
// -------------------------------------------------------------
} // end class Tree234App
// //
package chap10;
// tree234.java
// demonstrates 234 tree
// to run this program: C>java Tree234App
import java.io.*;
class Node1 {
private static final int ORDER = 3;
private int numItems;
private Node1 parent;
private Node1 childArray[] = new Node1[ORDER];
private DataItem itemArray[] = new DataItem[ORDER - 1];
// -------------------------------------------------------------
// connect child to this node
public void connectChild(int childNum, Node1 child) {
childArray[childNum] = child;
if (child != null)
child.parent = this;
}
// -------------------------------------------------------------
// disconnect child from this node, return it
public Node1 disconnectChild(int childNum) {
Node1 tempNode = childArray[childNum];
childArray[childNum] = null;
return tempNode;
}
// -------------------------------------------------------------
public Node1 getChild(int childNum) {
return childArray[childNum];
}
// -------------------------------------------------------------
public Node1 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 "/"
}
// -------------------------------------------------------------
} // end class Node
// //
class Tree23 {
private Node1 root = new Node1(); // make root node
// -------------------------------------------------------------
public int find(long key) {
Node1 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) {
Node1 curNode = root;
DataItem tempItem = new DataItem(dValue);
while (true) {
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
if (curNode.isFull()) {
// split(curNode, tempItem); //只能分裂叶节点
split1(curNode, tempItem, null); // 可以递归分裂
} else {
curNode.insertItem(tempItem); // insert new DataItem
}
} // end insert()
// -------------------------------------------------------------
// =============================================================
// 编程作业 10.4
// thisNode 当前要分裂的节点 ,只能为叶子这点
// newItem 要插入的数据项
public void split(Node1 thisNode, DataItem newItem) // split the node
{
// assumes node is full
DataItem itemB, itemC; // itemB保存中间数据项,itemC保存最大数据项
Node1 parent, child1, child2;
int itemIndex;
// 分裂当前节点
// 根据新数据项与当前节点中两个数据项的比较,分三种情况:
if (thisNode.getItem(0).dData > newItem.dData) { // 1.新数据项最小
itemC = thisNode.removeItem();
itemB = thisNode.removeItem();
thisNode.insertItem(newItem);
} else if (thisNode.getItem(1).dData < newItem.dData) {// 2.新数据项最大
itemC = newItem;
itemB = thisNode.removeItem();
} else {// 3.新数据项在中间
itemC = thisNode.removeItem();
itemB = newItem;
}
// 新节点
Node1 newRight = new Node1(); // make new node
newRight.insertItem(itemC); // item C to newRight
// 得到父节点
if (thisNode != root) {
parent = thisNode.getParent();
} else {
root = new Node1(); // make new root
parent = root; // root is our parent
root.connectChild(0, thisNode); // connect to parent
}
// 中间数据项插入到父节点
if (parent.isFull()) {
System.out.println("还未实现父节点分裂!!!");
} else {
itemIndex = parent.insertItem(itemB);
int n = parent.getNumItems(); // total items?
for (int j = n - 1; j > itemIndex; j--) // move parent's
{ // connections
Node1 temp = parent.disconnectChild(j); // one child
parent.connectChild(j + 1, temp); // to the right
}
// connect newRight to parent
parent.connectChild(itemIndex + 1, newRight);
}
} // end split()
// =============================================================
// 编程作业 10.5
// thisNode 当前要分裂的节点
// newItem 要插入的数据项
// newNode 上次分裂(子节点分裂)得到的新节点,如果是第一次分裂(叶节点分裂),则为null
public void split1(Node1 thisNode, DataItem newItem, Node1 newNode) {
// assumes node is full
DataItem itemB, itemC; // itemB保存中间数据项,itemC保存最大数据项
Node1 parent, child1, child2, newRight;
int itemIndex;
// 分裂当前节点
// 根据新数据项与当前节点中两个数据项的比较,分三种情况:
if (thisNode.getItem(0).dData > newItem.dData) { // 1.新数据项最小
itemC = thisNode.removeItem();
itemB = thisNode.removeItem();
thisNode.insertItem(newItem);
child1 = thisNode.disconnectChild(1);
child2 = thisNode.disconnectChild(2);
thisNode.connectChild(1, newNode);
newRight = new Node1(); // make new node
newRight.insertItem(itemC); // item C to newRight
newRight.connectChild(0, child1);
newRight.connectChild(1, child2);
} else if (thisNode.getItem(1).dData < newItem.dData) { // 2.新数据项最大
itemC = newItem;
itemB = thisNode.removeItem();
child2 = thisNode.disconnectChild(2);
newRight = new Node1(); // make new node
newRight.insertItem(itemC); // item C to newRight
newRight.connectChild(0, child2);
newRight.connectChild(1, newNode);
} else { // 3.新数据项在中间
itemC = thisNode.removeItem();
itemB = newItem;
child2 = thisNode.disconnectChild(2);
newRight = new Node1(); // make new node
newRight.insertItem(itemC); // item C to newRight
newRight.connectChild(0, newNode);
newRight.connectChild(1, child2);
}
// 得到父节点
if (thisNode != root) { // 当前节点是非根节点
parent = thisNode.getParent();
} else {// 当前节点是根节点
root = new Node1(); // make new root
parent = root; // root is our parent
root.connectChild(0, thisNode); // connect to parent
}
// 中间数据项插入到父节点
if (parent.isFull()) { // 父节点是满,则递归分裂父节点
split1(parent, itemB, newRight);
} else { // 父节点不满,直接插入
itemIndex = parent.insertItem(itemB);
// 调整相应的子节点
if (itemIndex == 0) {// 插入到第一个
parent.connectChild(2, parent.disconnectChild(1));
parent.connectChild(1, newRight);
} else { // 插入到第二个
parent.connectChild(2, newRight);
}
}
} // end split()
// =============================================================
// -------------------------------------------------------------
// gets appropriate child of node during search for value
public Node1 getNextChild(Node1 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(Node1 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++) {
Node1 nextNode = thisNode.getChild(j);
if (nextNode != null)
recDisplayTree(nextNode, level + 1, j);
else
return;
}
} // end recDisplayTree()
// -------------------------------------------------------------\
} // end class Tree234
// //
public class Tree23App {
public static void main(String[] args) throws IOException {
long value;
Tree23 theTree = new Tree23();
// ==================================================
// 编程作业 10.4
// theTree.insert(50);
// theTree.insert(40);
// theTree.insert(60);
// theTree.insert(30);
// theTree.insert(70);
// theTree.insert(80);
// theTree.insert(90);
// theTree.insert(65);
// // theTree.insert(20); //只能插入少于9个数据项
// ==================================================
// 编程作业 10.5
theTree.insert(50);
theTree.insert(40);
theTree.insert(60);
theTree.insert(30);
theTree.insert(70);
theTree.insert(80);
theTree.insert(90);
theTree.insert(65);
theTree.insert(100);
theTree.insert(85);
theTree.insert(95);
theTree.insert(86);
// ==================================================
while (true) {
System.out.print("Enter first letter of ");
System.out.print("show, insert, or find: ");
char choice = getChar();
switch (choice) {
case 's':
theTree.displayTree();
break;
case 'i':
System.out.print("Enter value to insert: ");
value = getInt();
theTree.insert(value);
break;
case 'f':
System.out.print("Enter value to find: ");
value = getInt();
int found = theTree.find(value);
if (found != -1)
System.out.println("Found " + value);
else
System.out.println("Could not find " + value);
break;
default:
System.out.print("Invalid entry\n");
} // end switch
} // end while
} // end main()
// --------------------------------------------------------------
public static String getString() throws IOException {
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
String s = br.readLine();
return s;
}
// --------------------------------------------------------------
public static char getChar() throws IOException {
String s = getString();
return s.charAt(0);
}
// -------------------------------------------------------------
public static int getInt() throws IOException {
String s = getString();
return Integer.parseInt(s);
}
// -------------------------------------------------------------
} // end class Tree234App
// //