二叉树

一、二叉树定义[以下数据来自百度百科]

二叉树是每个结点最多有两个子树的树结构。
二叉树有5种形态,分别是

  • 空二叉树【没有结点】

  • 只有根节点的二叉树

  • 只有左子树

  • 只有右子树

  • 完全二叉树
    二叉树的五种形态
    按其类型可以分为

  • 完全二叉树【每层的结点树达到了其最大值】

  • 满二叉树【除叶结点外其他结点都有左右结点,且叶结点都处于最底层】

  • 平衡二叉树【AVL树,左右子树的高度差不超过1】
    相关名词:

  • 树的结点(node):包含一个数据元素及若干指向子树的分支;

  • 孩子结点(child node):结点的子树的根称为该结点的孩子;

  • 双亲结点:B 结点是A 结点的孩子,则A结点是B 结点的双亲;

  • 兄弟结点:同一双亲的孩子结点; 堂兄结点:同一层上结点;

  • 祖先结点: 从根到该结点的所经分支上的所有结点

  • 子孙结点:以某结点为根的子树中任一结点都称为该结点的子孙

  • 结点层:根结点的层定义为1;根的孩子为第二层结点,依此类推;

  • 树的深度:树中最大的结点层

  • 结点的度:结点子树的个数【度只有0,1,2三种情况】

  • 树的度: 树中最大的结点度。

  • 叶子结点:也叫终端结点,是度为 0 的结点;

  • 分枝结点:度不为0的结点;

  • 有序树:子树有序的树,如:家族树;

  • 无序树:不考虑子树的顺序

二、二叉树性质

  1. 在二叉树的第i(i>=1)层最多有2^(i - 1)个结点。
  2. 深度为k(k>=0)的二叉树最少有k个结点,最多有2^k-1个结点。
  3. 对于任一棵非空二叉树,若其叶结点数为n0,度为2的非叶结点数为n2,则n0 = n2 +1。
  4. 具有n个结点的完全二叉树的深度为int_UP(log(2,n+1))。
  5. 如果将一棵有n个结点的完全二叉树自顶向下,同一层自左向右连续给结点编号1,2,3,......,n,然后按此结点编号将树中各结点顺序的存放于一个一维数组,并简称编号为i的结点为结点i( i>=1 && i<=n),则有以下关系:
    (1)若 i= 1,则结点i为根,无父结点;若 i> 1,则结点 i 的父结点为结点int_DOWN(i / 2);
    (2)若 2*i <= n,则结点 i 的左子女为结点 2*i;
    (3)若2*i<=n,则结点i的右子女为结点2*i+1;
    (4)若结点编号i为奇数,且i!=1,它处于右兄弟位置,则它的左兄弟为结点i-1;
    (5)若结点编号i为偶数,且i!=n,它处于左兄弟位置,则它的右兄弟为结点i+1;
    (6)结点i所在的层次为 int_DOWN(log(2,i))+1。

三、二叉树的常见算法

3.1 先序遍历

先访问本结点,再访问左结点,右结点。一个典型特征是根节点在第一位

public List<Object> preOrder(TreeNode node,List<Object> list){
        if(node!=null){
            list.add(node);
            preOrder(node.getLeftTr(),list);
            preOrder(node.getRightTr(),list);
        }
        return list;
    }

3.2 中序遍历

先访问左结点,再访问本结点,右结点。特征就是根节点居中【完全二叉树】
如果是有序二叉树,那么使用该排序方式可以大小有序的打印该树。

public List<Object> midOrder(TreeNode node,List<Object> list){
        if(node!=null){
            midOrder(node.getLeftTr(),list);
            list.add(node);
            midOrder(node.getRightTr(),list);
        }
        return list;
    }

3.3 后续遍历

先访问左结点,再访问右结点,本结点。根节点在最后一位

 public List<Object> lastOrder(TreeNode node,List<Object> list){
        if(node!=null){
            lastOrder(node.getLeftTr(),list);
            lastOrder(node.getRightTr(),list);
            list.add(node);
        }
        return list;
    }

3.4 层次遍历

从根节点(第一层)开始遍历

 public List<Object> rankOrder(TreeNode node,List<Object> list){
        Queue<TreeNode> q= Queues.newArrayDeque();
        q.offer(node);
        while(!q.isEmpty()){
            TreeNode n=q.poll();
            if(n!=null){
                list.add(n);
                if(n.hasLeftTr()){
                    q.offer(n.getLeftTr());
                }
                if(n.hasRightTr()){
                    q.offer(n.getRightTr());
                }
            }
        }

        return list;
    }

3.5 有序二叉树增加节点

从根节点开始遍历,若lnode<newNode<node ,则插入左子树,反之则右子树。遍历到子树的页结点,最后赋值。

public void addNode(TreeNode newNode){
        if(newNode.compareTo(this)<0){//左边
            if(this.leftTr==null){
                this.leftTr=newNode;
            }else{
                this.leftTr.addNode(newNode);
            }
        }else{
            if(this.rightTr==null){
                this.rightTr=newNode;
            }else{
                this.rightTr.addNode(newNode);
            }
        }
    }

3.6 有序二叉树删除结点

删除结点较与增加结点会更复杂一点,涉及到删除结点的子树的位置调换。
删除结点分为以下四种情况
1、根节点,特殊处理
2、删除结点为叶子节点:直接将删除结点的父节点的左 或者右结点置为null
3、删除结点只有左结点:需将删除结点的左子树,移到删除结点的父结点的右结点上
4、删除结点有右结点:需要判断右结点的子树的最小结点,并将最小结点的右子树移到删除结点的父节点的右子树

public void delete(TreeNode node){
        TreeNode x=find(node);
        TreeNode parent=findParent(x);
        if(!x.hasLeftTr()|| !x.hasRightTr()){//删除节点为叶子节点
            if(parent==null){//根节点
                this.setData(null);
            }else{
                if(parent.hasLeftTr()&& parent.getLeftTr().equals(x)){
                    parent.setLeftTr(null);//如果删除节点在父节点的左侧
                }
                if(parent.hasRightTr() && parent.getRightTr().equals(x)){
                    parent.setRightTr(null);//如果删除节点在父节点的右侧
                }
            }

        }
        if(x.hasLeftTr() &&!x.hasRightTr()){//删除的节点有左叶节点,没有右叶节点
            if(parent==null){
                this.setData(x.getLeftTr().getData());
                this.setLeftTr(x.getLeftTr().getLeftTr());
            }else{
                if(parent.hasLeftTr() && parent.getLeftTr().equals(x)){
                    parent.setLeftTr(x.getLeftTr());
                }if(parent.hasRightTr() && parent.getRightTr().equals(x)){
                    parent.setRightTr(x.getLeftTr());
                }
            }

        }
        if(x.hasRightTr()){//有右叶节点
            TreeNode minNode=x.getRightTr().finMinNode();
            parent=this.findParent(minNode);
            if(minNode.equals(x.getRightTr())){
                parent.setRightTr(minNode.getRightTr());
            }else{
                parent.setLeftTr(minNode.getRightTr());
            }
            x.setData(minNode.getData());
        }

    }

3.7 查找结点

从根结点开始遍历,才用3.1-3.4的方法均可以
也可以使用迭代遍历

 public  TreeNode find(TreeNode node){
        if(this==null){
            return null;
        }
        else if(this.compareTo(node)<0){
            return this.getRightTr().find(node);
        }else if(this.compareTo(node)>0){
            return this.getLeftTr().find(node);
        }
        return this;
    }

3.8 查找父结点

 public TreeNode findParent(TreeNode node1){

        if(this!=null && (node1.equals(this.getLeftTr()) || node1.equals(this.getRightTr()))){
            return this;
        }
        TreeNode parent=null;
        if(this.hasLeftTr()){
            parent=this.getLeftTr().findParent(node1);
            if(parent!=null){
                return parent;
            }
        }
        if(this.hasRightTr()){
            parent= this.getRightTr().findParent(node1);
            if(parent!=null){
                return parent;
            }
        }
         return parent;
    }

3.9 最小结点 or 最大结点

 public TreeNode finMinNode(){
        while (this.getLeftTr()!=null){
            return this.getLeftTr().finMinNode();
        }
        return this;
    }

    public TreeNode finMaxNode(){
        while(this.getRightTr()!=null){
            return this.getRightTr().finMaxNode();
        }
        return this;
    }

四、平衡树

平衡树是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树

4.1 Avl平衡树

参考资料:AVL树-自平衡二叉查找树(Java实现)
参考资料:手把手教,手写AVL树

4.2实现

4.2.1 左旋
 public AvlTreeNode rotateLeft(AvlTreeNode node){
        AvlTreeNode parent=node.getParent();
        AvlTreeNode anoNode=node.getRightTr();
        if(parent!=null){
            if (node.equals(parent.getLeftTr())){
                parent.setLeftTr(anoNode);
            }
            if(node.equals(parent.getRightTr())){
                parent.setRightTr(anoNode);
            }
        }
        anoNode.setLeftTr(node);
        node.setRightTr(anoNode.getRightTr());
        anoNode.setParent(node);
        anoNode.setParent(parent);
        return anoNode;
    }
4.2.2 右旋
    public AvlTreeNode rotateRight(AvlTreeNode node){
        AvlTreeNode parent=node.getParent();
        AvlTreeNode anoNode=node.getLeftTr();
        if(parent!=null){
            if (node.equals(parent.getLeftTr())){
                parent.setLeftTr(anoNode);
            }
            if(node.equals(parent.getRightTr())){
                parent.setRightTr(anoNode);
            }
        }
        anoNode.setRightTr(node);
        node.setLeftTr(anoNode.getRightTr());
        //父节点
        node.setParent(anoNode);
        anoNode.setParent(parent);

        return anoNode;

    }
4.2.3 先左旋再右旋
 public AvlTreeNode rotateLeftRight(AvlTreeNode node){
        AvlTreeNode parent=node.getParent();
        if(parent!=null){
            if (node.equals(parent.getLeftTr())){
                parent.setLeftTr(node.getLeftTr());
            }
            if(node.equals(parent.getRightTr())){
                parent.setRightTr(node.getRightTr());
            }
        }
        node.setLeftTr(rotateLeft(node.getLeftTr()));
       return rotateRight(node);
    }
4.2.4 先右旋再左旋
public AvlTreeNode rotateRightLeft(AvlTreeNode node){
        AvlTreeNode parent=node.getParent();
        if(parent!=null){
            if (node.equals(parent.getLeftTr())){
                parent.setLeftTr(node.getLeftTr());
            }
            if(node.equals(parent.getRightTr())){
                parent.setRightTr(node.getRightTr());
            }
        }
       node.setRightTr( rotateRight(node.getRightTr()));
        return rotateLeft(node);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值