二叉树深度计算

今天面试,被问到了二叉树的深度计算问题。

一听到这个问题,第一反应是大学被写烂了的基本数据结构问题。

然而我已经毕业3年,没写算法很久,手都生了。但是呢,不能说生了就只对面试官说思路吧,于是还是不假思索的立马拿起笔,写了起来,毕竟这是一个基本的数据结构题目。

首先立马写了一个二叉树结构。

public class BinaryTree{
        BinaryTree lChild, rChild;
        int value;
    }

一写出来,立马有了感觉。顺势写了计算方法

public int computeTreeDepth(BinaryTree binaryTree){
        if(binaryTree.lChild == null && binaryTree.rChild == null){
            return 1;
        }
        int lDepth = computeTreeDepth(binaryTree.lChild);
        int rDepth = computeTreeDepth(binaryTree.rChild);
        return Math.max(lDepth, rDepth);
    }

然后就给面试官看了。

面试官问我有感觉哪里不对吗?哎,只怪我太久没写算法题了,连这个基本的深度计算,都出现简单失误。更可气的是在当时的紧张环境下,我竟然没有发现哪里问题。后来经过面试官提醒,也终于改了过来。

    public int computeTreeDepth(BinaryTree binaryTree){
        if(binaryTree == null){
            return 0;
        }
        if(binaryTree.lChild == null && binaryTree.rChild == null){
            return 1;
        }
        int lDepth = computeTreeDepth(binaryTree.lChild) + 1;
        int rDepth = computeTreeDepth(binaryTree.rChild) + 1;
        return Math.max(lDepth, rDepth);
    }

此时我的心里一万个对自己无语,竟然在面试的时候出现这么大的失误(不过也只怪我平常自从毕业就很少去深入写算法题了,以后一定要注意)

这个时候,面试官说这是递归实现,写一下非递归实现。

当时心里无语的我顿时一塞,非递归实现,然后一想,也简单。然后就屁颠屁颠的开始写起来。写着写着我发现,情况似乎不是我想的这么简单。随着时间的深入,我的心里变得异常紧张,结果硬是在那几分钟没想出来。此刻真是深深的鄙视自己,无论是什么原因让我当时的大脑短路了,终究的结果是我没写出来非递归实现。之后面试官委婉的叫我下去在思考这个问题吧。

总结一下:

问题:自己当时太想着展现自己,导致自己的思路太过紧凑反而限制了我的思维,导致这个简单的问题也写出漏洞,甚至连非递归实现最后没编写出来。

对自己的思考:

1.作为程序猿,平常还是要多锻炼自己的算法编写习惯,多去思考。只有这样才可以在面试中对算法题有一个较好的把控。

2.面试的时候心态要放平稳,不能心浮气躁,导致自己出现各种临时小问题。因为对方只会看结果。

回来了,自己重新写了一遍二叉树的深度计算实现,果然很简单。记录一下吧,就当是自己前进的路上的一个绊脚石,也希望自己在每一次总结之后能进步自己。

多思考,多学习,放平心态,去面对每一个新技术,新思维。

下面贴代码

二叉树深度计算的递归实现:

    public int computeTreeDepth(BinaryTree binaryTree){
        if(binaryTree == null){
            return 0;
        }
        return Math.max(computeTreeDepth(binaryTree.lChild), computeTreeDepth(binaryTree.rChild)) + 1;
    }

二叉树深度计算的非递归实现:

首先想到是直接遍历,如果是直接遍历那自然就有两种思路,广度优先和深度优先。

先说广度优先,因为是遍历下一层意味着上一层都已遍历,先进先出,采用队列实现:

    public int computeTreeDepth(BinaryTree binaryTree){
        //实例化队列
        Queue<BinaryTree> mVisitList = new LinkedList<>();
        BinaryTree tmp;
        int depth = 0, lengthTmp;
        if(binaryTree == null){
            //根为空,直接返回0
            return 0;
        }
        //将根加入队列
        mVisitList.offer(binaryTree);
        while (!mVisitList.isEmpty()){
            //只要队列不为空,说明新的一层数据不为空,且已经加到队列,深度+1
            depth ++;
            //遍历该层到所有数据,将他们出队,并附带把所有下一层到数据都加进来(如果有的话)
            lengthTmp = mVisitList.size();
            while (lengthTmp -- > 0){
                tmp = mVisitList.poll();
                if(tmp.lChild != null){
                    mVisitList.offer(tmp.lChild);
                }
                if(tmp.rChild != null){
                    mVisitList.offer(tmp.rChild);
                }
            }
        }
        return depth;
    }

然后深度优先,因为是深度遍历那肯定是先进后出,采用栈实现,然后遍历思路是先遍历当前节点的左子树,没有左子树到情况在遍历右子树:

    public int computeTreeDepth(BinaryTree binaryTree){
        //实例化栈
        Stack<BinaryTree> mVisitList = new Stack<>();
        BinaryTree tmp = binaryTree;
        int depth = 0;//遍历过程中到最大深度
        while (tmp != null){
            if(tmp.lChild != null || tmp.rChild != null){
                //如果有子树,将当前节点入栈,且更新最大深度
                mVisitList.push(tmp);
                depth = Math.max(depth, mVisitList.size());
                //因为是左子树优先,所以深度遍历下一个子节点的时候,优先左子树
                tmp = tmp.lChild != null ? tmp.lChild : tmp.rChild;
                continue;
            }
            //当前节点没有子树,直接更新最大深度(访问到当前节点到深度是栈的深度+1)
            depth = Math.max(depth, mVisitList.size() + 1);
            //此时回溯去找右子树
            while (!mVisitList.isEmpty()){
                if(mVisitList.peek().rChild != null && mVisitList.peek().rChild != tmp){
                    //如果栈顶节点的右子树不为空,且栈顶节点的右子树不等于正在访问的节点,访问右子树
                    tmp = mVisitList.peek().rChild;
                    break;
                }
                //说明当前栈顶节点的右子树为空,直接出栈,继续回溯
                // 且要更新当前节点,用于记录当前正在回溯的节点,避免死循环回溯
                tmp = mVisitList.pop();
            }
        }
        return depth;
    }

好了,上面就是二叉树的实现,自己写了几个测试用例没出现问题。但是不能保证完全没问题,有问题的话或者可以优化的欢迎留言交流,一起学习进步。

### 完全二叉树深度计算方法 完全二叉树深度可以通过其节点总数来推导得出。假设一棵完全二叉树的高度为 \( h \),则该树具有如下特性: - 对于高度为 \( h \) 的完全二叉树,前 \( h-1 \) 层是一个满二叉树,因此这些层中的节点总数为 \( 2^{h-1} - 1 \)[^3]。 - 第 \( h \) 层可能未被填满,但所有节点均集中分布在左侧。 如果已知完全二叉树的总节点数 \( n \),可以根据以下公式计算深度 \( h \): \[ h = \lfloor \log_2(n+1) \rfloor \] 此公式的依据在于完全二叉树的性质以及对数函数的应用[^3]。具体而言,对于任意给定的节点数 \( n \),满足条件 \( 2^{h-1} \leq n < 2^h \) 的整数值 \( h \) 即为其深度。 以下是基于递归实现的一种算法用于动态计算完全二叉树深度: ```c int TreeDeep(struct node *T) { int deep = 0; if (T) { int ld = TreeDeep(T->l); int rd = TreeDeep(T->r); deep = ld >= rd ? ld + 1 : rd + 1; } return deep; } ``` 上述代码片段展示了如何利用递归解一般二叉树(包括完全二叉树)的深度[^2]。通过比较左右子树的深度并取较大者加一即可得到当前节点所在树的深度。 另外一种更高效的方法适用于完全二叉树场景下快速获取其深度。这种方法依赖于不断向左和右遍历直到叶子节点为止,并记录路径长度作为最终结果的一部分[^4]: ```javascript var countNodes = function(root) { if (root == null) { return 0; } let leftNode = root.left; let rightNode = root.right; let leftDepth = 0; let rightDepth = 0; while (leftNode != null) { leftNode = leftNode.left; leftDepth++; } while (rightNode != null) { rightNode = rightNode.right; rightDepth++; } if (leftDepth === rightDepth) { return Math.pow(2, rightDepth + 1) - 1 ; } let leftNum = countNodes(root.left); let rightNum = countNodes(root.right); return leftNum + rightNum + 1 ; }; ``` 以上JavaScript版本实现了针对完全二叉树特性的优化策略,在某些情况下能够显著减少不必要的递归调用次数从而提升性能表现[^4]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值