2026/5/21 3:05:13
网站建设
项目流程
成都建设局网站首页,东莞房产网站建设,商丘市网站建设推广,设计在线设计网站在二叉树的算法体系中#xff0c;读取#xff08;遍历#xff09;与写入#xff08;构建#xff09;是两个最核心的命题。本文将通过两道经典题目——二叉树的层序遍历与有序数组转搜索树#xff0c;深入剖析两种截然不同的思维模式#xff1a;…在二叉树的算法体系中读取遍历与写入构建是两个最核心的命题。本文将通过两道经典题目——二叉树的层序遍历与有序数组转搜索树深入剖析两种截然不同的思维模式基于队列的迭代BFS与基于递归的分治Divide Conquer并从内存与指针的角度分析其底层实现。一、 读取的艺术二叉树的层序遍历二叉树的层序遍历Level Order Traversal本质上是图论中的广度优先搜索BFS。不同于前中后序遍历“一条道走到黑”的深度优先逻辑层序遍历要求我们按照“剥洋葱”的方式一层一层地访问节点。1. 核心代码实现C代码实现class Solution { // 思路 先加入root进q然后每次处理完front之后把它左右子树加入进去最重要的其实还是要记录当前的size来记录有几个数进入vals作为一组 public: vectorvectorint levelOrder(TreeNode* root) { vectorvectorint ans; if (root nullptr) return ans; queueTreeNode* q; q.push(root); while (!q.empty()) { vectorint vals; // 快照核心逻辑固定住当前层的 size for (int i q.size(); i 0; --i) { vals.push_back(q.front()-val); if (q.front()-left) q.push(q.front()-left); if (q.front()-right) q.push(q.front()-right); q.pop(); // 要不就提前用node记录下来front要不就最后pop } ans.push_back(vals); } return ans; } };2. 深度解析队列与快照机制很多初学者容易写成普通的 BFS却无法将结果按层分开。上述代码中最精髓的一行在于for (int i q.size(); i 0; --i)这里隐藏了一个**“快照”**思想锁定状态在进入for循环之前q.size()代表了当前这一层所有的节点数量。我们必须在循环开始前就确定循环次数。动态入队在循环内部我们不断地push下一层的节点左右孩子。时空隔离如果不固定i的次数而是每次判断q.size()那么新加入的下一层节点会和当前层混在一起导致分层逻辑失效。通过这个机制队列q充当了一个缓冲区完美地衔接了上一层的消耗和下一层的生产。3. 时空复杂度分析时间复杂度O(N)每个节点进队一次、出队一次操作次数与节点总数 N 成正比。空间复杂度O(W)W 为二叉树的最大宽度。在最坏情况下满二叉树的最底层队列中需要同时存储大约 N/2 个节点因此空间复杂度与宽度相关数量级上视为 O(N)。二、 构建的艺术有序数组转二叉搜索树如果说层序遍历是利用队列进行“横向扫描”那么构建平衡二叉搜索树BST则是利用递归进行“纵向切分”。题目要求构建高度平衡的 BST且输入数组是有序的。这天然符合二分法和分治思想。1. 核心代码实现C代码实现class Solution { // 思路 题目要求平衡也就是左右高度差为1以内 那肯定要涉及到中间点 利用二分的思想构造子树 dfs递归构造 因为是左小右大 所以构造左边从mid左边选右边从mid右边选 TreeNode* dfs(vectorint nums, int left, int right) { if (left right) return nullptr; int mid left (right - left) / 2; TreeNode* root new TreeNode(nums[mid]); root-left dfs(nums, left, mid - 1); root-right dfs(nums, mid 1, right); return root; } public: TreeNode* sortedArrayToBST(vectorint nums) { return dfs(nums, 0, nums.size() - 1); } };2. 深度解析堆内存与指针构建在这段简短的递归代码中TreeNode* root new TreeNode(nums[mid]);这行代码极其关键它揭示了 C 中二叉树构建的内存模型堆区Heap分配使用了new关键字。这意味着节点对象是创建在堆内存中的。这一点至关重要。如果是栈上分配例如TreeNode node;函数dfs一结束节点就会被销毁。而堆上分配的节点生命周期独立于函数调用保证了整棵树在递归结束后依然存在。栈区Stack链接dfs函数的每一次调用都在系统栈中开辟了一个栈帧。变量root是一个指针暂存在栈上。root-left dfs(...)这一步实际上是利用栈上的递归回溯将分散在堆内存中的各个节点通过指针像“锁链”一样串联起来形成了树的结构。这种**“取中点 - 建根 - 递归构建左右”的顺序本质上是二叉树的前序遍历**逻辑。3. 时空复杂度分析时间复杂度O(N)每个数组元素只会被访问一次用来创建一个节点不存在重复计算。空间复杂度O(log N)这里的空间消耗主要来自递归调用栈。由于我们每次都取中间点mid保证了生成的树是高度平衡的。平衡二叉树的高度是log N因此递归的最大深度也就是log N。注这里未计算存储结果所需的 O(N) 空间仅计算算法辅助空间。三、 总结这两道题目展示了二叉树算法的两种极端层序遍历数据结构队列 (Queue)特性FIFO (先进先出)思维迭代水平扩展通过 size控制层级。平衡构建数据结构系统栈 (System Stack / Recursion)特性LIFO (后进先出)思维递归深度优先通过二分保证平衡。