判断一个树是否为完全二叉树

本文介绍了一种通过广度优先遍历判断一棵树是否为完全二叉树的方法。该方法通过对节点进行遍历并标记第n层最右侧节点的状态来确定树的结构,确保了在遇到第一个缺少子节点的情况后,后续节点不再有子节点出现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

完全二叉树的定义是,前n-1层都是满的,第n层如有空缺,则是缺在右边,即第n层的最右边的节点,它的左边是满的,右边是空的。
采用广度优先遍历,从根节点开始,入队列,如果队列不为空,循环。遇到第一个没有左儿子或者右儿子的节点,设置标志位,如果之后再遇到有左/右儿子的节点,那么这不是一颗完全二叉树。

这个方法需要遍历整棵树,复杂度为O(N),N为节点的总数。

//二叉树结点定义
typedef struct Node
{
    int data;
    struct Node* left;
    struct Node* right;
}Node;

//实现广度遍历需要队列
Queue<Node*> queue;

//第n层最右节点标志
bool leftMost = false;

bool ProcessChild(Node* child)
{
    if (child)
    {
        if (!leftMost)
        {
            queue.push(child);
        }
        else
        {
            return false;
        }
    }
    else
    {
        leftMost = true;
    }

    return true;
}

bool IsCompleteBinaryTree(Node* root)
{
    //空树也是完全二叉树
    if (!root)
        return true;

    //首先根节点入队列
    queue.push(root);

    while(!queue.empty())
    {
        Node* node = queue.pop();

        //处理左节点
        if (!ProcessChild(node->left))
            return false;

        //处理右节点
        if (!ProcessChild(node->right))
            return false;
    }

    //广度优先遍历完毕,此乃完全二叉树
    return true;
}
<think>好的,我需要向用户介绍如何判断一个二叉树是否完全二叉树。首先,我得回忆完全二叉树的定义。完全二叉树是指除了最后一层外,其他层的节点都必须是满的,而且最后一层的节点都靠左排列。那怎么判断呢? 我记得常见的办法是用层序遍历(BFS),在遍历过程中检查是否存在不符合的情况。比如,如果遇到一个空节点后,后面还有非空节点,那这棵就不是完全二叉树。或者,如果某个节点只有右孩子而没有左孩子,这也违反了完全二叉树的性质。 步骤大概是这样的:首先进行层序遍历,将节点依次入队,包括空节点。然后在遍历过程中,一旦遇到空节点,就检查队列中剩余的节点是否还有非空的情况。如果有,就不是完全二叉树。另外,在遍历过程中,如果左孩子为空而右孩子存在,也应该立即返回false。 不过可能还有更优化的方法,比如不需要将空节点入队,而是在遍历时记录是否遇到了空节点。当遇到空节点后,后面再遇到任何非空节点都视为不满足条件。同时,检查每个节点的左右孩子是否存在,确保左孩子存在或者左右都不存在的情况。 比如,具体步骤可能如下: 1. 使用队列进行层序遍历2. 设置一个标志位(比如isEnd)来标记是否已经遇到了空节点。 3. 遍历每个节点时,如果已经标记isEnd为true,但当前节点不为空,则返回false。 4. 检查左孩子是否存在,如果不存在但右孩子存在,返回false。 5. 如果左孩子不存在或者右孩子不存在,则将isEnd设为true,后续节点必须都为空。 需要处理一些特殊情况,比如根节点为空的情况,这时候也是完全二叉树吗?根据定义,空可能被视为完全二叉树。 另外,举个例子可能更好理解。比如,一个的结构是否符合完全二叉树的条件。例如,完全二叉树的例子:每一层都填满,除了最后一层可能不满,但最后一层节点都靠左。而非完全二叉树的情况可能是最后一层节点中间有空缺,或者某个节点只有右孩子而没有左孩子。 可能还需要考虑不同的测试用例,比如: - 所有节点都有左右子节点,且层数相同。 - 最后一层有空缺但不在最左边。 - 某个节点左空但右存在。 - 只有左子的情况是否符合。 在代码实现上,可以用队列来辅助。初始化队列,加入根节点。设置一个标志位为false。然后循环处理队列中的每个节点。对于当前节点,如果标志位为true且当前节点不为空,则返回false。否则,如果当前节点不为空,将左右子节点加入队列。如果当前节点为空,设置标志位为true。不过这可能不够,因为有些情况需要检查子节点是否存在。 或者另一种方法是,在遍历过程中,当遇到第一个空节点时,之后的节点必须全为空。同时,检查每个节点的左右子节点是否满足完全二叉树的条件。例如,当左子节点不存在时,右子节点必须也不存在。 需要确保所有可能的情况都被覆盖到。例如,当左子节点存在而右子节点不存在时,后续的所有节点都必须是叶子节点,且之后的层数不能有节点。 可能还需要注意处理节点数为1的情况,或者只有左子的情况。 总结下来,判断完全二叉树的步骤大致是: 1. 层序遍历二叉树2. 检查是否在遇到空节点后还有非空节点。 3. 检查每个节点的左右子节点是否符合完全二叉树的结构,如不存在只有右子节点的情况。 实现时,可以用一个队列来进行层序遍历,并在遍历过程中维护一个标记,表示是否已经遇到了空节点。每次处理节点时,如果标记已设置,但当前节点存在,则返回false。同时,处理每个节点的左右子节点,如果左子节点不存在而右子节点存在,返回false。否则,将存在的子节点入队,如果任何一个子节点不存在,则标记开始检查后续是否全为空。 这样应该能覆盖所有的情况,正确判断是否完全二叉树。</think>要判断一个二叉树是否完全二叉树,可以按照以下步骤进行: --- ### **一、完全二叉树的定义** 完全二叉树需满足两个条件: 1. **结构完整性**:除最后一层外,其他层必须完全填满。 2. **最后一层左对齐**:最后一层的所有节点必须尽可能靠左排列,不能存在“中间空缺”。 --- ### **二、判断方法(层序遍历法)** 通过层序遍历(BFS)检查节点是否符合规则: #### **步骤 1:层序遍历二叉树** 使用队列逐层遍历所有节点,包括空节点(例如将空节点用 `null` 标记)。 #### **步骤 2:标记空节点** 在遍历过程中,若遇到第一个空节点,则后续所有节点必须也为空。若后续出现非空节点,则不是完全二叉树。 #### **步骤 3:检查子节点规则** 若某个节点的左子节点为空,但右子节点非空,则违反完全二叉树的定义。 --- ### **三、具体实现逻辑** 1. **初始化队列**:将根节点入队。 2. **标记是否遇到空节点**:设置标志位 `hasNull` 初始为 `false`。 3. **循环处理队列**: - 出队当前节点。 - 若当前节点为空,设置 `hasNull = true`。 - 若当前节点非空: - 若 `hasNull` 已为 `true`,说明之前有空节点,直接返回 `false`。 - 检查左子节点是否存在:若左空右非空,返回 `false`。 - 将左右子节点入队(包括空节点)。 --- ### **四、代码示例(Python)** ```python class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def isCompleteTree(root): if not root: return True queue = [root] hasNull = False while queue: node = queue.pop(0) if not node: hasNull = True else: if hasNull: return False # 检查左子节点是否存在或右子节点是否非法 if node.left is None and node.right is not None: return False queue.append(node.left) queue.append(node.right) return True ``` --- ### **五、实例验证** 1. **完全二叉树示例**: ``` 1 / \ 2 3 / \ / 4 5 6 ``` 层序遍历顺序为 `1,2,3,4,5,6`,满足条件。 2. **非完全二叉树示例**: ``` 1 / \ 2 3 \ \ 4 5 ``` 错误点:节点2的左子节点为空但右子节点非空。 --- ### **六、复杂度分析** - **时间复杂度**:$$O(n)$$,需遍历所有节点。 - **空间复杂度**:$$O(n)$$,队列最多存储一层节点。 通过上述步骤,可以高效判断二叉树是否完全二叉树
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值