码迷,mamicode.com
首页 > 其他好文 > 详细

二叉树

时间:2015-07-30 23:13:20      阅读:154      评论:0      收藏:0      [点我收藏+]

标签:

 一、为什么使用二叉树?

  为什么要用到树?因为它通常结合了另外两种数据结构的优点:一种是有序数组,另一种是链表。在树中查找数据项的速度和在有序数组中查找一样快,并且插入数据项和删除数据项的速度也和链表一样。

二、二叉树的定义

  如果数种每个节点最多只能有两个子节点,这样的树称为“二叉树”。二叉树的每个节点的两个子节点称为“左节点”和“右节点”。二叉树中的节点不是必须有两个子节点;它可以只有一个左子节点,或者只有一个右子节点,或者干脆没有子节点(这种情况下它就是叶节点)。

三、二叉树的实现

  实现二叉树需要一个节点对象的内部类。这些对象包含数据,数据代表要存储的内容,而且还有指向节点的两个子节点的引用。还需要一个表示根的Node变量,以及查询、插入、遍历和删除的方法。下面是这个类的骨架。

package binaryTree;

import org.junit.Test;

public class BinaryTree {
    
    private Node root;
    
    /**
     * 插入数据
     * @param iDate 插入的数据
     */
    public void insert(int iDate){}
    
    /**
     * 查找结点
     * @param key 查找关键字
     * @return
     */
    public Node find(int key){}
    
    /**
     * 求最大值
     * @param localRoot
     * @return
     */
    public Node findMax(Node localRoot){ }
    
    /**
     * 求最小值
     * @param localRoot
     * @return
     */
    public Node findMin(Node localRoot){ }
    
    /**
     * 中序遍历
     * @param localRoot
     */
    public void inOrder(Node localRoot){ }
    
    /**
     * 前序遍历
     * @param localRoot
     */
    public void preOrder(Node localRoot){ }
    
    /**
     * 后序遍历
     * @param localRoot
     */
    public void lastOrder(Node localRoot){ }
    
    /**
     * 删除结点
     * @param key
     * @return
     */
    public boolean delete(int key){}
    
}

1)查找节点:查找比较容易实现。从根节点开始比较,如果查找值比当前值小,则跳到左子节点,如果大,跳到右子节点,如果相等直接返回。

   /**
     * 查找结点
     * @param key 查找关键字
     * @return
     */
    public Node find(int key){
        Node current = root;
        //如果链表为空,就返回null
        if(current==null){
            return null;
        }
        while(current.iDate!=key){
            if(current.iDate>key)
                current = current.leftChild;
            else
                current = current.rightChild;
            if(current==null)
                return null;
        }
        return current;
    }

2)插入节点:插入节点必须先确定新节点插入的位置。这段代码与查找节点的代码大致相同,区别是原来简单的查找节点时,遇到null(不存在)值后,表明要找的节点不存在,于是就立即返回。现在要插入一个节点,就在返回前插入(要是必要的话,要先创建)节点。

/**
     * 插入数据
     * @param iDate 插入的数据
     */
    public void insert(int iDate){
        Node newNode = new Node();
        newNode.iDate=iDate;
        
        if(root==null){
            root = newNode;
        }else{
            Node current = root;
            Node parent;  //当前结点的父结点
            while(true){
                parent = current;
                if(current.iDate>iDate){
                    current = current.leftChild;
                    if(current==null){
                        parent.leftChild = newNode;
                        return;
                    }
                }else{
                    current = current.rightChild;
                    if(current==null){
                        parent.rightChild=newNode;
                        return;
                    }
                }
            }
        }
    }

3)遍历树

3.1 中序遍历的步骤:

a.调用自身来遍历节点的左子树   b.访问该节点    c.调用自身来遍历节点的右子树

    /**
     * 中序遍历
     * @param localRoot
     */
    public void inOrder(Node localRoot){
        if(localRoot != null){
            inOrder(localRoot.leftChild);
            System.out.print(localRoot.iDate+" ");
            inOrder(localRoot.rightChild);
        }
    }

技术分享

                  中序遍历的调用

3.2 前序遍历的步骤:

a.访问该节点     b.调用自身来遍历节点的左子树     c.调用自身来遍历节点的右子树

    /**
     * 前序遍历
     * @param localRoot
     */
    public void preOrder(Node localRoot){
        if(localRoot != null){
            System.out.print(localRoot.iDate+" ");
            preOrder(localRoot.leftChild);
            preOrder(localRoot.rightChild);
        }
    }

3.3 后序遍历的步骤:

a.调用自身来遍历节点的左子树    b.调用自身来遍历节点的右子树    c.访问该节点 

    /**
     * 后序遍历
     * @param localRoot
     */
    public void lastOrder(Node localRoot){
        if(localRoot != null){
            preOrder(localRoot.leftChild);
            preOrder(localRoot.rightChild);
            System.out.print(localRoot.iDate+" ");
        }
    }
    

4)查找最大和最小值

要找最小值,先走到根的左子节点处,然后接着走到那个子节点的左子节点,如果类推,直接找到一个没有左子节点的节点,这个节点就是最小值得节点。最大值得原理是一样的,只是查找的是最有节点。

/**
     * 求最大值
     * @param localRoot
     * @return
     */
    public Node findMax(Node localRoot){
        Node current = localRoot;
        Node max=null;
        while(current != null){
            max=current;
            current = current.rightChild;
        }
        return max;
    }
    
    /**
     * 求最小值
     * @param localRoot
     * @return
     */
    public Node findMin(Node localRoot){
        Node current = localRoot;
        Node min=null;
        while(current != null){
            min=current;
            current = current.leftChild;
        }
        return min;
    }

5)删除节点:删除节点很麻烦也是方法中最难实现的。删除方法的第一部分和find()和insert()方法很像。它查找的要删除的节点。和insert()一样,需要保存要删除的父节点和要删除的节点是左节点还是右节点,这样就可以修改它的子字段的值了,并且判断修改的是左还是右。找到之后还要分三种情况考虑:

情况1:删除没有子节点的节点:直接删除就行

情况2:删除只有一个子节点的节点:将这个子节点放到删除的节点的位置就行

情况3:删除的有两个子节点的节点:这个有个窍门,用该节点的中序后继代替该节点的位置。在代替的时候又分为两种情况,一种是中序后继节点是删除节点的右子节点,一种是中序后继节点是删除节点的左后代。

 

中序后继:比该节点的关键字值次高的节点是它的中序后继,也就是用中序遍历它后面的那个值。

技术分享

1.如何得到中序后继:    首先,程序找到初始节点的右子节点,他的关键字值一定比初始节点大,然后转到初始节点的右子节点的左子节点那里(如果有的话),然后到这个左子节点的左子节点,以此类推,顺着左子节点的路径一直找,这个路径上的最后一个左子节点就是初始节点的的后继。

/**
     * 得到中序后继节点
     * @param delNode
     * @return
     */
    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){
       //把后继父节点的leftChild字段置为后继的右子节点 successorParent.leftChild
=successor.rightChild;
       //把后继节点的rightChild字段置为要删除节点的右子节点 successor.rightChild
=delNode.rightChild; } return successor; }

2.后继节点是删除节点的右子节点 操作步骤

a.把要删除的节点从它父节点的rightChild字段删除(当然也可能是leftChild字段),把这个字段指向后继

b.把要删除的节点的左子节点移出来,把它插到后继的leftChild字段。

技术分享

 

3.中序后继节点是删除节点的左后代 操作步骤

a.把后继父节点的leftChild字段置为后继的右子节点。

b.把后继节点的rightChild字段置为要删除节点的右子节点。

c.把要删除节点从它父节点的rightChild字段移除,把这个字段置为后继节点。

d.把要删除节点的左子节点从删除节点上移除,后继节点的leftChild字段置为删除节点的左子节点。

a和b由getSuccessor()方法完成。

技术分享

/**
     * 删除结点
     * @param key
     * @return
     */
    public boolean delete(int key){
        Node current = root; //当前节点
        Node parent = root; //当前点父
        boolean isLeftChild = true; //要删除的点是不是左//如果链表为空
        if(current==null)
            return false;
        //查找关键字所在的位置
        while(current.iDate!=key){
            parent = current;
            if(current.iDate>key){
                current = current.leftChild;
                isLeftChild = true;
            }
            else{
                current = current.rightChild;
                isLeftChild = false;
            }
            //到达点尾部仍没找到,
            if(current==null){
                return false;
            }
        }
        //a.如果没有子
        if(current.leftChild==null && current.rightChild==null){
            if(current == root){
                root = null;
            }else if(isLeftChild){
                parent.leftChild=null;
            }else{
                parent.rightChild=null;
            }
        }
        
        //b.如果有一个子
        else 
        //如果是右
        if(current.leftChild==null){
            if(current == root)
                root = current.rightChild;
            else if(isLeftChild){
                parent.leftChild=current.rightChild;
            }else{
                parent.rightChild = current.rightChild;
            }
        }
        //如果是左
        else if(current.rightChild==null){
            if(current == root)
                root = current.leftChild;
            else if(isLeftChild){
                parent.leftChild = current.leftChild;
            }else{
                parent.leftChild = current.rightChild;
            }
        }
        //c.如果删除的点有两个子
        else{
            Node successor = getSuccessor(current);
            if(current == root){
                root =successor;
            //把要删除的节点从它父节点的rightChild(当然也可能是leftChild字段),把这个字段指向后继
            }else if(isLeftChild){
                parent.leftChild=successor;
            }else{
                parent.rightChild=successor;
            }
            //把要删除的节点的左子节点移出来,把它插到后继的leftChild字段
            successor.leftChild=current.leftChild;
        }
        return true;
    }

 

二叉树

标签:

原文地址:http://www.cnblogs.com/duoluomengxing/p/4690675.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!