二叉树树的层次遍历实现
二叉树的所有的操作基本上都是跟遍历相关的,二叉树的深度遍历(先序、中序、后序)都设计栈的操作,但是二叉树的广度搜索(层次遍历)用到的就是队列的操作。
注意一点,二叉树的层次的遍历要得到的结果是把所有的信息放到一个一维的数组中,还是放到一个二维的数组中。
遍历结果存储到一维数组中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | vector< int > PrintFromTopToBottom(TreeNode *root) { vector< int > result; if (root == NULL) return result; queue< const TreeNode*> q; q.push(root); const TreeNode *ptop = NULL; while (!q.empty()) { ptop = q.front(); q.pop(); result.push_back(ptop->val); if (ptop->left) q.push(ptop->left); if (ptop->right) q.push(ptop->right); } //while return result; } |
上面的代码实现是把遍历的结果放到一维数组中,所以仅仅用到了一个队列。
遍历结果存放到二维数组中
如果要把最后遍历的结果保存到二维数组中,树的每一层的结点保存在一起,那么要两个队列分别遍历不同的层次:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | vector<vector< int >> levelOrder(TreeNode* root) { vector<vector< int > > result; if (root == NULL) { return result; } queue< const TreeNode*> current, next; const TreeNode *ptmp = NULL; vector< int > level; current.push(root); while (!current.empty()) { while (!current.empty()) { ptmp = current.front(); current.pop(); level.push_back(ptmp->val); if (ptmp->left) { next.push(ptmp->left); } if (ptmp->right) { next.push(ptmp->right); } } //while result.push_back(level); level.clear(); swap(current, next); } //while } |
上面的方法是利用两个队列实现的层次遍历,其实用一个队列也是可以的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | vector<vector< int >> levelOrder(TreeNode *root) { vector<vector< int > > result; vector< int > line; if (NULL == root) return result; queue< const TreeNode*> q; q.push(root); while (!q.empty()) { int size = q.size(); for ( int i = 0; i < size; i++) { const TreeNode *top = q.front(); q.pop(); line.push_back(top->val); if (top->left) { q.push(top->left); } if (top->right) { q.push(top->right); } } //for result.push_back(line); line.clear(); } //while return result; } |
通过获取queue的size可以知道这层的结点的个数。然后把这层遍历完,下一层的全部加入到队列的后面。
对于每层倒序的那种问题,最后把result.push_back(line)换成result.insert(result.begin(), line)就好了。
层次遍历的锯齿形遍历
先看一下遍历的样子:
具体的遍历方法是用两个栈,每个栈遍历一层,然后交替遍历。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | vector<vector< int >> zigzagLevelOrder(TreeNode *root) { vector<vector< int > > res; vector< int > line; if (NULL == root) { return res; } stack< const TreeNode*> s1; stack< const TreeNode*> s2; s1.push(root); while (!s1.empty() || !s2.empty()) { while (!s1.empty()) { const TreeNode *top = s1.top(); s1.pop(); line.push_back(top->val); if (top->left) s2.push(top->left); if (top->right) s2.push(top->right); } //while if (!line.empty()) res.push_back(line); line.clear(); while (!s2.empty()) { const TreeNode *top = s2.top(); s2.pop(); line.push_back(top->val); if (top->right) s1.push(top->right); if (top->left) s1.push(top->left); } //while if (!line.empty()) res.push_back(line); line.clear(); } //while return res; } |
二叉树层次遍历的应用
对于二叉树层次的遍历可能不会像二叉树深度遍历的应用那么广泛,但是看下面的应用:
判断一棵二叉树是否为对称的,先看一下递归的形式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | //判断对称二叉树的递归形式 bool isSymmetric(TreeNode* root) { if (root == NULL) { return true ; } return SubTreeSym(root->left, root->right); } bool SubTreeSym(TreeNode *left, TreeNode *right) { if (left == NULL && right == NULL) { return true ; } if ((left == NULL && right != NULL) || (left != NULL && right == NULL) ||(left->val != right->val)) { return false ; } return SubTreeSym(left->left, right->right) && SubTreeSym(left->right, right->left); } |
其实迭代的形式很简单,就是从树的根开始,把一棵树分成一棵大的左子树和一棵大的右子树,然后分别层次遍历。到每队结点的时候都要比较他们的NULL的性质和val值的大小。由于是树和自己的镜像比较,所以注意左右孩子入队列的先后。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | //判断对称二叉树的迭代形式 bool isSymmetric(TreeNode* root) { queue< const TreeNode*> q1; queue< const TreeNode*> q2; if (root == NULL) { return true ; } q1.push(root->left); q2.push(root->right); while (!q1.empty() && !q2.empty()) { const TreeNode *tmp1 = q1.front(); const TreeNode *tmp2 = q2.front(); q1.pop(); q2.pop(); if ((tmp1 == NULL && tmp2 != NULL) || (tmp1 != NULL && tmp2 == NULL)) { return false ; } if (tmp1 != NULL) { if (tmp1->val != tmp2->val) //确保两个指针都不是NULL,然后才可以比较他们的值的大小 return false ; q1.push(tmp1->left); q1.push(tmp1->right); q2.push(tmp2->right); q2.push(tmp2->left); } } //while return true ; } |
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· 对象命名为何需要避免'-er'和'-or'后缀
· SQL Server如何跟踪自动统计信息更新?
· AI与.NET技术实操系列:使用Catalyst进行自然语言处理
· dotnet 源代码生成器分析器入门
· 官方的 MCP C# SDK:csharp-sdk
· 一款 .NET 开源、功能强大的远程连接管理工具,支持 RDP、VNC、SSH 等多种主流协议!
· 一文搞懂MCP协议与Function Call的区别
· 一次Java后端服务间歇性响应慢的问题排查记录