二叉树

树是数据结构中最为重要的结构之一,尤其是二叉树。它与顺序表和单链表不同,它的结构更为复杂,运算更为抽象。

1. 树

1.1 概念

树是一种非线性的数据结构,它是由 n (n >=0) 个有限节点组成的一个具有层次关系的集合。它之所以称为树,是因为它的结构像是一颗倒挂的树。
在这里插入图片描述

  1. 节点的度: 一个节点的孩子节点的个数。如上图:A的度为3
  2. 树的度:一棵树中,最大节点的度成为数的度。如上图:该树的度为3
  3. 节点的层次:从根开始定义,根为第1层,根的子节点为第2层,以此类推
  4. 树的高度/深度:树中节点的最大层次。如上图:树的高度为3
  5. 兄弟节点:拥有相同的双亲结点。上图中,E 和 F 就是兄弟节点
  6. 森林:由两棵或者两棵以上的互不相交的树组成的集合,就称为森林。森林

1.2 树的表示形式

  1. 孩子兄弟表示法:
class Node {
    int val; // 树中存储的数据
    Node firstChild; // 第一个孩子引用
    Node nextBrother; // 第一个兄弟引用
}

2. 孩子表示法:

class Node {
    int val; // 树中存储的数据
    Node left; // 左孩子引用, 常常代表左孩子为根的整棵子树
    Node right; // 右孩子引用, 常常代表右孩子为根的整棵子树
}
  1. 孩子双亲表示法:
class Node {
    int val; // 树中存储的数据
    Node left; // 左孩子引用, 常常代表左孩子为根的整棵子树
    Node right; // 右孩子引用, 常常代表右孩子为根的整棵子树
    Node parent; // 当前节点的双亲节点
}

这里较为常用的是孩子表示法.

2. 二叉树

2.1 概念

二叉树是树中最为重要的概念.

一. 二叉树的特点:

  1. 每个节点最多有两棵子树. 即二叉树所有节点的度都 <= 2
  2. 二叉树有左右子树之分, 其次序不可以颠倒(因为二叉树是一棵有序树)

二: 二叉树的几种形态在这里插入图片描述
空树 和 只有根节点的树都是二叉树

2. 2 满二叉树和完全二叉树

2.2.1 满二叉树

一个二叉树, 如果每一层的节点数都达到最大值, 那么这棵二叉树就称为满二叉树
在这里插入图片描述
满二叉树的特点:如果一棵树有k层, 那么第 k 层就有 2 ^ (k - 1) 个节点。 总共有 (2 ^ k) -1 个节点。

2.2.2 完全二叉树

完全二叉树是由满二叉树所引出的。
但是需要注意的是: 当且仅当其每一个节点都与深度为k的满二叉树中编号从 1 - n 的节点一 一对应时, 才可以称之为完全二叉树。
在这里插入图片描述
完全二叉树的特点: 若对节点进行编号, 每一个节点的编号都是连续的。

2. 3 二叉树的性质

  1. 若规定根节点的层数为1, 那么一棵非空二叉树第 i 层上最多有 2 ^ (i - 1) (i > 0)个节点。
  2. 若一棵二叉树的深度为 k , 那么这棵二叉树最多有: (2 ^ k) - 1 (k > 0) 个节点.
  3. 对于任意一棵二叉树, 设它的叶子节点的个数为 n0, 那么度为 2 的节点的个数为: n0 = n2 + 1
  4. 具有n个节点的完全二叉树的深度 k 为: log2 (n + 1) 向上取整
  5. 若已知孩子节点的编号 i, 则其父亲节点的编号为 (i - 1) / 2
  6. 若已知父亲节点的编号, 则:
    左孩子的编号为: 2 * i + 1;
    右孩子的编号为: 2 * i + 2;

2.4 二叉树的基本操作

2.4.1 二叉树的遍历

二叉树的遍历方式有 4 种, 分别是:

  1. 前序遍历(先根遍历): 根 -> 左 -> 右
public void preOrderTraversal(Node root) {
        if(root == null) {
            return;
        }
        // 打印根
        System.out.print(root.val + " ");
        // 遍历左子树
        preOrderTraversal(root.left);
        // 遍历右子树
        preOrderTraversal(root.right);
 }
  1. 中序遍历: 左 -> 根 -> 右
 public void  inOrderTraversal(Node root) {
        if(root == null) {
            return;
        }
        // 遍历左子树
        inOrderTraversal(root.left);
        // 打印根
        System.out.print(root.val + " ");
        // 遍历右子树
        inOrderTraversal(root.right);
 }
  1. 后序遍历: 左 -> 右 -> 根
public void postOrderTraversal(Node root) {
        if(root == null) {
            return;
        }
        // 遍历左子树
        postOrderTraversal(root.left);
        // 遍历右子树
        postOrderTraversal(root.right);
        // 打印根
        System.out.print(root.val + " ");
}
  1. 层序遍历(需要借助堆来实现): 一层一层的进行遍历

关于二叉树的问题, 大多数情况下, 我们会使用递归进行求解. 通常情况下, 会将整棵二叉树化成子问题进行求解.

2.4.2 二叉树的层序遍历

二叉树的层序遍历需要借助队列进行.

// 二叉树的层序遍历    需要借助一个队完成
    // 出队元素若不为空, 判断它是否有左右孩子, 若存在, 就将孩子节点进队, 若不存在, 就不进队
    public void levelOrderTraversal(Node root) {
        Queue<Node> queue = new LinkedList<>();
        if(root == null) {
            return;
        }
        // root 不为空
        queue.offer(root);
        while (!queue.isEmpty()) {
            // 用 cur 接收出队元素
            Node cur = queue.poll();
            if(cur != null) {
                System.out.print(cur.val + " ");
                // 如果 cur 有左孩子就让左孩子进队
                if(cur.left != null) {
                    queue.offer(cur.left);
                }
                // 如果 cur 有右孩子就让右孩子进队
                if(cur.right != null) {
                    queue.offer(cur.right);
                }
            }
        }
        System.out.println();
 }

2.4.3 判断一棵树是否是完全二叉树

// 判断一棵树是不是完全二叉树
 public boolean isCompleteTree(Node root) {
     Queue<Node> queue = new LinkedList<>();
     if(root == null) {
         // 空树也是一棵完全二叉树
         return true;
     }
     queue.offer(root);
     while(!queue.isEmpty()) {
         Node cur = queue.poll();
         if(cur != null) {
             // 只要cur != null, 就将其左孩子, 右孩子都进队
             queue.offer(cur.left);
             queue.offer(cur.right);
         } else {
             // 如果cur == null, 就立即停止入队
             break;
         }
     }
     // 如果是因为cur == null
     while (!queue.isEmpty()) {
         Node cur1 = queue.peek();
         if(cur1 != null) {
             // 队中存在不为 null 的元素
             return false;
         }
         queue.poll();
     }
     return true;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值