数据结构——树

树数据结构

1.定义:

由n个结点组成的一个具有层次关系的集合。

2.图解:


①每个结点由零个或多个子结点;
②无父结点的结点叫做根结点;
③每一个非根节点都只有一个父结点。

3.基本概念

①结点的度:一个结点含有的子树个数;
②叶结点:度为0的结点;
③分支结点:度不为0的结点;
④层次:从根结点开始,层次为1,后续为n(n>=2);
⑤树的度:树中最大的度;
⑥树的高度(深度):树中结点的最大层次;
⑦森林:去掉根结点的树;
⑧孩子结点:一个结点的直接后继结点;
⑨父结点,兄弟节点与⑧类似。

4.二叉树

(1)定义:度不超过2的树。

(2)图解:

在这里插入图片描述

(3)二叉树树的设计框架:

在这里插入图片描述
BinaryTree <Key extends Comparable<Key>Value value>
可实现根据Key去排序,继承Comparable类。

(4)代码实现:

<1>属性与构造方法
	//根结点
    private Node root;
    //元素个数
    private int N;

    //构造方法
    public BinaryTree_二叉树(){
        root = null;
        N = 0;
    }
<2>内部结点类Node
	//内部结点类Node
    private class Node {
        //键
        Key key;
        //值
        Value value;
        //左结点
        Node left;
        //右结点
        Node right;

        //构造方法
        public Node(Key key,Value value,Node left,Node right){
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
        }
    }
<3>功能方法
a.插入键值对
	//插入一个键值对方法
    public void put(Key key,Value value){
        put(root,key,value);
    }
    //在指定树x插入一个键值对方法
    public Node put(Node x,Key key,Value value){
        /*
        当树x为空时,进入结束条件
        ①创建出一个新结点,返回新结点即可
        ②元素个数+1
        * */
        if (x == null){
            N++;
            Node newNode = new Node(key,value,null,null);
            return newNode;
        }
        /*
        * 当树x不为空时,需继续向下遍历:
        * ①比较Key值大小,若Key的值较大,进入x的右结点
        *                若Key的值较小,进入x的左结点
        *                若Key的值与原来相同,则直接覆盖原来的Value;
        * ②元素个数+1
        * */
        if (key.compareTo(x.key)>0){
            x.right = put(x.right,key,value);
        }
        else if (key.compareTo(x.key)<0){
            x.left = put(x.left,key,value);
        }
        else {
            x.value = value;
        }
        //该return无实际作用。
        return null;
    }
b.根据Key查找Value
	//根据Key查找Value
    public Value get(Key key){
        return get(root,key);
    }
    //根据Key在树x中查找Value
    public Value get(Node x,Key key){
        //若树x为空,返回null即可
        if (x == null) {
            return null;
        }
        /*
        * 若树x不为空,比较树x的key与key值大小:
        *       ①当key的值比x树上的key大时,进入x的右结点
        *       ②当key的值比x树上的key小时,进入x的左结点
        *       ③当key的值与x树上的key相等时,返回x的Value即可
        * */
        if (key.compareTo(x.key)>0){
            return get(x.right,key);
        }else if (key.compareTo(x.key)<0){
            return get(x.left,key);
        }else{
            return x.value;
        }
    }
c.删除键值对(难)
	//删除树中的键值对
    public void delete(Key key){
        delete(root,key);
    }
    //删除指定树x中的键值对
    public Node delete(Node x,Key key){
        //当树x为空时,返回null即可
        if (x == null){
            return null;
        }
        /*
        * (1)当树x不为空,比较key与树x的key:
        *       ①当key值比x树上的key大,进入x的右子树
        *       ②当key值比x树上的key小,进入x的左子树
        *       ③当key值与x树上的key值相等,进入删除操作;
        * (2)删除操作;
        * (3)元素个数-1。
        * */

        //如果当前结点不为空:
        if (key.compareTo(x.key)>0){
            //向当前节点的右子树走:
            x.right = delete(x.right,key);
        }else if (key.compareTo(x.key)<0){
            //向当前节点的左子树走:
            x.left = delete(x.left,key);
        }else{//找到原树中键是key的结点:
            /*
             * 删除结点的思路:
             *1.找到要删除元素的右子树中的最小值:
             * */
            if (x.right == null){
                return x.left;
            }
            if (x.left == null){
                return x.right;
            }
            //若以上条件均不满足,则找右子树中的最小值:
            Node minNode = x.right;
            while (minNode.left != null){
                //如果最小结点仍有左节点,则向下遍历;
                minNode = minNode.left;
            }
            //删除右子树中最小的结点:
            Node n = x.right;
            while (n.left != null){
                if (n.left.left == null){
                    n.left = null;
                }else{
                    n = n.left;
                }
            }
            //让x结点的左子树成为minNode的左子树
            minNode.left = x.left;
            //让x结点的右子树成为minNode的右子树
            minNode.right = x.right;
            //让x结点的父结点指向minNode。
            x = minNode;
        }
        //元素个数-1:
        N--;
        return x;
    }
d.获取元素个数
	//获取元素个数
    public int size(){
        return N;
    }
e.获取树中最小值的键
	//获取树中最小值的键
    public Key min(){
        return min(root).key;
    }
    //获取指定树中x的最小值的结点
    public Node min(Node x){
        //当树x没有左结点时,自己本身就是最小值
        if (x.left == null){
            return x;
        }
        else{
            return min(x.left);
        }
    }
f.获取树中最大值的键
	//获取树中最大值的键
    public Key max(){
        return max(root).key;
    }
    //获取指定树中x的最大值的结点
    public Node max(Node x){
        //当树x不为空时,从树x开始,一直向右递归遍历即可得到最大结点。
        if (x.right == null){
            return x;
        }else{
            return max(x.right);
        }
    }
<4>二叉树几种遍历方法

对二叉树:在这里插入图片描述

前序遍历:
           ①顺序:1->2->3
           ②结果:ABDECFG
中序遍历:
           ①顺序:2->1->3
           ②结果:DBEACFG
后序遍历:
           ①顺序:2->3->1
           ②结果:DEBFGCA
层序遍历:

a.前序遍历
       A.思路分析:

A.当树x为空时,无需遍历,直接返回即可
B.当树x不为空时,递归调用该方法将key存入keys中即可。
      由于是前序遍历(当前结点应先输出):
           ①将当前结点x存入队列中,优先输出;
           ②如果x有左子树,递归将左子树传入即可;
           ③如果x有右子树,递归将右子树传入即可。

       B.代码实现:
	//获取整个树中的所有键
    public Queue<Key> preErgodic(){
        //创建一个新队列,调用获取root树上的所有键方法,返回队列即可。
       Queue<Key> keys = new Queue<>();
       preErgodic(root,keys);
       return keys;
    }
    //获取指定树x中的所有键,并存入keys队列中
    public void preErgodic(Node x,Queue<Key> keys){
        /*
        A.当树x为空时,无需遍历,直接返回即可
        B.当树x不为空时,递归调用该方法将key存入keys中即可。
            前序遍历:①将当前结点x存入队列中,优先输出;
                    ②如果x有左子树,递归将左子树传入即可;
                    ③如果x有右子树,递归将右子树传入即可。
         */
        if (x == null){
            return;
        }
        //将当前结点x的key存入队列keys中,优先输出
        keys.insert(x.key);
        //左子树遍历
        if (x.left != null){
            preErgodic(x.left,keys);
        }
        //右子树遍历
        if (x.right != null){
            preErgodic(x.right,keys);
        }
    }
b.中序遍历
       A.思路分析:

A.当树x为空时,无需遍历,直接返回即可
B.当树x不为空时,递归调用该方法将key存入keys中即可。
      由于是中序遍历(当前结点应在中间输出):
           ①如果x有左子树,递归将左子树传入即可;
           ②将当前结点x存入队列中,中间输出;
           ③如果x有右子树,递归将右子树传入即可。

       B.代码实现:
	//获取整个树中的键
    public Queue <Key> midErgodic() {
        Queue<Key> keys = new Queue<>();
        midErgodic(root,keys);
        return keys;
    }
    //获取指定树x上的键,并将其存入队列keys中
    public void midErgodic(Node x,Queue<Key> keys){
        //当当前树x为空时,直接返回即可
        if (x == null){
            return ;
        }
        //左子树存入队列
        if (x.left != null){
            midErgodic(x.left,keys);
        }
        //当前结点x存入队列中
        keys.insert(x.key);
        //右子树存入队列
        if (x.right != null){
            midErgodic(x.right,keys);
        }
    }
c.后序遍历
       A.思路分析:

A.当树x为空时,无需遍历,直接返回即可
B.当树x不为空时,递归调用该方法将key存入keys中即可。
      由于是后序遍历(当前结点应在最后输出):
           ①如果x有左子树,递归将左子树传入即可;
           ②如果x有右子树,递归将右子树传入即可。
           ③将当前结点x存入队列中,最后输出;

       B.代码实现:
	//获取整个树中的键
    public Queue<Key> afterErgodic(){
        Queue<Key> keys = new Queue<>();
        afterErgodic(root,keys);
        return keys;
    }
    //获取指定树x上的键
    public void afterErgodic(Node x,Queue<Key> keys){
        //当当前树x为空时,直接返回
        if (x == null){
            return;
        }
        //遍历左子树
        if (x.left != null){
            afterErgodic(x.left,keys);
        }
        //遍历右子树
        if (x.right != null){
            afterErgodic(x.right,keys);
        }
        //将当前节点x存入keys
        keys.insert(x.key);
    }
d.层序遍历:
       A.思路分析:

           ①创建两个队列,分别存储弹出的Node和Key;
           ②默认将根结点传入Node所在的队列中;
           ③循环遍历:当结点队列没有元素时停止遍历,每循环一次从结点队列中弹出一个元素,将其key存入keys队列中。
           ④弹出结点如果有左(右)结点,则继续向结点队列中插入即可。

     B.代码实现
//层序遍历——二叉树的高级遍历
public Queue<Key> layerErgodic(){
        //创建两个队列,分别存储弹出元素的key和node:
        Queue<Key> keys = new Queue <>();
        Queue<Node> nodes = new Queue <>();
        //默认将根节点放入nodes中:
        nodes.insert(root);
        //循环遍历,当队列中没有元素时停止循环:
        while(!nodes.isEmply()){
            //从队列中弹出一个结点,将key放入keys中:
            Node removeElement = nodes.remove();
            keys.insert(removeElement.key);
            //判断元素如果有左子树,将左子树进入队列:
            if (removeElement.left!=null){
                nodes.insert(removeElement.left);
            }
            //判断元素如果有右子树,将右子树进入队列:
            if (removeElement.right!=null){
                nodes.insert(removeElement.right);
            }
        }
        return keys;
    }

(5)二叉树的简单应用

<1>求二叉树的最大深度:
       a.思路分析:

          ①如果树无元素,直接返回0即可;
          ②如果树有元素,则分别求左右子结点的最大深度;
          ③比较左右子结点最大深度,取较大值即为二叉树的最大深度。

    b.代码实现:
	//求整棵树的最大深度
    public int maxDepth(){
        return maxDepth(root);
    }
    //求指定树x的最大深度
    public int maxDepth(Node x){
        //如果当前树x为空,直接返回0
        if (x == null){
            return 0;
        }
        //不为空则分别遍历左右两结点
        int leftDepth = maxDepth(x.left)+1;
        int rightDepth = maxDepth(x.right)+1;
        //返回左右两边较大值即可
        return leftDepth > rightDepth ? leftDepth : rightDepth;
    }

完!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值