zoukankan      html  css  js  c++  java
  • 数据结构(五):树

     

    一、 树的概述

      树是计算机中应用广泛的一种数据结构,日常生活中常见的图谱,公司组织结构等,都是树结构的数据。

      树结构在计算机中是根朝上,叶子结点向下的。如图,它是由N个有限结点组成的具有层次关系的集合。

       

      树有如下特点:

    • 没有父结点的称为根结点
    • 每个结点有0或多个子结点
    • 每一个非根结点只有一个父结点
    • 每个结点及其后代结点可以看成一颗子树,称为当前结点父结点的一颗子树

    二、 树的术语

    • 结点的度:结点所含子树的个数称为结点的度,如下图:2,3,4为1的子树,1的度为3。5,8和6为2的子树,2的度为2。
    • 叶结点:度为0的结点称为叶结点,如4即为叶结点
    • 分支结点:度不为0的结点称为分支结点。
    • 结点的层次:根结点层次为1,往下的后继结点层次为2,依次类推
    • 结点的层序编号:将结点的元素,从上到下,从左到右依次排序,如下图为12345678
    • 树的度:树中所有结点的度的最大值,不一定是根结点,如下截图的6若衍生很多的子结点,则度的最大值计算应该算6的子结点个数
    • 树的深度:层次最多的结点
    • 森林:去掉根结点后的N个不相交的集合,称为森林
    • 孩子结点:一个结点的后继结点称为孩子结点
    • 父结点:一个结点的前驱结点称为父结点
    • 兄弟结点:同一父结点的孩子结点互称为兄弟结点

      

    三、 二叉树的基本定义

      二叉树:度不超过2的数称为二叉树

       

      满二叉树:每一个结点度都达到最大值的树称为满二叉树

       

      完全二叉树:往二叉树放元素时,按从上到下,从左到右的次序去放,即度为0的叶子结点,只能出现在最下或次下层的称为完全二叉树

       

    四、 二叉查找树的构建

       

      构建如上图的二叉树,需要怎样声明对象和组成二叉树呢,见如下实现:

    public class Node<Key,Value> {

           //结点的键

           public Key key;

          

           //结点的值

           public Value value;

          

           //结点的左子结点

           public Node left;

          

           //结点的右子结点

           public Node right;

           public Node(Key key, Value value, Node left, Node right) {

                  super();

                  this.key = key;

                  this.value = value;

                  this.left = left;

                  this.right = right;

           }

          

           public static void main(String[] args) {

                  Node node1 = new Node(1, "A", null, null);

                  Node node2 = new Node(2, "B", null, null);

                  Node node3 = new Node(3, "C", null, null);

                  Node node4 = new Node(4, "D", null, null);

                  Node node5 = new Node(5, "E", null, null);

                  Node node6 = new Node(6, "F", null, null);

                 

                  node1.left = node2;

                  node1.right = node3;

                 

                  node2.left = node4;

                  node2.right = node5;

                 

                  node3.left = node6;

           }

    }

    五、 二叉查找树的实现

      二叉树插入键值对:

      (1)     当前二叉树的结点为空时,把新结点作为根结点

      (2)     当前二叉树的结点不为空时,若新结点的key小于当前结点,则继续查找当前结点的左子结点

      (3)     若新结点的key大于当前结点,则继续查找当前结点的右子结点

      (4)     若新结点的key等于当前结点,则将当前结点的value替换

       二叉树获取指定结点:

      (1)     从根结点开始,若查询的key小于当前结点,则继续查找当前结点的左子结点

      (2)     若查询的key大于当前结点,则继续查找当前结点的右子结点

      (3)     若查询的key等于当前结点,则返回当前结点

      二叉树删除指定结点:

      (1)     首先使用获取结点的方法,找到要删除的结点

      (2)     找到被删除结点右子树中最小的元素min,并删除它

      (3)     让被删除结点的左子树成为min的左子树,让被删除结点的右子树成为min的右子树

      (4)     让被删除结点的父节点指向min

    public class BinaryTree<Key extends Comparable<Key>,Value> {

           // 根结点声明

           public Node root;

          

           //树的元素个数

           public int N;

          

           public BinaryTree() {

                  root = null;

                  N=0;

           }

          

           /**

            * 二叉树元素个数

            * @return

            */

           public int size() {

                  return N;

           }

          

           /**

            * 二叉树是否为空

            * @return

            */

           public boolean isEmpty() {

                  return N==0;

           }

          

           /**

            * 往二叉树中添加元素

            * @param key

            * @param value

            */

           public void put(Key key,Value value) {

                  root = put(root, key, value);

           }

          

           /**

            * 往二叉树指定结点添加元素

            * @param node

            * @param key

            * @param value

            */

           public Node put(Node node,Key key,Value value) {

                  if(node==null) {

                         //总数+1

                         N++;

                         return new Node(key, value, null, null);

                  }

                 

                  int compare = key.compareTo((Key) node.key);

                 

                  if(compare>0) {

                         //存放的key大于当前结点,则继续遍历当前结点的右子树

                         node.right = put(node.right,key,value);

                  }else if(compare<0) {

                         //存放的key小于当前结点,则继续遍历当前结点的左子树

                         node.left = put(node.left,key,value);

                  }else {

                         //存放的key等于当前结点,则替换

                         node.value = value;

                  }

                 

                  return node;

           }

          

           /**

            * 从二叉树中获取key对应的value

            * @param key

            * @return

            */

           public Value get(Key key) {

                  return get(root,key);

           }

          

           /**

            * 从二叉树指定结点中获取key对应的value

            * @param node

            * @param key

            * @return

            */

           public Value get(Node node,Key key) {

                  if(node==null) {

                         return null;

                  }

                 

                  int compare = key.compareTo((Key) node.key);

                 

                  if(compare>0) {

                         //查找的key大于当前结点,则继续遍历当前结点的右子树

                         return get(node.right,key);

                  }else if(compare<0) {

                         //查找的key小于当前结点,则继续遍历当前结点的左子树

                         return get(node.left,key);

                  }else{

                         return (Value) node.value;

                  }

           }

          

           /**

            * 删除二叉树中指定结点

            * @param key

            */

           public Node delete(Key key) {

                  return delete(root,key);

           }

          

           /**

            * 删除二叉树中指定结点

            * @param node

            * @param key

            */

           public Node delete(Node node,Key key) {

                  if(node==null) {

                         return null;

                  }

                 

                  int compare = key.compareTo((Key) node.key);

                 

                  if(compare>0) {

                         //删除的key大于当前结点,则继续遍历当前结点的右子树,

                         node.right = delete(node.right,key);

                  }else if(compare<0) {

                         //删除的key大于当前结点,则继续遍历当前结点的右子树,

                         node.left = delete(node.left,key);

                  }else {

                         //被删除结点只有左结点,没有右结点的情况

                         if(node.left!=null && node.right == null) {

                                //总数减1

                                N--;

                                return node.left;

                         }

                        

                         //被删除结点只有右结点,没有左节点的情况

                         if(node.right!=null && node.left == null) {

                                //总数减1

                                N--;

                                return node.right;

                         }

                        

                         //找到被删除结点的右子树中的最小结点,用来取代被删除的结点

                         Node min = node.right;

                         while(min.left!=null) {

                                min = min.left;

                         }

                        

                         //删除被删除结点的右子树中的最小结点

                         Node delNode = node.right;

                         while(delNode.left !=null) {

                                if(delNode.left.left==null) {

                                       delNode.left = null;

                                }else {

                                       delNode = delNode.left;

                                }

                         }

                        

                         //让被删除结点右子树中的最小结点,取代被删除的结点

                         min.right = node.right;

                         min.left = node.left;

                         node = min;

                        

                         //总数减1

                         N--;

                  }

                  return node;

           }

    }

     

    六、 二叉查找树最小键

      查找二叉树中最小键的思路:从根结点找起,一直找当前结点的左子树,直到当前结点的左子树为空时,则当前结点为二叉查找树的的最小键

    /**

            * 查找二叉查找树的最小键

            * @return

            */

           public Key min() {

                  return (Key) min(root).key;

           }

          

           public Node min(Node node) {

                  if(node.left!=null) {

                         return min(node.left);

                  }else {

                         return node;

                  }

           }

    七、 二叉查找树最大键

      查找二叉树中最大键的思路:从根结点找起,一直找当前结点的右子树,直到当前结点的右子树为空时,则当前结点为二叉查找树的的最大键

    /**

            * 查找二叉查找树的最大键

            * @return

            */

           public Key max() {

                  return (Key) max(root).key;

           }

          

           public Node max(Node node) {

                  if(node.right!=null) {

                         return max(node.right);

                  }else {

                         return node;

                  }

           }

     

    八、 二叉树的最大深度

      计算二叉树最大深度的思路

    • 当只有一个根结点时为1,当根结点为null时是0
    • 计算左子树的最大深度
    • 计算右子树的最大深度
    • 比较左右子树最大深度,取较大值+1(根结点)

    /**

             * 查找二叉树的最大深度

            * @return

            */

           public int deepLength() {

                  return deepLength(root);

           }             

          

           public int deepLength(Node node) {

                  //根结点为空

                  if(node==null) {

                         return 0;

                  }

                 

                  //根结点不为空,定义左结点深度maxLeft,右结点深度maxRight,最大深度等于maxLeft和maxRight的较大值+1

                  int maxLeft = 0;

                  int maxRight = 0;

                  int max = 0;

                  System.out.println(node.key);

                 

                  //右子树不为空,则持续遍历当前结点的右子树

                  if(node.right!=null) {

                         maxRight = deepLength(node.right);

                  }

                 

                  //左子树不为空,则持续遍历当前结点的左子树

                  if(node.left!=null) {

                         maxLeft = deepLength(node.left);

                  }

                 

                  //最大深度计算

                  max = maxLeft > maxRight ? maxLeft+1 : maxRight+1;

                  return max;

           }

    九、 二叉树的遍历

      如线性表中的链表,数组,队列,栈一样,二叉树也有遍历查找的需求,由于树的结构和线性表有很大的差异,没有办法从前往后去查询,此时我们可以根据搜索路径来查找,即通过控制根结点

      左子树或右子树的访问顺序,来实现二叉树的遍历查询。

    •  前序遍历:先访问根结点,再访问左子树,后访问右子树
    • 中序遍历:先访问左子树,再访问根结点,后访问右子树
    • 后序遍历:先遍历左子树,再访问右子树,后访问根结点

      

      前序遍历结果:EBADCGFH

      中序遍历结果:ABCDEFGH

      后序遍历结果:ACDBFHGE

    十、 二叉树的遍历-前序遍历

      前序遍历的实现思路

    • 将当前结点的key放入队列中
    • 遍历当前结点的左子树,左子树不为空时,递归遍历左子树
    • 遍历当前结点的右子树,右子树不为空时,递归遍历右子树

    /**

            * 前序遍历实现

            */

           public Queue<Key> preSearch() {

                  Queue<Key> keys = new Queue<Key>();

                  preSearch(root, keys);

                  return keys;

           }

           public void preSearch(Node node, Queue<Key> keys) {

                  if (node == null) {

                         return;

                  }

                  // 将当前结点放入队列中

                  keys.enqueue((Key) node.key);

                  // 遍历左子树,不为空时递归遍历

                  if (node.left != null) {

                         preSearch(node.left, keys);

                  }

                  // 遍历右子树,不为空时递归遍历

                  if (node.right != null) {

                         preSearch(node.right, keys);

                  }

           }

    十一、     二叉树的遍历-后序遍历

      后序遍历的实现思路

    • 遍历当前结点的左子树,左子树不为空时,递归遍历左子树
    • 遍历当前结点的右子树,右子树不为空时,递归遍历右子树
    • 将当前结点的key放入队列中

    /**

            * 后序遍历实现

            */

           public Queue<Key> afterSearch() {

                  Queue<Key> keys = new Queue<Key>();

                  afterSearch(root, keys);

                  return keys;

           }

           public void afterSearch(Node node, Queue<Key> keys) {

                  if (node == null) {

                         return;

                  }

                  // 遍历左子树,不为空时递归遍历

                  if (node.left != null) {

                         afterSearch(node.left, keys);

                  }

                  // 遍历右子树,不为空时递归遍历

                  if (node.right != null) {

                         afterSearch(node.right, keys);

                  }

                 

                  // 将当前结点放入队列中

                  keys.enqueue((Key) node.key);

           }

     

    十二、     二叉树的遍历-中序遍历

      中序遍历的实现思路

    • 遍历当前结点的左子树,左子树不为空时,递归遍历左子树
    • 将当前结点的key放入队列中
    • 遍历当前结点的右子树,右子树不为空时,递归遍历右子树

    /**

            * 中序遍历实现

            */

           public Queue<Key> midSearch() {

                  Queue<Key> keys = new Queue<Key>();

                  midSearch(root, keys);

                  return keys;

           }

           public void midSearch(Node node, Queue<Key> keys) {

                  if (node == null) {

                         return;

                  }

                  // 遍历左子树,不为空时递归遍历

                  if (node.left != null) {

                         midSearch(node.left, keys);

                  }

                 

                  // 将当前结点放入队列中

                  keys.enqueue((Key) node.key);

                  // 遍历右子树,不为空时递归遍历

                  if (node.right != null) {

                         midSearch(node.right, keys);

                  }

    }

     

    十三、     二叉树的遍历-层序遍历

      层序遍历的实现思路

    • 创建队列A,存储当前结点
    • 循环队列A,首先弹出队列中的结点key,并存储到队列B中,如果当前结点的左子树不为空,则将左节点存储到队列A中,如果当前结点的右子树不为空,则将右结点存储到队列A中

           /**

            * 层序遍历实现

            */

           public Queue<Key> layerSearch() {

                  Queue<Node> A = new Queue<Node>();

                  Queue<Key> B = new Queue<Key>();

                  A.enqueue(root);

                  while (!A.isEmpty()) {

                         Node n = A.dequeue();

                         B.enqueue((Key) n.key);

                         if (n.left != null) {

                                A.enqueue(n.left);

                         }

                         if (n.right != null) {

                                A.enqueue(n.right);

                         }

                  }

                  return B;

           }

     

  • 相关阅读:
    python 语言打印直角三角形的几种方法
    python基础练习,循环、列表、字典、数组
    python命令行运行django项目, can't open file 'manage.py' 问题解决
    python数组列表、字典、拷贝、字符串
    Python输入与循环
    Python实现制度转换(货币,温度,长度)
    Matlab中图论工具箱的应用
    eclipse环境下日志打印输出
    mxGraph上下级节点与连线高亮显示代码
    mxgraph菜单配置及读取菜单节点名称方法
  • 原文地址:https://www.cnblogs.com/jiyukai/p/13991731.html
Copyright © 2011-2022 走看看