二叉树

二叉树

今天我们来说一下二叉树,先提一点,hash里面的红黑树也是从二叉树演变而来的,这个我们以后再说,这就涉及到动态查找。

1.二叉树的几种形式

1.二叉树

2.满二叉树

一棵二叉树的结点要么是叶子结点,要么它有两个子结点(如果一个二叉树的层数为K,且结点总数是(2^k) -1,则它就是满二叉树。)
在这里插入图片描述

3.完全二叉树

若设二叉树的深度为k,除第 k 层外,其它各层 (1~k-1) 的结点数都达到最大个数,第k 层所有的结点都连续集中在最左边,这就是完全二叉树。就是结点树为n的完全二叉树的结构要和满二叉树前你个结点的结构要一样。
在这里插入图片描述

4.哈夫曼树

树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

结合哈夫曼编码去理解:https://www.cnblogs.com/-citywall123/p/11297523.html

5.平衡二叉树

它或者是一颗空树,或它的左子树和右子树的深度之差(平衡因子)的绝对值不超过1,且它的左子树和右子树都是一颗平衡二叉树。

在这里插入图片描述

2.二叉树的性质

  1. 二叉树结点的层次:规定根节点的层次为0,则其他结点的层次再从双亲父母结点层次+1;

  2. 二叉树的深度: 树中所有结点的层次树的最大值+1;

  3. 结点的度:该结点拥有子结点的个数;

  4. 二叉树中第i层(i>=0)层上的结点数最多为2的i次方;

  5. 深度为h(h>=1)的二叉树最多有2的h次方减1。

3.二叉树的遍历

经过二叉树的遍历,可以得到而二叉树结点的不同线性序列,将非线性结构的二叉树变为线性序列,从而可以确定二叉树中某个结点再某种遍历序列的前驱和后继。

1.先根遍历(DLR)

  1. 递归法

    public void preRootTraverse(TreeNode root){
        if(root!=null){
            System.out.println(root.data);
            preRootTraverse(root.left);
            preRootTraverse(root.right);
        }
    }
    
  2. 非递归法

    将递归法转换为为非递归法一般有两种,一种是直接转换法,不需要回溯,递归结果通过循环结构来代替,通过一些变量来保存中间结果,比如说双指针、斐波那契。还有一种就是间接转换法,需要回溯,可以利用队列、栈来保存中间结果。

    递归法虽然写起来很方便,结构简洁,不过在时间和空间的开销比较大,所以我们借助栈来报存中间结果。

    这里我们使用栈来保存右子树。过程如下:

    (1)创建一个栈对象,让根结点入栈。

    (2)当栈不为空的时候,将栈顶结点抛出栈并访问该结点。

    (3)对当前访问结点的非空左孩子结点相继依次的访问,并将当前访问该结点的非空右孩子结点压入栈中。

    (4)重复(2)、(3),直到栈为空。

    public void preRootTraverse(TreeNode root){ 
        if(root!=null){
         
            LinkStack stack  = new LinkStack;//构造栈
            stack.push(root);//根结点入栈
            while(!stack.isEmpty()){//判断栈是否为空
                TreeNode node = stack.pop();//抛出栈顶元素,并返回其值
                System.out.print(node.data);//访问上一步返回的结点
                while(node!=null){
                    if(node.left!=null){//访问左孩子
                        System.out.print(node.left.data);//访问结点
                    }
                    if(noded.right!=null){//访问右孩子,如果非空就入栈,
                        stack.push(node.right);
                    }
                    node = node.right;//依次访问下一个左结点
                }
            }
        }
    
    }
    

2.中根遍历(LDR)

  1. 递归法

    public void inRootTraverse(TreeNode root){
        if(root!=null){
            preRootTraverse(root.left);
            System.out.println(root.data);
            preRootTraverse(root.right);
       }
    }
    
  2. 非递归

    (1)创建一个栈对象,将根结点入栈。

    (2)若栈非空,将栈顶元素的非空左孩子结点相继入栈。

    (3)栈顶结点出栈,并访问非空栈顶接地那的非空右孩子结点入栈。

    (4)重复(2)、(3)直到栈为空。

    public void inRootTraverse(TreeNode root){
        if(root!=null){
            LinkStack stack  = new LinkStack;//构造栈
            stack.push(root);//根结点入栈
            while(!stack.isEmpty()){//判断栈是否为空
                while(stack.peek()!=null){//将栈顶结点的左孩子结点相继入栈
                    stack.push(((TreeNode)stack.peek()).left);
                }
                stack.pop();//空结点退栈
                if(!stack.isEmpty()){
                    TreeNode node = stack.pop();//移除栈顶结点,并返回其值
                    System.out.print(node.data);//访问结点
                    stack.push(node.right);//结点的右孩子入栈
                }          
        }
    
    }
    

3.后根遍历(RDL)

  1. 递归法

    public void postRootTraverse(TreeNode root){
        if(root!=null){
            preRootTraverse(root.left);
            preRootTraverse(root.right);
            System.out.println(root.data);
        }
    }
    
  2. 非递归

     /** 非递归使用单栈实现二叉树后序遍历 */
        public static void iterativePostOrder(BinaryTreeNode root) {
            Stack<BinaryTreeNode> stack = new Stack<>();
            BinaryTreeNode node = root;
            // 访问根节点时判断其右子树是够被访问过
            BinaryTreeNode preNode = null;
            while (node != null || stack.size() > 0) {
                // 把当前节点的左侧节点全部入栈
                while (node != null) {
                    stack.push(node);
                    node = node.left;
                }
                if (stack.size() > 0) {
                    BinaryTreeNode temp = stack.peek().right;
                    // 一个根节点被访问的前提是:无右子树或右子树已被访问过
                    if (temp == null || temp == preNode) {
                        node = stack.pop();
                        visit(node);
                        preNode = node;// 记录刚被访问过的节点
                        node = null;
                    } else {
                        // 处理右子树
                        node = temp;
                    }
                }
            }
        }
    

4.层次遍历

  1. 广度优先遍历(BFS)

在这里插入图片描述

(1)创建一个队列对象,根结点入栈;

(2)若队列为空,将队列首结点出堆,并访问该结点,再将该节点的非空左右孩子结点入队列。

(3)重复(2),直到队列为空。

public void levelTraverse(TreeNode root){
    if(root!=null){
        LinkQueue q  = new LinkQueue;//构造队列
        q.offer(root);//根结点入队列
        while(!q.isEmpty()){//判断队列是否为空
            TreeNode node = q.poll();//抛出队列首元素,并返回其值
            System.out.print(node.data);//访问上一步返回的结点
            while(node!=null){
                if(node.left!=null){//访问左孩子,如果非空就入队列,
                     q.offer(node.left);
                }
                if(noded.right!=null){//访问右孩子,如果非空就入队列,
                    q.offer(node.right);
                }
            }
        }
    }

}
  1. 深度优先遍历(DFS)

    就是前后中遍历

    
    

4.算法应用

1.二叉树上的查找算法

(1)若二叉树为空,则不存在这个节点,返回空值,否则,将根结点的值与x进行比较,若相等,返回该结点。

(2)若不相等,则在该结点的左子树中进行查找,若找到返回找到的结点。

(3)若在左子树中没有找到值为x的结点,就继续在右子树中进行查找,并返回查找的结点。

public TreeNode searchNode(TreeNode T,Object x){//运用的先根节点的递归查找
    if(T!=null){
        if(T.data.equals(x)){
            return T;
            else{
                TreeNode Lresult = searchNode(T.left,x);
                return lresult!=nuLL? lresult:searchNode(T.right,x);
            }
        }
    }
    return null;
}

2.二叉树中结点的个数

  1. 先根遍历的方法

    public int countNode(TreeNode T){
        int count = 0;
        if(T!=null){
            count++;
            count+=countNode(T.left);
            count+=countNode(T.right);
        }
        return count;
    }
    
  2. 层次遍历的方法

    public int countNode(TreeNode root){
        int count = 0;
        if(root!=null){
            LinkQueue q  = new LinkQueue;//构造队列
            q.offer(root);//根结点入队列
            while(!q.isEmpty()){//判断队列是否为空
                TreeNode node = q.poll();//抛出队列首元素,并返回其值
                count++;
                while(node!=null){
                    if(node.left!=null){//访问左孩子,如果非空就入队列,
                         q.offer(node.left);
                    }
                    if(noded.right!=null){//访问右孩子,如果非空就入队列,
                        q.offer(node.right);
                    }
                }
            }
        }
    
    }
    
  3. 递归法

    publiv int countNode(TreeNode T){
        if(T==null){
            return 0;
        }
        else{
            return countNode(T.left)+countNode(T.right)+1;
        }
    }
    

3.二叉树的深度

  1. 递归法

    publiv int getDepth(TreeNode T){
        if(T==null){
            return 0;
        }
        else{
            int lDepth = getDepth(T.left);
            int rDepth = getDepth(T.right);
            return Math.max(lDepth,rDepth)+1;
        }
    }
    
  2. 层次遍历(bfs)

    class Solution {
        public int maxDepth(TreeNode root) {
            if (root == null) return 0;
            int depth = 0;
            Queue<TreeNode> q = new LinkedList<>();
            q.add(root);
            while (!q.isEmpty()) {
                int size = q.size();
                for (int i = 0; i < size; i++) {
                    TreeNode tmp = q.poll();
                    if (tmp.left != null) {
                        q.add(tmp.left);
                    }
                    if (tmp.right != null) {
                        q.add(tmp.right);
                    }
                }
                depth++;
            }
            return depth;
        }
    }
    

4.判断二叉树是否相等

  1. 递归法

    public boolean isEqual(TreeNode T1,TreeNode T2){
        if(T1=null&&T2==null){
            return true;
        }else if(T1!=null&&T2!==null){
            return(T1.data.equals(T2.data)&&(isEqual(T1.left,T2.left))&&(isEqual(T1.right,T2.right2));
        }else
            return false;
    }
    

5.二叉树的建立

  1. 由先根遍历序列和中根遍历序列建立一颗二叉树

    (1)先取先根遍历的第一个结点作为根结点;

    (2)在中根遍历序列汇总寻找根结点,确定根结点在中根遍历序列中的位置,假设为i(0<=i<+=count-1),其中,count就是二叉树遍历序列中总结点的个数。

    (3)在中根遍历序列中确定:根结点前的i个结点构成坐姿输的中根遍历序列,根结点后的count-1-i个结点构成右子树的中根遍历序列;

    (4)在先根遍历序列中确定:根结点后i个结点序列构成左子树的先根遍历序列,剩下的count-i-1个结点序列构成右子树的先根遍历序列;

    (5)由(3)、(4)又确定了左右子树的先根和中根遍历序列,接下来可以用同上述的方法来确定左、右子树的根结点,以此递归就可建立唯一的一课二叉树。

//声明参数:preOrder是整棵树的先根遍历序列,inOrder是整棵树的中根遍历序列,preIndex是先根遍历序列在preOrder中开始的位置,inIndex是中根遍历序列在inIndex中开始的位置,count便是树中结点的总个数。
public BiTree(String preOrder,String inOrder,int preIndex,int inIndex,int count){
    if(count>0){
        char r = preOrder.charAt(preIndex);
        int i=0;
        for(;i<count;i++){
            if(r=inOrder.charAt(i+inIndex))
                break;
        }
        root = new TreeNode(r);
        root.left = new BiTree(preOrder,inOrder,preIndex+1,inIndex,i).root;
        root.right = new BiTree(preOrder,inOrder,preIndex+1+i,inIndex+1+i,count-1-i).root;
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值