赞
踩
我们今天来看二叉树的习题:
这是一个典型的回溯,深度优先算法的题,这里我们来简单的介绍一下深度优先算法和广度优先算法:
深度优先搜索(Depth-First Search, DFS)和广度优先搜索(Breadth-First Search, BFS)是两种常用的图(或树)遍历算法。虽然它们都是用于探索图中所有节点的策略,但在搜索顺序和所需数据结构上有所不同。
深度优先搜索(DFS)
深度优先搜索是一种沿着图的深度方向尽可能深地搜索的算法。它会优先访问离起点最近的未访问节点的子节点,直到到达叶子节点(没有子节点的节点)或无法继续深入为止,然后回溯到上一个节点,尝试访问其未被访问的另一个子节点,重复这一过程,直到图中所有节点都被访问过。
实现方式:
递归实现
:从某个起始节点开始,递归地访问其未访问过的子节点。栈实现
:使用栈来保存待访问节点,每次从栈顶取出一个节点,访问它,并将其未访问过的子节点压入栈中。
广度优先搜索(BFS)
广度优先搜索是一种从起点开始,按层次逐层向外遍历的算法。它会先访问与起点距离最近的所有节点(即邻居节点),然后再访问这些节点的邻居节点,以此类推,直到遍历到目标节点或遍历完整个图。
实现方式:
队列实现
:使用队列来保存待访问节点,每次从队头取出一个节点,访问它,并将其未访问过的邻居节点加入队尾。
比较
搜索顺序:
DFS
:沿着一条路径深入到底,再回溯并尝试另一条路径,类似“纵深”探索。BFS
:从起点开始,逐层向外扩散,类似于“地毯式”搜索。
所需数据结构:
DFS
:递归实现时不需要额外数据结构;栈实现时需用到栈。BFS
:需要使用队列。
空间复杂度:
DFS
:递归实现的空间复杂度取决于递归调用的深度,最坏情况下可能达到O(n);栈实现的空间复杂度为O(n),n为图中节点数。BFS
:空间复杂度为O(n),需要存储所有待访问节点。
适用场景:
DFS
:适用于寻找图中是否存在某条路径、求解最短路径问题(无权图)等,特别是在高度大于宽度的图中效果较好。BFS
:适用于求解最短路径问题(有权图需使用Dijkstra算法或A*算法等)、寻找两个节点间的最短路径、拓扑排序等问题,特别是在宽度大于高度的图中效果较好。
我们今天这道题就是一个深度优先搜索,我们拿这棵树来举例子:
我们用二维vector来存放满足条件的路径:
我们从3开始,先往左走,到9:
3 + 9 = 12故将3和9放入vector[0]中:
这个时候,往回走,回到3:
回到3,便往右走:
重复上述步骤,整理思路可得代码:
class Solution { public: // 定义一个成员函数 dfs,用于实现深度优先搜索,查找所有和为目标值的路径 void dfs(TreeNode* root, vector<int>& path, vector<vector<int>>& result, int targetSum) { // 如果当前节点为空,直接返回 if (root == nullptr) { return; } // 将当前节点的值加入路径,并从目标和中减去该值 path.push_back(root->val); targetSum -= root->val; // 如果当前节点是叶子节点(无左右子节点),且剩余目标和为0,说明找到了一条符合条件的路径 if (root->left == nullptr && root->right == nullptr && targetSum == 0) { result.push_back(path); // 将当前路径加入结果集 } // 递归遍历左子树 dfs(root->left, path, result, targetSum); // 递归遍历右子树 dfs(root->right, path, result, targetSum); // 回溯:从路径中移除当前节点的值,以便于后续节点的搜索 path.pop_back(); } // 定义一个成员函数 pathSum,用于查找二叉树中所有和为目标值的路径 vector<vector<int>> pathSum(TreeNode* root, int targetSum) { // 初始化结果容器,用于存放所有和为目标值的路径 vector<vector<int>> result; // 初始化当前路径向量 vector<int> path; // 当前路径 // 调用 dfs 函数,从根节点开始搜索 dfs(root, path, result, targetSum); // 返回找到的所有和为目标值的路径 return result; } };
二叉搜索树(Binary Search Tree, BST)是一种特殊类型的二叉树,它具有以下关键属性:
定义与性质:
有序性:对于二叉搜索树中的任意节点,其左子树中所有节点的值都严格小于该节点的值,而右子树中所有节点的值都严格大于该节点的值。这保证了树中的数据按照某种顺序排列。
递归结构:二叉搜索树的每个子节点(如果存在)本身也是一个二叉搜索树,也就是说,整个数据结构具有递归性质。
我们之前自己实现的二叉树就是二叉搜索树,如果还有小伙伴不怎么熟悉的,可以点击这里:
有一个重要的性质:二叉树的中序遍历是一个递增的数列,这个用来判断搜索二叉树是一个非常关键的方法。
https://www.nowcoder.com/practice/f31fc6d3caf24e7f8b4deb5cd9b5fa97?tpId=196&tqId=37156&ru=/exam/oj
我们可以利用二叉搜索树中序遍历的特点来判断是否为二叉搜索树,判断是否为完全二叉树,这里我们就可以借助队列,用广度优先搜索:
假设我们有一棵完全二叉树:
如果利用队列,全部放在队列中,应该是这样的顺序:
如果是非完全二叉树:
我们发现,如果是完全二叉树,那么空节点应该全都集中在末尾,如果是非完全二叉树,中间就会出现空结点,所以我们只要在遇到空结点时,检查之后是否有结点,如果没有就是完全二叉树,有的话就是非完全二叉树。
// 定义一个成员函数 judgeCompelte,用于判断给定的二叉树是否为完全二叉树 bool judgeCompelte(TreeNode* root) { // 初始化一个队列,用于按层遍历二叉树 queue<TreeNode*> queue; // 如果根节点不为空,将其入队 if (root) queue.push(root); // 使用队列进行层序遍历 while (!queue.empty()) { // 弹出队首节点 TreeNode* front = queue.front(); queue.pop(); // 如果遇到空节点,表示已经到达当前层的最后一个有效节点,跳出循环 if (front == nullptr) break; // 将当前节点的左右子节点(无论是否为空)依次入队,准备遍历下一层 queue.push(front->left); queue.push(front->right); } // 遍历结束后,队列中剩余的节点应全为空,因为它们对应于完全二叉树中不存在的节点 // 如果队列中还有非空节点,说明这不是完全二叉树,返回false while (!queue.empty()) { TreeNode* front = queue.front(); queue.pop(); if (front) { return false; } } // 队列已清空,且所有弹出的节点都符合完全二叉树的条件,返回true,表示给定二叉树是完全二叉树 return true; }
判断是否为二叉搜索树:
// 中序遍历二叉树,并将遍历结果存储在给定的向量中 void inorder(TreeNode* root, vector<int>& vc) { if (root == nullptr) { return; } inorder(root->left, vc); // 遍历左子树 vc.push_back(root->val); // 将当前节点的值添加到向量中 inorder(root->right, vc); // 遍历右子树 } // 判断给定的二叉树是否为二叉搜索树 bool isBST(TreeNode* root) { vector<int> vc; inorder(root, vc); // 对二叉树进行中序遍历 for (int i = 0; i < vc.size() - 1; i++) { if (vc[i] >= vc[i + 1]) { // 如果当前元素大于或等于下一个元素,则不是二叉搜索树 return false; } } return true; // 如果所有元素都小于下一个元素,则是二叉搜索树 }
那么这道题就可以迎刃而解了:
/** * struct TreeNode { * int val; * struct TreeNode *left; * struct TreeNode *right; * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * }; */ class Solution { public: //判断完全是否为完全二叉树 bool judgeCompelte(TreeNode* root) { //初始化队列 queue<TreeNode*> queue; //入队 if(root) queue.push(root); while(!queue.empty()) { TreeNode* front = queue.front(); queue.pop(); if(front == nullptr) break; queue.push(front->left); queue.push(front->right); } while(!queue.empty()) { TreeNode* front = queue.front(); queue.pop(); if (front) { return false; } } return true; } //中序遍历 void inoder(TreeNode* root,vector<int>& vc) { if(root == nullptr) { return; } inoder(root->left,vc); vc.push_back(root->val); inoder(root->right,vc); } //判断 vector<bool> judgeIt(TreeNode* root) { vector<bool> result(2,false); if(!root) { result[0] = true; result[1] = true; return result; } //判断是否为二叉搜索树 vector<int> vc; inoder(root,vc); result[0] = true; for(int i = 0; i <vc.size() - 1; i++) { if(vc[i] >= vc[i+1]) { result[0] = false; break; } } //判断是否为完全二叉树 result[1] = judgeCompelte(root); return result; } };
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。