JAVA数据结构——二叉树

JAVA数据结构

Java数据结构(2)——二叉树

1.二叉树

树是一种典型的数据结构,具有很多重要的应用。树提供了一种层次组织结构,数据可以存储在树中每个节点内。前文所提到的线性表,其结构是线性的。而二叉树是一种层次结构,它要么是空集,要么是由一个称为根(root)的元素和两棵不同的二叉树组成,这两棵二叉树分别称为左子树右子树。定义中允许这两科树中的一颗或者两棵为空。下面是一些关于二叉树的基本概念:

  • 长度:指在该条路径上的边的个数。
  • 深度:指从根节点到该节点的路径长度。
  • 左(右)孩子结点:一个结点的左(右)子树的根节点称为这个结点的左(右)孩子。
  • 叶节点:没有左右孩子结点的结点。

本文将以一种特殊类型的二叉树——二叉查找树来对二叉树这种数据结构进行分析。二叉查找树的特征是:对于树中的每一个结点,它的左子树中的结点的值都小于该结点的值,而它的右子树中结点的值都大于该节点的值,二叉查找树中没有重复的元素。

1.1 二叉查找树

二叉查找树可以使用链式结构来表示,同理数据结构(1)中所提到的链表,二叉查找树的结点类型与之相仿,每个结点类型都包含了一个数值和两个结点类型left、right分别指向左孩子和右孩子,具体结点类表示代码如下。

class TreeNode<E extends Comparable<E>>{
  E element;
  TreeNode left;
  TreeNode right;
  public TreeNode(E e){
    element=e;
  }
}

在定义二叉树时,变量root指向树的根节点。若树为空,那么root的值为null。那么我们创建一个简单的具有5,8,,10这三个值的二叉树如下所示。

TreeNode<Integer> root=new TreeNode<Integer>(new Integer(8)); //头结点
TreeNode<Integer> left=new TreeNode<Integer>(new Integer(5)); //左子树
TreeNode<Integer> right=new TreeNode<Integer>(new Integer(10)); //右子树
1.2 查找元素

要在二叉查找树中查找一个元素,可以从根节点开始向下扫描,直到找到一个匹配元素或者达到一课空子树(树中并无匹配数据)。查找算法主要利用了二叉查找树的特性,即

对于树中的每一个结点,它的左子树中的结点的值都小于该结点的值,而它的右子树中结点的值都大于该节点的值,二叉查找树中没有重复的元素。

根据前文创建的结点类泛型类型,结点中的值是Comparable的子类,能够使用比较器进行比较。在构建二叉树时的插入算法,也将运用Comparable中的方法进行元素的比较。具体算法如下:

public boolean search(E element){
  TreeNode<E> current=root;//令根结点为当前结点
  while(current!=null){
    if(element.compareTo(current.element)<0){//利用比较器比较元素
      current=current.left;
    }else if(element.compareTo(current.element)>0){
      current=current.right;
    }else return true;//树中存在查找元素
  }
  return false;//树中不存在查找元素
}
1.3 插入元素

在二叉查找树(Binary Serach Tree)中插入一个元素,主要分为两个步骤,先确定插入元素在树中的具体插入位置,然后再插入元素。确定具体插入位置的关键在于确定新节点的父结点的位置。具体算法如下所示。

public boolean insert(E e){
  if(root==null)
    root=new TreeNode<E>(e);//树为空则创建头结点
  else{
    TreeNode<E> current=root;//当前结点指针
    TreeNdoe<E> parent=null;//父结点指针
    while(current!=null)
      if(e.compareTo(current.element)<0){
        parent=current;
        current=current.left;
      }else if(e.compareTo(current.element)>0){
        parent=current;
        current=current.right;
      }
  }
}

上述算法表示,若这棵树是空的就创建一个根结点;否则寻找新元素节点的父结点的位置。寻找方法则是不断比较插入元素与树中元素的大小,若新元素的值小于父元素的值,则将新元素的结点设置为父结点的左孩子;若新元素的值大于父元素的值,则将心愿素的结点设置为父结点的右孩子。

1.4 树的遍历

树的遍历就是访问树中每个结点一次且仅一次的过程。遍历的方法有很多种,一般通过迭代的方法能够容易实现,下面代码展示了各遍历方法是如何实现的。常用的遍历方法有如下5个:

  • 中序遍历:首先递归地访问当前结点的左子树,然后访问当前结点,最后递归地访问该节点的右子树。该遍历方法按照递增顺序显示二叉查找树中的所有结点。
  • 前序遍历:首先访问当前结点,然后递归地访问该节点的左子树,最后递归地访问该节点的右子树。
  • 后序遍历:首先递归地访问当前结点的左子树,然后递归地访问该节点的右子树,最后访问该节点本身。
  • 深度优先:遍历方法与前序遍历方法相同。
  • 广度优先:逐层访问树中的节点,首先访问根结点,然后从左至右访问根结点所有的孩子,再从左往右访问根结点所有的孙子节点,以此类推。

  • 中序遍历方法
public void inOrder(TreeNode<E> root){
  if(root==null) return;
  inOrder(root.left);//先遍历左子树,首先打印"最左"的叶子结点的值,然后打印其父结点的值
  System.out.println(root.element+" ");
  inOrder(root.right);//遍历右子树,同样按照左子树——根结点——右子树的顺序依次打印
}

  • 前序遍历方法
public void preOrder(TreeNode<E> root){
  if(root==null) return;
  System.out.println(root.element+" ");
  preOrder(root.left);
  preOrder(root.right);
}

  • 后序遍历方法
public void postOrder(TreeNode<E> root){
  if(root==null) return;
  postOrder(root.left);
  postOrder(root.right);
  System.out.println(root.element+" ");
}
1.5 删除元素

在二叉树种删除一个元素,比向二叉树中添加一个元素复杂很多。二叉树由于是层状结构,一旦某个结点被删除,整棵树可能都要进行重新构建。为了从一棵二叉查找树中删除一个元素,首先需要定位包含该元素的结点,以及它的父结点。假设current指向二叉查找书中包含删除元素的结点,parent指向current结点的父结点。current结点可能是parent结点的左孩子,也有可能是右孩子,这里需要分两种情况进行考虑:

  • 情况一
    当前结点(current)没有左孩子。这时只需要把该节点的父结点和当前结点的右孩子相连即可。

  • 情况二
    当前结点(current)有一个左孩子。此时我们需要定义变量rightMost,用来表示current结点的左子树中具有最大元素的结点。变量parentOfRightMost指向rightMost结点的父结点。其中,rightMost结点由于已经是current左子树中具有最大元素的结点,在二叉查找树中,这样的结点是不存在右孩子的,但可以存在左孩子。
    然后使用rightMost结点中的元素替换current结点中的元素值,将parentOfrightMost结点和rightMost结点的左孩子相连,然后删除rightMost结点。

二叉查找树删除方法代码实现如下所示:

public boolean delete(E e){
  TreeNode<E> parent=null;
  TreeNode<E> current=root;

  /*定位需要删除节点的位置*/
  while(current!=null){
    if(e.compareTo(current.element)<0){
      parent=current;
      current=current.left;
    }else if(e.compareTo(current.element)>0){
      parent=current;
      current=current.right;
    }else break;
}
  if(current==null) return false;

  /*第一种情况,当前结点没有左孩子*/
  if(current.left==null){
    if(parent==null){
      root=current.right;
    }else{
      if(e.compareTo(parent.element)<0)
        parent.left=current.right;
      else
        parent.right=current.right;
    }
  }else{
  /*第二种情况,当前结点有左孩子*/

    TreeNode<E> parentOfrightMost=current;
    TreeNode<E> rightMost=current.left;
    /*定义新变量,查找左子树中具有最大值元素的位置*/

    while(rightMost.right!=null){
      parentOfrightMost=rightMost;
      rightMost=rightMost.right;
    }

    current.element=rightMost.element;//将当前结点左子树下最大元素值赋值给当前结点
    if(parentOfrightMost.right==rightMost)
      parentOfrightMost.right=rightMost.left;
    else
      parentOfrightMost.left=rightMost.left;
  }
  return ture;
}
1.6 构造二叉查找树

Java集合框架API的设计模式遵循接口——抽象类——扩展类的顺序,这里我们仿照这种模式构造二叉树类。使用一个名为Tree的接口来定义树的所有常用操作,并提供名为AbstractTree的抽象类,该抽象类部分实现了Tree接口,扩展这个抽象类定义了一个具体的BinaryTree类。其中包含的方法如下。

  • Tree
方法名作用
boolean search(E e)搜索元素,成功则返回true
boolean insert(E e)插入元素,成功则返回true
boolean delete(E e)删除元素,成功则返回true
void inOrder()中序遍历二叉树
void preOrder()前序遍历二叉树
void postOrder()后序遍历二叉树
boolean isEmpty()如果树为空则返回true

  • BinaryTree
方法名作用
TreeNode root根结点
public BinaryTree()构造函数
public BinaryTree(E[] objects)由一个元素数组创建二叉树

具体实现代码如下:

public interface Tree<E extends Comparable<E>>{
  /*包括上述表格中接口的函数*/
}

public class BinaryTree<E extends Comparable<E>> implements Tree<E>{
  protected TreeNode<E> root;
  public BinaryTree(){}
  public BinaryTree(E[] objects){
    for(int i=0;i<objects.length;i++)
      insert(objects[i]);
  }
  /*下列函数详见前文*/
  public boolean insert(E e){}
  boolean insert(E e){}
  boolean delete(E e){}
  void inOrder(){}
  void preOrder(){}
  void postOrder(){}
  boolean isEmpty(){}
}
1.7小结

BST是一种分层的数据结构,本文主要简介了如何定义和实现BST类,并实现了搜索、插入、删除以及一系列遍历方法。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值