浅谈二叉树

为什么要使用二叉树

  因为二叉树结合了有序数组和链表的特点。在二叉树中查找数据的速度和在有序数组中查找一样快,并且插入数据项和删除数据项的速度也和链表一样快

查找

  从根开始进行查找,当前节点的key值与所查key值相等时返回该节点;当前节点的key值小于所查key值时,则去其左子树进行查找;当前节点的key值大于所查key值时,则去其右子树进行查找。

public Node find(int key){
Node current = root;
while(current.iData != key){
if(key < current.iData){
current = current.leftChild;
} else {
current = current.rightChild;
}

if(current == null){
return null;
}
}
return current;
}

插入

  二叉树的插入首先需要确定新节点插入的位置,即先找插入位置,再插入新节点

public void insert(int id, double dd){
Node newNode = new Node();
newNode.iData = id;
newNode.fData = 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;
}
} else {
current = current.rightChild;
if(current == null){
parent.rightChild = newNode;
return;
}
}
}
}
}

遍历

  遍历树的意思是根据一种特定的顺序访问树的每一个节点,树的遍历分为以下三种形式:

  • 前序遍历
    1.访问节点
    2.调用自身遍历该节点的左子树
    3.调用自身遍历该节点的右子树

    public void preOrder(Node localRoot){
    if(localRoot != null){
    System.out.print(localRoot.iData + " ");
    inOrder(localRoot.leftChild);
    inOrder(localRoot.rightChild);
    }
    }

  • 中序遍历
    1.调用自身遍历该节点的左子树
    2.访问节点
    3.调用自身遍历该节点的右子树

    public void inOrder(Node localRoot){
    if(localRoot != null){
    inOrder(localRoot.leftChild);
    System.out.print(localRoot.iData + " ");
    inOrder(localRoot.rightChild);
    }
    }

  • 后序遍历
    1.调用自身遍历该节点的左子树
    2.调用自身遍历该节点的右子树
    3.访问节点

    public void preOrder(Node localRoot){
    if(localRoot != null){
    inOrder(localRoot.leftChild);
    inOrder(localRoot.rightChild);
    System.out.print(localRoot.iData + " ");
    }
    }

查找最大值和最小值

  查找最小值时,先走到根的左子节点处,然后接着走到那个节点的左子节点,如此类推,知道找到一个没有左子节点的节点,这个节点即为最小值所在的节点。

public Node mininum(){
Node current = root;
Node last = null;
while(current != null){
last = current;
current = current.leftChild;
}
return last;
}
  查找最大值时,先走到根的右子节点处,然后接着走到那个节点的右子节点,如此类推,知道找到一个没有右子节点的节点,这个节点即为最大值所在的节点。

public Node maxnum(){
Node current = root;
Node last = null;
while(current != null){
last = current;
current = current.righttChild;
}
return last;
}

删除节点

  删除节点是二叉树中最复杂的常用操作,删除节点的方法为先查找到所要删除的节点,找到该节点后有以下三种情况需要考虑:

  • 该节点是叶节点(没有子节点)
      要删除叶节点,只需要改变该节点的父节点的对应字段的值,由该节点改为null就可以了。要删除的节点仍然存在,但是它已经不是树的一部分了。

    if(current.leftChild == null && current.rightChild == null){
    if(current == root){
    root = null;
    }else if(isLeftChild){
    parent.leftChild = null;
    }else{
    parent.rightChild = null;
    }
    }

  • 该节点有一个子节点
      该节点只有两个连接:连向父节点和连向它唯一的子节点。删除这个节点的时候,需要从这个序列中“剪断”这个节点,把它的子节点直接连到它的父节点上。这个过程要求改变父节点适当的引用(左子节点还是右子节点),指向要删除节点的子节点。

    if(current.rightChild == null){
    if(current == root){
    root = current.leftChild;
    }else if(isLeftChild){
    parent.leftChild = current.leftChild;
    }else{
    parent.rightChild = current.leftChild;
    }
    }else if(current.leftChild == null){
    if(current == root){
    root =current.rightChild;
    }else if(isLeftChild){
    parent.leftChild = current.rightChild;
    }else{
    parent.rightChild = current.rightChild;
    }
    }

  • 该节点有两个子节点
      如果要删除的节点有两个子节点,就不能只是用它的一个子节点代替它。删除有两个子节点的节点,用它的中序后继来代替该节点。删除的过程分为以下三个步骤:
    1. 找后继节点
      首先,找到初始节点的右子节点,它的关键字值一定比初始节点大。然后转到初始节点的右子节点的左子节点那里(如果有的话),然后到这个左子节点的左子节点,以此类推,顺着左子节点的路径一直向下找。这个路径上的最后一个左子节点就是初始节点的后继。

    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;
    }
    return successor;
    }
    2. 后继节点是删除节点的右子节点
      如果后继节点是current节点的右子节点,只需要把以后继为根的子树移到删除的节点的位置。这个操作需要两个步骤:把current从它父节点的rightChild字段删掉(也有可能是leftChild),把这个 字段指向后继;把current的左子节点移出来,把它插到后继的leftChild字段。

    Node successor = getSuccessor(current);
    if(current == root){
    root = successor;
    }else if(isLeftChild){
    parent.leftChild = successor;
    }else {
    parent.rightChild = successor;
    }
    successor.leftChild = current.leftChild;
    **3. 后继节点是删除节点的左子节点 **
      如果后继节点是要删除节点右子节点的左后代,执行删除操作需要以下四个步骤:把后继父节点的leftChild字段置为后继的右节点;把后继的rightChild字段置为要删除节点的右子节点;把current从它父节点的rightChild字段移除,把这个字段置为后继;把current的左子节点从current移除,把后继的leftChild字段置为current的左子节点。

    if(successor != delNode.rightChild){
    successorParent.leftChild = successor.rightChild;
    successor.rightChild = delNode.rightChild;
    }
    if(current == root){
    root = successor;
    }else if(isLeftChild){
    parent.leftChild = successor;
    }else {
    parent.rightChild = successor;
    }
    successor.leftChild = current.leftChild;

删除是必要的么

  二叉树的删除是一个比较复杂的操作,实际上因为它非常复杂,一些程序言都尝试着躲开它。他们会在Node类中加一个Boolean字段,名称如isDeleted。要删除一个节点时,就把此节点的这个字段置为true。其他操作,如find(),在查找之前先判断这个节点是不是标志为已经删除了。这样删除的节点不会改变树的结构。当然,这样做存储中还保留着这种“已经删除”的节点。

posted @ 2016-04-19 09:26  编程的小蚂蚁  阅读(224)  评论(0)    收藏  举报