二叉排序树的插入、遍历和删除

    xiaoxiao2021-04-16  35

    二叉排序树又称为二叉查找树BST。它或者是一颗空树,或者具有下列性质的二叉树: 1、如果左子树不为空,那么左子树上的所有结点值均小于它的根节点的值; 2、如果右子树不为空,那么右子树上的所有结点的值均大于它的根节点的值; 由于二叉树具有有序的特性,所以常考。

    实现代码如下:

    package tree; class Node //记得定义先 { public int data; public Node left; public Node right; public Node(int data) { this.data=data; this.right=null; this.left=null; } } public class treetest { private Node root; public treetest() { root=null; } public Node find( int key) { if(root==null) return -1; Node current=root; //start at root while(!(current.data==key)) { if(current.data<key) current=current.right; else current=current.left ; if(current==null) return -1; } return current.data; } public void insert(int data) { Node newnode = new Node(data); if(root==null) root=newnode; else { Node current =root; //start at root Node parent; while(true) //exits internally 寻找插入的位置 { parent=current; if(data<current.data) { current=current.left; if(current==null) { parent.left=newnode; return; } } else { current=current.right; if(current==null) {parent.right=newnode; return; } } } } } public void buildtree(int [] a) { for(int i=0;i<a.length;i++) { insert(a[i]); } }

    遍历过程:

    //中序遍历方法递归实现 会使所有的节点按关键字值升序被访问到 递归 //用递归方法遍历整棵树要用一个结点作为参数。初始化时是根 。 // 1 调用自身来遍历结点的左子树 2 访问这个结点 3 调用自身来遍历结点的右子树 // 不关心结点是否有关键字值,而是看这个结点是否有子节点 终止条件:结点为null //中序遍历是一个递增的有序序列 public void inOrder(Node localroot) { if(localroot!=null) { inOrder(localroot.left); System.out.print(localroot.data+" "); inOrder(localroot.right); } } public void inOrder() { this.inOrder(this.root); // } //先序遍历 递归 public void preOrder( Node localroot) { if(localroot!=null) { System.out.print(localroot.data+" "); preOrder(localroot.left ); preOrder(localroot.right); } } public void preOrder() { this.preOrder(this.root); } //后序遍历 递归 public void postOrder(Node localroot) { if(localroot!=null) { postOrder(localroot.left); postOrder(localroot.right); System.out.print(localroot.data+" "); } } public void postOrder() { this.postOrder(this.root); }

    查找最小节点:走到左子树为空为止

    //查找最小值 public Node minmum() { Node current,last=null; current=root; while(current!=null) { last=current; //remember node! current=current.left; } return last; }

    难点:删除结点~ 情况有三: 1、该节点是叶节点(没有子节点); 2、该节点有一个子节点; 3、该节点有两个子节点。

    //情况1 删除没有子节点的结点 为叶节点 public boolean delete(int key) { Node current=root; Node parent=root; boolean isleftchild =true; // find first while(current.data!=key) { parent =current; if(current.data>key) { isleftchild=true; current=current.left; } else { isleftchild=false; current=current.right ; } if(current==null) return false; //did't find } //找到之后,还需确认他是否真的没有子节点;若没有,还需要检查他是不是根 ,是,则直接置为null。 if(current.left ==null&&current.right==null) { if(current==root) //if root root=null; else if(isleftchild) parent.left=null; else parent.right=null; }

    //情况2:删除只有一个子节点的结点 //这个结点只有两个连接:父节点和他唯一的子节点 //特殊情况:被删除的结点可能是根,他没有父节点,只是被合适的子树所替代 else if(current.right==null) { if(current==root) root=current.left; else if(isleftchild) //left child of parent parent.left =current.left; else parent.right=current.left; } else if(current.left==null) { if(current==root) root=current.right; else if(isleftchild) //left child of parent parent.left=current.right; else parent.right=current.right; else { 情况3

    情况3不能简单用子树替代,但是记得在二叉搜索树里结点是按照升序的关键字值排列的。对每一个结点来说,比该结点关键字值高的结点是他的中序后继,可称为该节点的后继。 可以使用窍门:删除有两个子节点的节点,用他的中序后继来代替该节点。 (更麻烦的情况是,它的后继节点也有自己的子节点,后面讨论)

    问题:如何查找后继节点? 首先,找到初始节点的右子节点,他的关键字值一定会比初始节点大,然后转到初始节点的右子节点的左子节点那里,(如果有的话),然后找到这个左子节点的左子节点,以此类推,一直找下去。则这条路径上最后一个左子节点就是初始节点的后继,如下图。

    这样做的目标是:找到比初始节点关键值大的节点集合中最小的一个节点。所以先向右找到右子节点,再找这颗子树的中值最小的节点,就顺着左子节点路径找下去。 如果初始节点的右子节点没有左子节点,那么这个右子节点本身就是后继。

    //情况3:删除有两个子节点的结点 // 找后继节点 返回参数delnode的后继节点,此时已经判断过这个要删除的节点有两个子节点 // 当后继节点是delnode的右子节点时: 图 8.19 // 1把current从他的父节点的right删除,也可能是左,把这个字段指向后继; // 2 把current的左子节点移除来,插到后继的左边。 Node successor =getsuccessor(current); //connect parent of current to successor instead if(current==root) // if root root=successor; else if(isleftchild) //连接删除节点的父节点 parent.left=successor; else parent.right=successor; //connect successor to current's left child successor.left=current.left; } return true; //当后继节点是delnode右子节点的左后代 图8.20 //1、把后继父节点的leftchild字段置为successor右子节点 //2、把successor的rightchild字段置为要删除节点的右子节点 //3、把current从他父节点的rightchild字段删除,把这个字段置为successor //4、把current的左子节点从current移除,successor的leftchild字段置为current的左子节点 //1、2 由getsuccessor()完成 3、4与上面的情况代码一样 }

    private Node getsuccessor( Node delnode) //获取后继节点 { Node successorparent =delnode; Node successor=delnode; Node current=delnode.right; while(current!=null) { successorparent =successor; successor=current; current=current.left; } if(successor!=delnode.right) //right child make connection { successorparent.left=successor.right; //rightchild to sp successor.right=delnode.right; //s to rightchild } return successor; }

    主函数:

    public static void main(String[] args) { treetest tree = new treetest(); int[] a={3,4,8,2,5,6}; tree.buildtree(a); int node =tree.find(9); System.out.println("find:" +node); int node2 =tree.minmum(); System.out.println(node2); //System.out.print("中序遍历: "); //tree.inOrder(); //System.out.println(); //System.out.print("先序遍历: "); //tree.preOrder(); //System.out.println(); //System.out.print("后序遍历: "); //tree.postOrder(); //System.out.println(); tree.displaytree();

    这个竟然没看懂~

    public void displaytree() { Stack gstack = new Stack(); gstack.push(root); int nblanks =32; boolean isrowempty=false; System.out.println("..........."); while(isrowempty==false) { Stack localstack = new Stack(); isrowempty=true; for(int j=0;j<nblanks;j++) { System.out.print(' '); } while(gstack.isEmpty()==false) { Node temp =(Node)gstack.pop(); if(temp!=null) { System.out.print(temp.data); localstack.push(temp.left); localstack.push(temp.right); if(temp.left!=null||temp.right!=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) gstack.push(localstack.pop()); } System.out.println( "........."); } }

    层序遍历:

    //层序遍历 :用队列实现 public void layerTranverse() { if(this.root==null) return; Queue<Node> q = new LinkedList<Node>(); q.add(this.root); while(!q.isEmpty()) //直到队列为空 { Node n =q.poll(); System.out.print(n.data); System.out.print(" "); if(n.left!=null) q.add(n.left ); if(n.right!=null) q.add(n.right); } }

    如果已知先序遍历和中序遍历,那如何求后序遍历?? 1、先确定树的根节点。 2、求解树的子树 3、不断递归,直到根节点左边和右边都为空时,则他已经为叶子节点。

    package tree; //已知先序遍历、中序遍历,如何求后序遍历? class Node { public Node left; public Node right; public int data; public Node(int data) { this.data=data; this.left=null; this.right=right; } } public class Binarytreetwo { private Node root; public Binarytreetwo() { this.root=null; } public void postOrder(Node localroot) { if(localroot!=null) { postOrder(localroot.left); postOrder(localroot.right); System.out.print(localroot.data+" "); } } public void postOrder() { this.postOrder(this.root); } public Node initTree(int[]preOrder,int start1,int end1,int[]inOrder,int start2,int end2) { if(start1>end1||start2>end2) return null; int rootdata=preOrder[start1]; Node head =new Node(rootdata); // //找到根节点所在的位置 int rootindex= findindexinarray(inOrder,rootdata,start2,end2); //构建左子树 int offset=rootindex-start2-1; Node left =initTree(preOrder,start1+1,offset+start1+1,inOrder,start2,start2+offset); //构建右子树 Node right=initTree(preOrder,offset+2+start1,end1,inOrder,rootindex+1,end2); head.left=left; head.right=right; return head; } public void initTree(int[] preOrder,int[] inOrder) { this.root=this.initTree(preOrder, 0, preOrder.length-1, inOrder, 0, inOrder.length-1); } public int findindexinarray(int[]a ,int x,int begin,int end) { for(int i=begin;i<=end;i++) { if(a[i]==x) return i; } return -1; } public static void main(String[] args) { Binarytreetwo tree= new Binarytreetwo(); int [] preOrder={1,2,4,8,9,5,10,3,6,7}; int[] inOrder={8,4,9,2,10,5,1,6,3,7}; tree.initTree(preOrder,inOrder); System.out.println("后序遍历为:"); tree.postOrder(); } }

    历时两天终于搞定了~

    转载请注明原文地址: https://ju.6miu.com/read-672931.html

    最新回复(0)