数据结构(java)之二叉树

1.        二叉树的逻辑结构

a)       定义:二叉树是一种非线性结构,是n个有限元素的集合,由一个根节点与两个不相交的左子树和右子树组成,当集合为空又称为空二叉树,一个元素也成为一个节点。二叉树是有序的。

b)       基本概念

                     i.            节点的度:节点下子树的个数,范围为0-2

                    ii.            叶节点:度为0的节点

                  iii.            分支节点:度不为0的节点

                  iv.            路径、路径长度:设n(i+1)n(i)的父节点,则n(1)…n(k)称为一条路径,路径长度为k-1

                    v.            节点层数:从根节点开始到叶节点,每个节点层数等于其父节点的节点层数+1,根节点的节点层数位1

                  vi.            数的深度:叶节点的最大节点层数

                 vii.            数的度:各节点度的最大值,数的度最大位2

                viii.            满二叉树:所有分支节点都有左子树和右子树且叶节点都在同一层的二叉树

                   ix.            完全二叉树:一棵深度为kn个节点的二叉树,对树中的节点按照从上到下,从左到右的顺序进行编号,树中编号为i的节点与满二叉树编号为i的节点位置相同则称为完全二叉树。特点是:叶子节点只能出现在最下层和次下层,且最下层的叶子节点集中在左边。(可以理解为在一棵深度为k满二叉树的第k层(共有2^(k-1)个节点)的最右边第一个节点开始向左去掉0(2^(k-1))0-2^(k-1)之间的奇数个节点

c)        主要性质

                     i.            非空二叉树的第k层最多有2^(k-1)个节点

                    ii.            一棵深度为k的二叉树最有有2^k-1个节点

                  iii.            对于一棵非空二叉树:叶子节点数=度为2的节点数+1

                  iv.            具有n个节点的满二叉树的深度为log(n+1),具有n个节点的完全二叉树的深度为log(n)+1

                    v.            对于具有n个节点的完全二叉树,按照从上到下从左到右的顺序进行排序,对于序号为i的节点

[1]      i>1,则双亲的序号为i/2的整数部分,i=1,则该节点是树的根节点;

[2]      2i<n,则i的左孩子为2i,若2i>n,则该节点无左孩子

[3]      2i+1<n,则i的右孩子为2i+1,若2i+1>n,则该节点无右孩子

d)       二叉树的抽象数据类型

                     i.            数据元素:具有相同类型的数据集合

                    ii.            数据结构:除根节点外,每个节点都有一个直接前驱,除叶节点外,每个节点都有1-2个直接后驱

                  iii.            数据操作:一般二叉树的操作定义在IBiTree接口中

publicinterface IBiTree<E> {

  //返回根节点

  TreeNode<E> getRoot();

  //返回fnode的左子树

  TreeNode<E> getLeftChild(TreeNode<E> fnode);  

  //返回fnode的右子树

  TreeNode<E> getRightChild(TreeNode<E> fnode);

  //fnode左子树添加节点

  void insertLeftChild(TreeNode<E> lcnode,TreeNode<E> fnode);

  //fnode的右子树添加节点

  void insertRightChild(TreeNode<E> rcnode,TreeNode<E> fnode);

  //删除fnode的左子树

  TreeNode<E> deleteLeftChild(TreeNode<E> fnode);

  //删除fnode的右子树

  TreeNode<E> deleteRightChild(TreeNode<E> fnode);

  //查找指定节点

  TreeNode<E> find(TreeNode<E> target);

  //以第前序方式遍历二叉树

  void pretraverse(TreeNode<E> root);

  //以中序方式遍历二叉树

  void intraverse(TreeNode<E> root);

  //以后序遍历二叉树

  void posttraverse(TreeNode<E> root);

  //以层序方式遍历二叉树

  void leveltraverse();

}

2.        二叉树的实现之顺序存储

a)       顺序存储结构:用一组连续的数组来存储二叉树数据,通常按照从左到右、从上到下的顺序进行存储,对于一般的二叉树存储在数组中,下标不能很好的反映二叉树中的节点逻辑关系,需要在数组中添加空节点,这样比较浪费存储空间。因此,一般用数组存储满二叉树或者完全二叉树比较合适。

3.        二叉树的实现之链式存储

a)       链式存储结构

                     i.            二叉链式存储结构:每个节点包括存储数据的元素,指向左子树的left和指向右子树的right,当左子树或右子树不存在时,将left或者right设置为null

                    ii.            三叉链式存储结构:相比二叉链式存储结构增加了指向父节点的parent,最常用的时二叉链式存储结构

b)       代码实现

//定义树节点,包括数据,权值,左子树,右子树

publicclass TreeNode<E> {

  private E e;

  privateintweight;

  private TreeNode<E> left;

  private TreeNode<E> right;

  //不带权值的构造方法

  public TreeNode(E e) {

     super();

     this.e = e;

     this.weight=0;

     this.left = null;

     this.right = null;

  }

  //带权值的构造方法

  public TreeNode(E e, intweight) {

     super();

     this.e = e;

     this.weight = weight;

  }

 

  public E getE() {

     returne;

  }

  publicvoid setE(E e) {

     this.e = e;

  }

  public TreeNode<E> getLeft() {

     returnleft;

  }

  publicvoid setLeft(TreeNode<E> left) {

     this.left = left;

  }

  public TreeNode<E> getRight() {

     returnright;

  }

  publicvoid setRight(TreeNode<E> right) {

     this.right = right;

  }

}

//创建普通二叉树类

publicclass MyLinkTree<E> implements IBiTree<E> {

  private TreeNode<E> root;

  //初始化二叉树

  public MyLinkTree(TreeNode<E> root) {

     super();

     this.root = root;

  }

  //返回树的根节点

  @Override

  public TreeNode<E> getRoot() {

     returnthis.root;

  }

  //返回指定节点的左孩子

  @Override

  public TreeNode<E> getLeftChild(TreeNode<E> fnode) {

     TreeNode<E> temp=find(fnode);

     returntemp.getLeft();

  }

  //返回指定节点的右孩子

  @Override

  public TreeNode<E> getRightChild(TreeNode<E> fnode) {

     TreeNode<E> temp=find(fnode);

     returntemp.getRight();

  }

  //在指定节点插入左孩子,如过其左孩子已经存在,将原近年来的左孩子作为新建左孩子的左子树

  @Override

  publicvoid insertLeftChild(TreeNode<E> lcnode, TreeNode<E> fnode) {

     TreeNode<E> temp=find(fnode);

     if(temp.getLeft()!=null) {

         TreeNode<E> left=temp.getLeft();

         temp.setLeft(lcnode);

         lcnode.setLeft(left);

     }

     else

         temp.setLeft(lcnode);

  }

  //在指定节点插入右孩子,如果其右孩子存在,将其原来的右孩子作为新建右孩子的右子树

  @Override

  publicvoid insertRightChild(TreeNode<E> rcnode, TreeNode<E> fnode) {

     TreeNode<E> temp=find(fnode); //找到指定节点

     if(temp.getRight()!=null) {

         TreeNode<E> right=temp.getRight();

         temp.setRight(rcnode);

         rcnode.setRight(right);

     }

     else

         temp.setRight(rcnode);

  }

  //删除指定节点的左子树(将其左子树下的所有节点删除)

  @Override

  public TreeNode<E> deleteLeftChild(TreeNode<E> fnode) {

     TreeNode<E> temp=find(fnode);

     TreeNode<E> lcnode=temp.getLeft();

     temp.setLeft(null);

     returnlcnode;

  }

  //删除指定节点的右子树(将其右子树下的所有节点删除)

  @Override

  public TreeNode<E> deleteRightChild(TreeNode<E> fnode) {

     TreeNode<E> temp=find(fnode);

     TreeNode<E> rcnode=temp.getRight();

     temp.setRight(null);

     returnrcnode;

  }

  //利用层序遍历找到指定节点

  public TreeNode<E> find(TreeNode<E> target){

     TreeNode<E> temp=this.root;

     IQueue<TreeNode> queue=new MylinkQueue<>();

     queue.enqueue(temp);

     while(!queue.isEmpty()) {

         temp=queue.dequeue();

         if(temp==target)

            returntemp;

         if(temp.getLeft()!=null)

            queue.enqueue(temp.getLeft());

         if(temp.getRight()!=null)

            queue.enqueue(temp.getRight());

     }

     returnnull;

  }

  //前序遍历

  @Override

  publicvoid pretraverse(TreeNode<E> root) {

     if(root!=null) {

         System.out.println(root.getE());

         pretraverse(root.getLeft());

         pretraverse(root.getRight());

     }

  }

  //中序遍历

  @Override

  publicvoid intraverse(TreeNode<E> root) {

     if(root!=null) {

         intraverse(root.getLeft());

         System.out.println(root.getE());

         intraverse(root.getRight());

     }

  }

  //后续遍历

  @Override

  publicvoid posttraverse(TreeNode<E> root) {

     if(root!=null) {

         posttraverse(root.getLeft());

         posttraverse(root.getRight());

         System.out.println(root.getE());

     }

  }

  //层序遍历

  @Override

  publicvoid leveltraverse() {

     IQueue<TreeNode<E>> queue=new MylinkQueue<>();

     TreeNode<E> temp=this.root;

     queue.enqueue(temp);

     while(!queue.isEmpty()) {

         temp=queue.dequeue();

         System.out.println(temp.getE());

         if(temp.getLeft()!=null)

            queue.enqueue(temp.getLeft());

         if(temp.getRight()!=null)

            queue.enqueue(temp.getRight());

     }

  }

}

4.   二叉排序树(二叉查找树)

a)   性质:左子树的值要小于其根节点的值,右子树的值要大于其根节点值

b)   二叉查找树的基本操作

        i.     插入:与各个根节点的值比较,大于根节点的值则去右子树寻找插入位置,小于根节点的值则取左子树寻找插入位置

      ii.     删除:借助一个方法找到需要删除节点的父节点,如果这个节点是一个叶子节点,直接将其父节点的左孩子或者右孩子设为null;如果这个节点是一个根节点,且只有一个左孩子或者右孩子,则将其左孩子或右孩子替换到要删除的节点;如果这个节点又左右两个孩子,一种方法是取其左子树的最大值的节点值替代此节点的值,然后删除左子树值最大的节点,另一种方法是取其右子树的最小值代替此节点的值,然后删除其右子树值最大的节点,这里我们选择第一种方法

c)   代码实现如下

//创建二叉查找树,一般二叉查找树的数据元素类型都是Integer

publicclass MyBinaryTree {

  private TreeNode<Integer> root;

  //初始化二叉查找树

  public MyBinaryTree(inte) {

     super();

     this.root = new TreeNode<Integer>(e);

  }

  //返回树的根节点

  public  TreeNode<Integer> getRoot() {

     returnthis.root;

  }

  //将数据插入到二叉查找树的指定位置

  publicvoid insert(inte) {

     TreeNode<Integer> temp=this.root;

     TreeNode<Integer> node=new TreeNode<Integer>(e);

     while(temp!=null) {

         //先比较数值与节点大小,比节点值大向节点的右子树搜索,比节点小向左子树搜索

         if(e>temp.getE()) {

            //如果节点的右子树为空,则在该节点将新建节点插入作为右子树并返回

            if(temp.getRight()==null) {

                temp.setRight(node);

                return;

            }

            else

                temp=temp.getRight();

         }

         else {

            //如果节点的左子树为空,则在该节点将新建节点插入作为左子树并返回

            if(temp.getLeft()==null) {

                temp.setLeft(node);

                return;

            }

            else

                temp=temp.getLeft();

         }

     }  

  }

  //二叉树的删除

  publicint delete(intk) {

     //首先找到这个节点的父节点

     TreeNode<Integer> tempf=findFather(k);

     //再找到这个节点

     TreeNode<Integer> temp;

     TreeNode<Integer> tempc;

     if(k>tempf.getE())

         temp=tempf.getRight();

     else

         temp=tempf.getLeft();

     //如果是叶子节点,直接删除

     if(temp.getLeft()==null&&temp.getRight()==null) {

         if(tempf.getLeft()==temp)

            tempf.setLeft(null);

         else

            tempf.setRight(null);

         returntemp.getE();

     }

     //如果是根节点且只有一个子节点,将其子节点替换到要删除的节点位置

     if(temp.getLeft()==null||temp.getRight()==null) {

         if(temp.getLeft()==null)

            tempc=temp.getRight();

         else

            tempc=temp.getLeft();

         if(tempf.getLeft()==temp) {

            tempf.setLeft(tempc);

            returntemp.getE();

         }

         if(tempf.getRight()==temp) {

            tempf.setRight(tempc);

             returntemp.getE();

         }

     }

     //如果是根节点且有左右子节点,找出其左子树最大的节点或者右子树最小节点替换将要删除的点

     else {

         //左子树最大节点指从这个节点开始最左边

         tempc=temp.getLeft();

         while(tempc.getRight()!=null)

            tempc=tempc.getRight();

         delete(tempc.getE());

         intm=temp.getE();

         temp.setE(tempc.getE());

         returnm;

     }

     return -1;

  }

  //先序遍历

  publicvoid pretraverse(TreeNode<Integer> root) {

     if(root!=null) {

         System.out.println(root.getE());

         pretraverse(root.getLeft());

         pretraverse(root.getRight());

     }

  }

  //中序遍历

  publicvoid intraverse(TreeNode<Integer> root) {

     if(root!=null) {

         intraverse(root.getLeft());

         System.out.println(root.getE());

         intraverse(root.getRight());

     }

  }

  //后续遍历

  publicvoid posttraverse(TreeNode<Integer> root) {

     if(root!=null) {

         posttraverse(root.getLeft());

         posttraverse(root.getRight());

         System.out.println(root.getE());

     }

  }

  //层序遍历

  publicvoid leveltraverse() {

     IQueue<TreeNode<Integer>> queue=new MylinkQueue<>();

     TreeNode<Integer> temp=this.root;

     queue.enqueue(temp);

     while(!queue.isEmpty()) {

         temp=queue.dequeue();

         System.out.println(temp.getE());

         if(temp.getLeft()!=null)

            queue.enqueue(temp.getLeft());

         if(temp.getRight()!=null)

            queue.enqueue(temp.getRight());

     }

  }

  //找到指定节点的父节点

  public TreeNode<Integer> findFather(intk){

     IQueue<TreeNode<Integer>> queue=new MylinkQueue<>();

     queue.enqueue(this.getRoot());

     TreeNode<Integer> tempf;

     TreeNode<Integer> tempc;

     if(k==this.getRoot().getE())

         returnnull;

     if(k>this.getRoot().getE())

         queue.enqueue(this.getRoot().getRight());

     if(k<this.getRoot().getE())

         queue.enqueue(this.getRoot().getLeft());

     while(!queue.isEmpty()) {

         tempf=queue.dequeue();

         tempc=queue.dequeue();

         if(k>tempc.getE()) {

            queue.enqueue(tempc);

            queue.enqueue(tempc.getRight());

         }

         if(k==tempc.getE()) {

            returntempf;

         }

         if(k<tempc.getE()) {

            queue.enqueue(tempc);

            queue.enqueue(tempc.getLeft());

         }

     }

     returnnull;

  }

}

5.   哈夫曼树

a)   概念:也叫最优二叉树,指由根节点到各个节点的路径长度乘以节点的权值,使得带权路径之和最小。

b)   要使得带权路径之和最小,则应该使权值大的节点离根节点近,权值小的节点离根节点远

c)   步骤:

        i.     首先以每一个节点为根节点建立一棵树,形成一个集合

      ii.     选出这些树中根节点权值最小的和次最小的两棵树作为左子树和右子树,两棵子树权值和为根节点形成一棵新的树

     iii.     在原集合中删除作为左右子树的两棵树,并将新的树加入集合

      iv.     重复ii,iii直到集合中只剩下一棵树

 

        v.     假设每棵左子树到其父节点的边为0,右子树到其父节点的边1,按照先序遍历或者层序遍历求根节点到每个节点边的序列作为字符编码

1.        二叉树的逻辑结构

a)       定义:二叉树是一种非线性结构,是n个有限元素的集合,由一个根节点与两个不相交的左子树和右子树组成,当集合为空又称为空二叉树,一个元素也成为一个节点。二叉树是有序的。

b)       基本概念

                     i.            节点的度:节点下子树的个数,范围为0-2

                    ii.            叶节点:度为0的节点

                  iii.            分支节点:度不为0的节点

                  iv.            路径、路径长度:设n(i+1)n(i)的父节点,则n(1)…n(k)称为一条路径,路径长度为k-1

                    v.            节点层数:从根节点开始到叶节点,每个节点层数等于其父节点的节点层数+1,根节点的节点层数位1

                  vi.            数的深度:叶节点的最大节点层数

                 vii.            数的度:各节点度的最大值,数的度最大位2

                viii.            满二叉树:所有分支节点都有左子树和右子树且叶节点都在同一层的二叉树

                   ix.            完全二叉树:一棵深度为kn个节点的二叉树,对树中的节点按照从上到下,从左到右的顺序进行编号,树中编号为i的节点与满二叉树编号为i的节点位置相同则称为完全二叉树。特点是:叶子节点只能出现在最下层和次下层,且最下层的叶子节点集中在左边。(可以理解为在一棵深度为k满二叉树的第k层(共有2^(k-1)个节点)的最右边第一个节点开始向左去掉0(2^(k-1))0-2^(k-1)之间的奇数个节点

c)        主要性质

                     i.            非空二叉树的第k层最多有2^(k-1)个节点

                    ii.            一棵深度为k的二叉树最有有2^k-1个节点

                  iii.            对于一棵非空二叉树:叶子节点数=度为2的节点数+1

                  iv.            具有n个节点的满二叉树的深度为log(n+1),具有n个节点的完全二叉树的深度为log(n)+1

                    v.            对于具有n个节点的完全二叉树,按照从上到下从左到右的顺序进行排序,对于序号为i的节点

[1]      i>1,则双亲的序号为i/2的整数部分,i=1,则该节点是树的根节点;

[2]      2i<n,则i的左孩子为2i,若2i>n,则该节点无左孩子

[3]      2i+1<n,则i的右孩子为2i+1,若2i+1>n,则该节点无右孩子

d)       二叉树的抽象数据类型

                     i.            数据元素:具有相同类型的数据集合

                    ii.            数据结构:除根节点外,每个节点都有一个直接前驱,除叶节点外,每个节点都有1-2个直接后驱

                  iii.            数据操作:一般二叉树的操作定义在IBiTree接口中

publicinterface IBiTree<E> {

  //返回根节点

  TreeNode<E> getRoot();

  //返回fnode的左子树

  TreeNode<E> getLeftChild(TreeNode<E> fnode);  

  //返回fnode的右子树

  TreeNode<E> getRightChild(TreeNode<E> fnode);

  //fnode左子树添加节点

  void insertLeftChild(TreeNode<E> lcnode,TreeNode<E> fnode);

  //fnode的右子树添加节点

  void insertRightChild(TreeNode<E> rcnode,TreeNode<E> fnode);

  //删除fnode的左子树

  TreeNode<E> deleteLeftChild(TreeNode<E> fnode);

  //删除fnode的右子树

  TreeNode<E> deleteRightChild(TreeNode<E> fnode);

  //查找指定节点

  TreeNode<E> find(TreeNode<E> target);

  //以第前序方式遍历二叉树

  void pretraverse(TreeNode<E> root);

  //以中序方式遍历二叉树

  void intraverse(TreeNode<E> root);

  //以后序遍历二叉树

  void posttraverse(TreeNode<E> root);

  //以层序方式遍历二叉树

  void leveltraverse();

}

2.        二叉树的实现之顺序存储

a)       顺序存储结构:用一组连续的数组来存储二叉树数据,通常按照从左到右、从上到下的顺序进行存储,对于一般的二叉树存储在数组中,下标不能很好的反映二叉树中的节点逻辑关系,需要在数组中添加空节点,这样比较浪费存储空间。因此,一般用数组存储满二叉树或者完全二叉树比较合适。

3.        二叉树的实现之链式存储

a)       链式存储结构

                     i.            二叉链式存储结构:每个节点包括存储数据的元素,指向左子树的left和指向右子树的right,当左子树或右子树不存在时,将left或者right设置为null

                    ii.            三叉链式存储结构:相比二叉链式存储结构增加了指向父节点的parent,最常用的时二叉链式存储结构

b)       代码实现

//定义树节点,包括数据,权值,左子树,右子树

publicclass TreeNode<E> {

  private E e;

  privateintweight;

  private TreeNode<E> left;

  private TreeNode<E> right;

  //不带权值的构造方法

  public TreeNode(E e) {

     super();

     this.e = e;

     this.weight=0;

     this.left = null;

     this.right = null;

  }

  //带权值的构造方法

  public TreeNode(E e, intweight) {

     super();

     this.e = e;

     this.weight = weight;

  }

 

  public E getE() {

     returne;

  }

  publicvoid setE(E e) {

     this.e = e;

  }

  public TreeNode<E> getLeft() {

     returnleft;

  }

  publicvoid setLeft(TreeNode<E> left) {

     this.left = left;

  }

  public TreeNode<E> getRight() {

     returnright;

  }

  publicvoid setRight(TreeNode<E> right) {

     this.right = right;

  }

}

//创建普通二叉树类

publicclass MyLinkTree<E> implements IBiTree<E> {

  private TreeNode<E> root;

  //初始化二叉树

  public MyLinkTree(TreeNode<E> root) {

     super();

     this.root = root;

  }

  //返回树的根节点

  @Override

  public TreeNode<E> getRoot() {

     returnthis.root;

  }

  //返回指定节点的左孩子

  @Override

  public TreeNode<E> getLeftChild(TreeNode<E> fnode) {

     TreeNode<E> temp=find(fnode);

     returntemp.getLeft();

  }

  //返回指定节点的右孩子

  @Override

  public TreeNode<E> getRightChild(TreeNode<E> fnode) {

     TreeNode<E> temp=find(fnode);

     returntemp.getRight();

  }

  //在指定节点插入左孩子,如过其左孩子已经存在,将原近年来的左孩子作为新建左孩子的左子树

  @Override

  publicvoid insertLeftChild(TreeNode<E> lcnode, TreeNode<E> fnode) {

     TreeNode<E> temp=find(fnode);

     if(temp.getLeft()!=null) {

         TreeNode<E> left=temp.getLeft();

         temp.setLeft(lcnode);

         lcnode.setLeft(left);

     }

     else

         temp.setLeft(lcnode);

  }

  //在指定节点插入右孩子,如果其右孩子存在,将其原来的右孩子作为新建右孩子的右子树

  @Override

  publicvoid insertRightChild(TreeNode<E> rcnode, TreeNode<E> fnode) {

     TreeNode<E> temp=find(fnode);//找到指定节点

     if(temp.getRight()!=null) {

         TreeNode<E> right=temp.getRight();

         temp.setRight(rcnode);

         rcnode.setRight(right);

     }

     else

         temp.setRight(rcnode);

  }

  //删除指定节点的左子树(将其左子树下的所有节点删除)

  @Override

  public TreeNode<E> deleteLeftChild(TreeNode<E> fnode) {

     TreeNode<E> temp=find(fnode);

     TreeNode<E> lcnode=temp.getLeft();

     temp.setLeft(null);

     returnlcnode;

  }

  //删除指定节点的右子树(将其右子树下的所有节点删除)

  @Override

  public TreeNode<E> deleteRightChild(TreeNode<E> fnode) {

     TreeNode<E> temp=find(fnode);

     TreeNode<E> rcnode=temp.getRight();

     temp.setRight(null);

     returnrcnode;

  }

  //利用层序遍历找到指定节点

  public TreeNode<E> find(TreeNode<E> target){

     TreeNode<E> temp=this.root;

     IQueue<TreeNode> queue=new MylinkQueue<>();

     queue.enqueue(temp);

     while(!queue.isEmpty()) {

         temp=queue.dequeue();

         if(temp==target)

            returntemp;

         if(temp.getLeft()!=null)

            queue.enqueue(temp.getLeft());

         if(temp.getRight()!=null)

            queue.enqueue(temp.getRight());

     }

     returnnull;

  }

  //前序遍历

  @Override

  publicvoid pretraverse(TreeNode<E> root) {

     if(root!=null) {

         System.out.println(root.getE());

         pretraverse(root.getLeft());

         pretraverse(root.getRight());

     }

  }

  //中序遍历

  @Override

  publicvoid intraverse(TreeNode<E> root) {

     if(root!=null) {

         intraverse(root.getLeft());

         System.out.println(root.getE());

         intraverse(root.getRight());

     }

  }

  //后续遍历

  @Override

  publicvoid posttraverse(TreeNode<E> root) {

     if(root!=null) {

         posttraverse(root.getLeft());

         posttraverse(root.getRight());

         System.out.println(root.getE());

     }

  }

  //层序遍历

  @Override

  publicvoid leveltraverse() {

     IQueue<TreeNode<E>> queue=new MylinkQueue<>();

     TreeNode<E> temp=this.root;

     queue.enqueue(temp);

     while(!queue.isEmpty()) {

         temp=queue.dequeue();

         System.out.println(temp.getE());

         if(temp.getLeft()!=null)

            queue.enqueue(temp.getLeft());

         if(temp.getRight()!=null)

            queue.enqueue(temp.getRight());

     }

  }

}

4.   二叉排序树(二叉查找树)

a)   性质:左子树的值要小于其根节点的值,右子树的值要大于其根节点值

b)   二叉查找树的基本操作

        i.     插入:与各个根节点的值比较,大于根节点的值则去右子树寻找插入位置,小于根节点的值则取左子树寻找插入位置

      ii.     删除:借助一个方法找到需要删除节点的父节点,如果这个节点是一个叶子节点,直接将其父节点的左孩子或者右孩子设为null;如果这个节点是一个根节点,且只有一个左孩子或者右孩子,则将其左孩子或右孩子替换到要删除的节点;如果这个节点又左右两个孩子,一种方法是取其左子树的最大值的节点值替代此节点的值,然后删除左子树值最大的节点,另一种方法是取其右子树的最小值代替此节点的值,然后删除其右子树值最大的节点,这里我们选择第一种方法

c)   代码实现如下

//创建二叉查找树,一般二叉查找树的数据元素类型都是Integer

publicclass MyBinaryTree {

  private TreeNode<Integer> root;

  //初始化二叉查找树

  public MyBinaryTree(inte) {

     super();

     this.root = new TreeNode<Integer>(e);

  }

  //返回树的根节点

  public  TreeNode<Integer> getRoot() {

     returnthis.root;

  }

  //将数据插入到二叉查找树的指定位置

  publicvoid insert(inte) {

     TreeNode<Integer> temp=this.root;

     TreeNode<Integer> node=new TreeNode<Integer>(e);

     while(temp!=null) {

         //先比较数值与节点大小,比节点值大向节点的右子树搜索,比节点小向左子树搜索

         if(e>temp.getE()) {

            //如果节点的右子树为空,则在该节点将新建节点插入作为右子树并返回

            if(temp.getRight()==null) {

                temp.setRight(node);

                return;

            }

            else

                temp=temp.getRight();

         }

         else {

            //如果节点的左子树为空,则在该节点将新建节点插入作为左子树并返回

            if(temp.getLeft()==null) {

                temp.setLeft(node);

                return;

            }

            else

                temp=temp.getLeft();

         }

     }  

  }

  //二叉树的删除

  publicint delete(intk) {

     //首先找到这个节点的父节点

     TreeNode<Integer> tempf=findFather(k);

     //再找到这个节点

     TreeNode<Integer> temp;

     TreeNode<Integer> tempc;

     if(k>tempf.getE())

         temp=tempf.getRight();

     else

         temp=tempf.getLeft();

     //如果是叶子节点,直接删除

     if(temp.getLeft()==null&&temp.getRight()==null) {

         if(tempf.getLeft()==temp)

            tempf.setLeft(null);

         else

            tempf.setRight(null);

         returntemp.getE();

     }

     //如果是根节点且只有一个子节点,将其子节点替换到要删除的节点位置

     if(temp.getLeft()==null||temp.getRight()==null) {

         if(temp.getLeft()==null)

            tempc=temp.getRight();

         else

            tempc=temp.getLeft();

         if(tempf.getLeft()==temp) {

            tempf.setLeft(tempc);

            returntemp.getE();

         }

         if(tempf.getRight()==temp) {

            tempf.setRight(tempc);

             returntemp.getE();

         }

     }

     //如果是根节点且有左右子节点,找出其左子树最大的节点或者右子树最小节点替换将要删除的点

     else {

         //左子树最大节点指从这个节点开始最左边

         tempc=temp.getLeft();

         while(tempc.getRight()!=null)

            tempc=tempc.getRight();

         delete(tempc.getE());

         intm=temp.getE();

         temp.setE(tempc.getE());

         returnm;

     }

     return -1;

  }

  //先序遍历

  publicvoid pretraverse(TreeNode<Integer> root) {

     if(root!=null) {

         System.out.println(root.getE());

         pretraverse(root.getLeft());

         pretraverse(root.getRight());

     }

  }

  //中序遍历

  publicvoid intraverse(TreeNode<Integer> root) {

     if(root!=null) {

         intraverse(root.getLeft());

         System.out.println(root.getE());

         intraverse(root.getRight());

     }

  }

  //后续遍历

  publicvoid posttraverse(TreeNode<Integer> root) {

     if(root!=null) {

         posttraverse(root.getLeft());

         posttraverse(root.getRight());

         System.out.println(root.getE());

     }

  }

  //层序遍历

  publicvoid leveltraverse() {

     IQueue<TreeNode<Integer>> queue=new MylinkQueue<>();

     TreeNode<Integer> temp=this.root;

     queue.enqueue(temp);

     while(!queue.isEmpty()) {

         temp=queue.dequeue();

         System.out.println(temp.getE());

         if(temp.getLeft()!=null)

            queue.enqueue(temp.getLeft());

         if(temp.getRight()!=null)

            queue.enqueue(temp.getRight());

     }

  }

  //找到指定节点的父节点

  public TreeNode<Integer> findFather(intk){

     IQueue<TreeNode<Integer>> queue=new MylinkQueue<>();

     queue.enqueue(this.getRoot());

     TreeNode<Integer> tempf;

     TreeNode<Integer> tempc;

     if(k==this.getRoot().getE())

         returnnull;

     if(k>this.getRoot().getE())

         queue.enqueue(this.getRoot().getRight());

     if(k<this.getRoot().getE())

         queue.enqueue(this.getRoot().getLeft());

     while(!queue.isEmpty()) {

         tempf=queue.dequeue();

         tempc=queue.dequeue();

         if(k>tempc.getE()) {

            queue.enqueue(tempc);

            queue.enqueue(tempc.getRight());

         }

         if(k==tempc.getE()) {

            returntempf;

         }

         if(k<tempc.getE()) {

            queue.enqueue(tempc);

            queue.enqueue(tempc.getLeft());

         }

     }

     returnnull;

  }

}

5.   哈夫曼树

a)   概念:也叫最优二叉树,指由根节点到各个节点的路径长度乘以节点的权值,使得带权路径之和最小。

b)   要使得带权路径之和最小,则应该使权值大的节点离根节点近,权值小的节点离根节点远

c)   步骤:

        i.     首先以每一个节点为根节点建立一棵树,形成一个集合

      ii.     选出这些树中根节点权值最小的和次最小的两棵树作为左子树和右子树,两棵子树权值和为根节点形成一棵新的树

     iii.     在原集合中删除作为左右子树的两棵树,并将新的树加入集合

      iv.     重复ii,iii直到集合中只剩下一棵树

        v.     假设每棵左子树到其父节点的边为0,右子树到其父节点的边1,按照先序遍历或者层序遍历求根节点到每个节点边的序列作为字符编码

 

posted @ 2018-08-03 20:04  带带大璞璞  阅读(499)  评论(0编辑  收藏  举报