1.为什么使用树?
二叉树结合了两种数据结构的优点:一种是有序数组,另一种是链表。在树中查找数据项的速度和在有序数组中一样快O(logN),在树中删除和添加数据项和在链表中一样快O(1)。
2.实现树类
首先需要一个节点对象类,这个对象包含要保存的数据,以及指向左子树,右子树的引用。代码如下:
- //
- public class Node {
- public int iData; //data item(key)
- public double dData; //data item
- public Node leftChild; //this is Node's left child
- public Node rightChild; //this is Node's right child
- public void displayNode()
- {
- System.out.print("{");
- System.out.print(iData);
- System.out.print(",");
- System.out.print(dData);
- System.out.print("}");
- }
- }// end class Node
树类的实现:这个类的骨架如下
- class Tree
- {
- private Node root;
- public void find(int key)
- {
- }
- public void insert(int id,double dd)
- {
- }
- public void delete(int id)
- {
- }
- //other methods
- }//end class Tree
查找 方法find()
- //------------------------------------------------------------
- public Node find(int key) //find node with given key
- {
- Node current=root; //current指针指向root开始
- while(current.iData!=key) //while not match
- {
- if(current.iData>key)current=current.leftChild;//比key小,current指针走左子树
- else //反之向右
- current=current.rightChild;
- if(current==null)
- return null;
- }
- return current;
- }//end find();
- //------------------------------------------------------------
使用一个引用current,初始化为root节点,从root开始跟要查找的key比较,key比当前节点的iData对象小的时候,current引用变为当前current的左子树,否则就指向右子树。如果 current指向为null说明整个数没有匹配的数据项,返回null。找到匹配的数据项的时候,退出循环,返回 current指向的节点。
插入方法insert()
- //------------------------------------------------------------
- public void insert(int id,double dd)
- {
- Node newNode=new Node();
- newNode.iData=id;
- newNode.dData=dd;
- if(root==null) //如果根节点为空,把新节点设为根节点
- root=newNode;
- else //否则找到节点插入的位置,插入节点
- {
- Node current=root;
- Node parent;
- while(true)
- {
- parent=current;
- if(id<current.iData)
- {
- current=current.leftChild;
- if(current==null)
- {
- parent.leftChild=newNode;
- return;
- }
- }//end if go left
- else
- {
- current=current.rightChild;
- if(current==null)
- {
- parent.rightChild=newNode;
- return;
- }
- }// end else go right
- }//end while()
- }//end else root
- }//end insert()
- //------------------------------------------------------------------------------------------
首先判断树是否为空,如果是空,把根节点赋值为要插入的节点。否则,寻找要插入节点的位置,方法和find()类似,只是需要保存parent引用指向current的父节点,当current==null的时候即找到要插入的位置时,把要插入的节点赋给current的左子树或者右子树。
删除节点方法delete()
- //------------------------------------------------------------------------------------------
- public boolean delete(int key)//delete node with given key
- {
- Node current=root;
- Node parent=root;
- boolean isLeftChild=true;
- while(current.iData!=key)
- {
- parent=current;
- if(key<current.iData)
- {
- isLeftChild=true;
- current=current.leftChild;
- }
- else
- {
- isLeftChild=false;
- current=current.rightChild;
- }
- if(current==null)return false;//没有找到要删除的节点,结束删除
- }//end while()查找要删除节点
- if(current.leftChild==null&¤t.rightChild==null)//三种情况之一:要删除的节点没有左右子树
- {
- if(current==root) //首先判断这个要删除的节点是否为根
- root=null; //如果是根,把树清空
- else if(isLeftChild)//不是根节点后,判断要删除的节点是否是它的父节点的左子树
- parent.leftChild=null;//如果是,把其父节点的左子树清空
- else
- parent.rightChild=null;//如果不是,把其父节点的右子树清空
- }
- //三种情况之二:要删除的节点只有一边子树的情况
- else if(current.rightChild==null)//如果要删除的节点只有左子树,那么把其左子树直接接到其父节点即可
- {
- if(current==root)root=current.leftChild;//如果要删除的节点是根节点,则把其左子树设为根
- else if(isLeftChild)parent.leftChild=current.leftChild;//非根节点的情况下,isLeftChild,就把要删除的节点的左子树接到要删除的父节点的左子树上
- else parent.rightChild=current.leftChild;//当要删除的节点是其父节点的右子树的情况
- }
- else if(current.leftChild==null)//如果要删除的节点只有右子树,那么把其右子树直接接到其父节点即可
- {
- if(current==root)root=current.rightChild;//如果要删除的节点是根节点,则把其左右子树设为根
- else if(isLeftChild)parent.leftChild=current.rightChild;//非根节点的情况下,isLeftChild,就把要删除的节点的右子树接到要删除的父节点的左子树上
- else parent.rightChild=current.rightChild;//当要删除的节点是其父节点的右子树的情况
- }
- else //第三种情况
- {
- Node successor=getSuccessor(current);
- if(current==root)
- root=successor;
- else if(isLeftChild)
- parent.leftChild=successor;
- else
- parent.rightChild=successor;
- successor.leftChild=current.leftChild;
- }
- return true;
- }//end delete();
- //------------------------------------------------------------------------
- private Node getSuccessor(Node delNode)//返回比当前要删除的节点大的最小的节点,并执行必须的操作
- {
- Node successorParent=delNode;
- Node successor=delNode;
- Node current=delNode.rightChild;
- while(current!=null)
- {
- successorParent=successor;
- successor=current;
- current=current.leftChild;
- }
- if(successor!=delNode.rightChild)
- {
- successorParent.leftChild=successor.rightChild;
- successor.rightChild=delNode.rightChild;
- }
- return successor;
- }
- //------------------------------------------------------------------------
删除节点的操作比较复杂,大的方面需要分三种情况考虑:1.要删除的节点没有子节点;2.要删除的节点只有一个子节点;3.要删除的节点有两个子节点。这里有另一个方法getsuccessor()其作用是找到整棵树中比要删除节点大的最小的节点(次小节点)并对要删除节点的右子树做一些操作(使其可以直接充当代替删除节点的后继节点的右子树)。这里有个技巧,树中任何节点的后继节点只有两种可能:1.为其右节点(当其右节点不含有左子树时);2.为其右节点的最左子节点。
需要注意的是:需要一个标记boolean isLeftChild来标识要删除的节点是其父节点的左子树还是右子树。当找到了要删除节点current的后继节点successor时,只需根据isLeftChild把successor接入到parent的左或者右子树,然后把current的右子树充当successor的右子树, current的左子树充当successor的左子树,即完成了删除操作。
遍历traverse()前序、中序、后序遍历使用了递归(recursive)的方法。
- //------------------------------------------------------------------------
- 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();
- }
- //------------------------------------------------------------
- private void preOrder(Node localRoot)//前序遍历
- {
- if(localRoot!=null)
- {
- System.out.print(localRoot.iData+" ");
- preOrder(localRoot.leftChild);
- preOrder(localRoot.rightChild);
- }
- }
- //------------------------------------------------------------
- private void inOrder(Node localRoot)//中序遍历
- {
- if(localRoot!=null)
- {
- inOrder(localRoot.leftChild);
- System.out.print(localRoot.iData+" ");
- inOrder(localRoot.rightChild);
- }
- }
- //-----------------------------------------------------------------
- private void postOrder(Node localRoot)//后续遍历
- {
- if(localRoot!=null)
- {
- postOrder(localRoot.leftChild);
- postOrder(localRoot.rightChild);
- System.out.print(localRoot.iData+" ");
- }
- }
- //------------------------------------------------------------------
完整代码如下:
Node节点类:
- //
- public class Node {
- public int iData; //data item(key)
- public double dData; //data item
- public Node leftChild; //this is Node's left child
- public Node rightChild; //this is Node's right child
- public void displayNode()
- {
- System.out.print("{");
- System.out.print(iData);
- System.out.print(",");
- System.out.print(dData);
- System.out.print("}");
- }
- }// end class Node
Tree类:
- package tree;
- import java.util.*;
- public class Tree {
- private Node root; //first node of tree
- //------------------------------------------------------------
- public Tree()
- {
- root=null;
- }
- //------------------------------------------------------------
- public Node find(int key) //find node with given key
- {
- Node current=root; //current指针指向root开始
- while(current.iData!=key) //while not match
- {
- if(current.iData>key)current=current.leftChild;//比key小,current指针走左子树
- else //反之向右
- current=current.rightChild;
- if(current==null)
- return null;
- }
- return current;
- }//end find();
- //------------------------------------------------------------
- public void insert(int id,double dd)
- {
- Node newNode=new Node();
- newNode.iData=id;
- newNode.dData=dd;
- if(root==null) //如果根节点为空,把新节点设为根节点
- root=newNode;
- else //否则找到节点插入的位置,插入节点
- {
- Node current=root;
- Node parent;
- while(true)
- {
- parent=current;
- if(id<current.iData)
- {
- current=current.leftChild;
- if(current==null)
- {
- parent.leftChild=newNode;
- return;
- }
- }//end if go left
- else
- {
- current=current.rightChild;
- if(current==null)
- {
- parent.rightChild=newNode;
- return;
- }
- }// end else go right
- }//end while()
- }//end else root
- }//end insert()
- //------------------------------------------------------------------------------------------
- public boolean delete(int key)//delete node with given key
- {
- Node current=root;
- Node parent=root;
- boolean isLeftChild=true;
- while(current.iData!=key)
- {
- parent=current;
- if(key<current.iData)
- {
- isLeftChild=true;
- current=current.leftChild;
- }
- else
- {
- isLeftChild=false;
- current=current.rightChild;
- }
- if(current==null)return false;//没有找到要删除的节点,结束删除
- }//end while()查找要删除节点
- if(current.leftChild==null&¤t.rightChild==null)//三种情况之一:要删除的节点没有左右子树
- {
- if(current==root) //首先判断这个要删除的节点是否为根
- root=null; //如果是根,把树清空
- else if(isLeftChild)//不是根节点后,判断要删除的节点是否是它的父节点的左子树
- parent.leftChild=null;//如果是,把其父节点的左子树清空
- else
- parent.rightChild=null;//如果不是,把其父节点的右子树清空
- }
- //三种情况之二:要删除的节点只有一边子树的情况
- else if(current.rightChild==null)//如果要删除的节点只有左子树,那么把其左子树直接接到其父节点即可
- {
- if(current==root)root=current.leftChild;//如果要删除的节点是根节点,则把其左子树设为根
- else if(isLeftChild)parent.leftChild=current.leftChild;//非根节点的情况下,isLeftChild,就把要删除的节点的左子树接到要删除的父节点的左子树上
- else parent.rightChild=current.leftChild;//当要删除的节点是其父节点的右子树的情况
- }
- else if(current.leftChild==null)//如果要删除的节点只有右子树,那么把其右子树直接接到其父节点即可
- {
- if(current==root)root=current.rightChild;//如果要删除的节点是根节点,则把其左右子树设为根
- else if(isLeftChild)parent.leftChild=current.rightChild;//非根节点的情况下,isLeftChild,就把要删除的节点的右子树接到要删除的父节点的左子树上
- else parent.rightChild=current.rightChild;//当要删除的节点是其父节点的右子树的情况
- }
- else //第三种情况
- {
- Node successor=getSuccessor(current);
- if(current==root)
- root=successor;
- else if(isLeftChild)
- parent.leftChild=successor;
- else
- parent.rightChild=successor;
- successor.leftChild=current.leftChild;
- }
- return true;
- }//end delete();
- //------------------------------------------------------------------------
- private Node getSuccessor(Node delNode)//返回比当前要删除的节点大的最小的节点,并执行必须的操作
- {
- Node successorParent=delNode;
- Node successor=delNode;
- Node current=delNode.rightChild;
- while(current!=null)
- {
- successorParent=successor;
- successor=current;
- current=current.leftChild;
- }
- if(successor!=delNode.rightChild)
- {
- successorParent.leftChild=successor.rightChild;
- successor.rightChild=delNode.rightChild;
- }
- return successor;
- }
- //------------------------------------------------------------------------
- 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();
- }
- //------------------------------------------------------------
- private void preOrder(Node localRoot)//前序遍历
- {
- if(localRoot!=null)
- {
- System.out.print(localRoot.iData+" ");
- preOrder(localRoot.leftChild);
- preOrder(localRoot.rightChild);
- }
- }
- //------------------------------------------------------------
- private void inOrder(Node localRoot)//中序遍历
- {
- if(localRoot!=null)
- {
- inOrder(localRoot.leftChild);
- System.out.print(localRoot.iData+" ");
- inOrder(localRoot.rightChild);
- }
- }
- //-----------------------------------------------------------------
- private void postOrder(Node localRoot)//后续遍历
- {
- if(localRoot!=null)
- {
- postOrder(localRoot.leftChild);
- postOrder(localRoot.rightChild);
- System.out.print(localRoot.iData+" ");
- }
- }
- //------------------------------------------------------------------
- public void displayTree()
- {
- Stack globalStack=new Stack();
- globalStack.push(root);
- int nBlanks=32;
- boolean isRowEmpty=false;
- System.out.println(".............................................................................");
- while(isRowEmpty==false)
- {
- Stack<Node> localStack=new Stack();
- isRowEmpty=true;
- for(int j=0;j<nBlanks;j++)
- System.out.print(" ");
- while(globalStack.isEmpty()==false)
- {
- Node temp=(Node) globalStack.pop();
- if(temp!=null)
- {
- System.out.print(temp.iData);
- localStack.push(temp.leftChild);
- localStack.push(temp.rightChild);
- if(temp.leftChild!=null||temp.rightChild!=null)
- isRowEmpty=false;
- }
- else
- {
- System.out.print("..");
- localStack.push(null);
- localStack.push(null);
- }
- for(int j=0;j<nBlanks*2.2;j++)
- System.out.print(" ");
- }
- System.out.println();
- nBlanks/=2;
- while(localStack.isEmpty()==false)
- globalStack.push(localStack.pop());
- }
- System.out.println("................................................................................");
- }//end displayTree()
- //------------------------------------------------------------------
- }
App类:
- package tree;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- public class TreeApps {
- /**
- * @param args
- * @throws IOException
- */
- public static void main(String[] args) throws IOException {
- // TODO Auto-generated method stub
- int value;
- Tree theTree=new Tree();
- theTree.insert(50, 1.5);
- theTree.insert(25, 1.5);
- theTree.insert(75, 1.5);
- theTree.insert(12, 1.5);
- theTree.insert(37, 1.5);
- theTree.insert(43, 1.5);
- theTree.insert(30, 1.5);
- theTree.insert(33, 1.5);
- theTree.insert(87, 1.5);
- theTree.insert(93, 1.5);
- theTree.insert(97, 1.5);
- while(true)
- {
- System.out.print("Enter first letter of show, ");
- System.out.print("insert,find,delete,or traverse: ");
- int choice=getChar();
- switch(choice)
- {
- case 's':
- theTree.displayTree();
- break;
- case 'i':
- System.out.print("Enter value to insert: ");
- value=getInt();
- theTree.insert(value, value+0.9);
- break;
- case 'f':
- System.out.print("Enter value to find: ");
- value=getInt();
- Node found=theTree.find(value);
- if(found!=null)
- {
- System.out.print("Found: ");
- found.displayNode();
- System.out.print("/n");
- }
- else
- System.out.print("Could not find");
- System.out.print(value+"/n");
- break;
- case 'd':
- System.out.print("Enter value to delete: ");
- value=getInt();
- boolean didDelete=theTree.delete(value);
- if(didDelete)
- System.out.print("Deleted "+value+"/n");
- else
- System.out.print("Could not delete ");
- System.out.print(value+"/n");
- break;
- case 't':
- System.out.print("Enter type 1,2 or 3");
- value=getInt();
- theTree.traverse(value);
- break;
- default:
- System.out.print("Invalid entry/n");
- }//end switch
- }//end while
- }//end main()
- //----------------------------------------------------------------
- public static String getString()throws IOException
- {
- InputStreamReader in=new InputStreamReader(System.in);
- BufferedReader br=new BufferedReader(in);
- 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 TreeApp
- //
显示结果:
- Enter first letter of show, insert,find,delete,or traverse: s
- .............................................................................
- 50
- 25 75
- 12 37 .. 87
- .. .. 30 43 .. .. .. 93
- .. .. .. .. .. 33 .. .. .. .. .. .. .. .. .. 97
- ................................................................................
- Enter first letter of show, insert,find,delete,or traverse: