遍历

N叉树的遍历

树的遍历

一棵二叉树可以按照前序、中序、后序或者层序来进行遍历。在这些遍历方法中,前序遍历、后序遍历和层序遍历同样可以运用到N叉树中。

回顾 - 二叉树的遍历

  1. 前序遍历 - 首先访问根节点,然后遍历左子树,最后遍历右子树;
  2. 中序遍历 - 首先遍历左子树,然后访问根节点,最后遍历右子树;
  3. 后序遍历 - 首先遍历左子树,然后遍历右子树,最后访问根节点;
  4. 层序遍历 - 按照从左到右的顺序,逐层遍历各个节点。

请注意,N叉树的中序遍历没有标准定义,中序遍历只有在二叉树中有明确的定义。尽管我们可以通过几种不同的方法来定义N叉树的中序遍历,但是这些描述都不是特别贴切,并且在实践中也不常用到,所以我们暂且跳过N叉树中序遍历的部分。

把上述关于二叉树遍历转换为N叉树遍历,我们只需把如下表述:

遍历左子树... 遍历右子树...

变为:

对于每个子节点:

通过递归地调用遍历函数来遍历以该子节点为根的子树

我们假设for循环将会按照各个节点在数据结构中的顺序进行遍历:通常按照从左到右的顺序,如下所示。

N叉树遍历示例

我们用如图所示的三叉树来举例说明:

1.前序遍历

在N叉树中,前序遍历指先访问根节点,然后逐个遍历以其子节点为根的子树。

例如,上述三叉树的前序遍历是: A->B->C->E->F->D->G.

2.后序遍历

在N叉树中,后序遍历指前先逐个遍历以根节点的子节点为根的子树,最后访问根节点。

例如,上述三叉树的后序遍历是: B->E->F->C->G->D->A.

3.层序遍历

N叉树的层序遍历与二叉树的一致。通常,当我们在树中进行广度优先搜索时,我们将按层序的顺序进行遍历。

例如,上述三叉树的层序遍历是: A->B->C->D->E->F->G.

练习

接下来,我们将为你提供几道与N叉树相关的习题。

N-ary Tree Preorder Traversal

给定一个 N 叉树,返回其节点值的前序遍历

例如,给定一个 3叉树 :

返回其前序遍历: [1,3,5,6,2,4]

说明: 递归法很简单,你可以使用迭代法完成此题吗?

  1. #include <iostream>
  2. #include <vector>
  3. #include <stack>
  4. using namespace std;
  5. class Node{
  6. public:
  7. int val;
  8. vector<Node*> children;
  9. Node(){}
  10. Node(int _val, vector<Node*>_children){
  11. val = _val;
  12. children = _children;
  13. }
  14. };
  15. /// Recursion
  16. /// Time Complexity: O(n)
  17. /// Space Complexity: O(h)
  18. class SolutionA{
  19. public:
  20. vector<int> preorder(Node* root){
  21. vector<int> res;
  22. dfs(root, res);
  23. return res;
  24. }
  25. private:
  26. void dfs(Node* node, vector<int>& res){
  27. if(!node)
  28. return;
  29. res.push_back(node->val);
  30. for(Node* next: node->children)
  31. dfs(next, res);
  32. }
  33. };
  34. /// Non-Recursion
  35. /// Using stack
  36. /// Time Complexity: O(n)
  37. /// Space Complexity: O(h)
  38. class SolutionB{
  39. public:
  40. vector<int> preorder(Node* root){
  41. vector<int> res;
  42. if(!root)
  43. return res;
  44. stack<Node*> stack;
  45. stack.push(root);
  46. while(!stack.empty()){
  47. Node* cur = stack.top();
  48. stack.pop();
  49. res.push_back(cur->val);
  50. for(vector<Node*>::reverse_iterator iter = cur->children.rbegin();
  51. iter != cur->children.rend(); iter++)
  52. stack.push(*iter);
  53. }
  54. return res;
  55. }
  56. };
  57. int main(){
  58. return 0;
  59. }

N-ary Tree Postorder Traversal

给定一个 N 叉树,返回其节点值的后序遍历

例如,给定一个 3叉树 :

返回其后序遍历: [5,6,3,2,4,1].

说明: 递归法很简单,你可以使用迭代法完成此题吗?

  1. #include <iostream>
  2. #include <vector>
  3. #include <stack>
  4. using namespace std;
  5. class Node{
  6. public:
  7. int val;
  8. vector<Node*> children;
  9. Node(){}
  10. Node(int _val, vector<Node*> _children){
  11. val = _val;
  12. children = _children;
  13. }
  14. };
  15. /// Recursion
  16. /// Time Complexity: O(n)
  17. /// Space Complexity: O(h)
  18. class SolutionA{
  19. public:
  20. vector<int> postorder(Node* root){
  21. vector<int> res;
  22. dfs(root, res);
  23. return res;
  24. }
  25. private:
  26. void dfs(Node* node, vector<int>& res){
  27. if(!node)
  28. return;
  29. for(Node* next: node->children)
  30. dfs(next, res);
  31. res.push_back(node->val);
  32. }
  33. };
  34. /// Non-Recursion
  35. /// Using stack
  36. ///
  37. /// Time Complexity: O(n)
  38. /// Space Complexity: O(h)
  39. class SolutionB{
  40. public:
  41. vector<int> postorder(Node* root){
  42. vector<int> res;
  43. if(!root)
  44. return res;
  45. stack<Node*> stack;
  46. stack.push(root);
  47. while(!stack.empty()){
  48. Node* cur = stack.top();
  49. stack.pop();
  50. res.push_back(cur->val);
  51. for(Node* next: cur->children)
  52. stack.push(next);
  53. }
  54. reverse(res.begin(), res.end());
  55. return res;
  56. }
  57. };
  58. int main(){
  59. return 0;
  60. }

N叉树的层序遍历

给定一个 N 叉树,返回其节点值的层序遍历。 (即从左到右,逐层遍历)。

例如,给定一个 3叉树 :

返回其层序遍历:

  1. [
  2. [1],
  3. [3,2,4],
  4. [5,6]
  5. ]

说明:

  1. 树的深度不会超过 1000
  2. 树的节点总数不会超过 5000
  1. #include <iostream>
  2. #include <vector>
  3. #include <queue>
  4. using namespace std;
  5. class Node {
  6. public:
  7. int val = NULL;
  8. vector<Node*> children;
  9. Node() {}
  10. Node(int _val, vector<Node*> _children) {
  11. val = _val;
  12. children = _children;
  13. }
  14. };
  15. /// BFS
  16. /// Store step in the queue
  17. ///
  18. /// Time Complexity: O(n)
  19. /// Space Complexity: O(n)
  20. class SolutionA{
  21. public:
  22. vector<vector<int>> levelOrder(Node* root){
  23. vector<vector<int>> res;
  24. if(!root)
  25. return res;
  26. queue<pair<Node*, int>> q;
  27. q.push(make_pair(root, 0));
  28. while(!q.empty()){
  29. Node* cur = q.front().first;
  30. int step = q.front().second;
  31. q.pop();
  32. if(step == res.size())
  33. res.push_back({cur->val});
  34. else
  35. res[step].push_back(cur->val);
  36. for(Node* next: cur->children)
  37. q.push(make_pair(next, step + 1));
  38. }
  39. return res;
  40. }
  41. };
  42. int main(){
  43. return 0;
  44. }

递归

N叉树的经典递归解法

经典递归法

我们在之前的章节中讲过如何运用递归法解决二叉树问题。在这篇文章中,我们着重介绍如何将这个思想引入到N叉树中。

在阅读以下内容之前,请确保你已阅读过 运用递归解决树的问题 这篇文章。

  1. "自顶向下"的解决方案

"自顶向下"意味着在每个递归层次上,我们首先访问节点以获得一些值,然后在调用递归函数时,将这些值传给其子节点。

一个典型的 "自顶向下" 函数 top_down(root, params) 的工作原理如下:

  1. 1. 对于 null 节点返回一个特定值
  2. 2. 如果有需要,对当前答案 answer 进行更新 // answer <-- params
  3. 3. for each child node root.children[k]:
  4. 4. ans[k] = top_down(root.children[k], new_params[k]) // new_params <-- root.val, params
  5. 5. 如果有需要,返回答案 answer // answer <-- all ans[k]
  1. "自底向上"的解决方案

"自底向上" 意味着在每个递归层次上,我们首先为每个子节点递归地调用函数,然后根据返回值和根节点本身的值给出相应结果。

一个典型的 "自底向上" 函数 bottom_up(root) 的工作原理如下:

  1. 1.对于 null 节点返回一个特定值
  2. 2.for each child node root.children[k]:
  3. 3. ans[k] = bottom_up(root.children[k]) // 为每个子节点递归地调用函数
  4. 4. 返回答案 answer // answer <- root.val, all ans[k]

Maximum Depth of N-ary Tree

给定一个 N 叉树,找到其最大深度。

最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。

例如,给定一个 3叉树 :

我们应返回其最大深度,3。

说明:

  1. 树的深度不会超过 1000
  2. 树的节点总不会超过 5000
  1. #include <iostream>
  2. #include <vector>
  3. using namespace std;
  4. /// DFS
  5. /// Time Complexity: O(n)
  6. /// Space Complexity: O(n)
  7. /// Definition for a Node.
  8. class Node{
  9. public:
  10. int val;
  11. vector<Node*> children;
  12. Node(){}
  13. Node(int _val, vector<Node*> _children){
  14. val = _val;
  15. children = _children;
  16. }
  17. };
  18. class Solution{
  19. public:
  20. int maxDepth(Node* root){
  21. if(!root)
  22. return 0;
  23. int res = 1;
  24. for(Node* child: root->children)
  25. res = max(res, 1 + maxDepth(child));
  26. return res;
  27. }
  28. };
  29. int main(){
  30. return 0;
  31. }

小结

这张卡旨在介绍N叉树的基本思想。 实际上,二叉树只是N叉树的一种特殊形式,N叉树相关问题的解决方案与二叉树的解法十分相似。 因此,我们可以把在二叉树中学到的知识扩展到N叉树中。

我们提供了一些经典的N叉树习题,以便进一步帮助你理解本章中N叉树的概念。

数据结构丨N叉树的更多相关文章

  1. C#数据结构-线索化二叉树

    为什么线索化二叉树? 对于二叉树的遍历,我们知道每个节点的前驱与后继,但是这是建立在遍历的基础上,否则我们只知道后续的左右子树.现在我们充分利用二叉树左右子树的空节点,分别指向当前节点的前驱.后继,便 ...

  2. js:数据结构笔记9--二叉树

    树:以分层的方式存储数据:节点:根节点,子节点,父节点,叶子节点(没有任何子节点的节点):层:根节点开始0层: 二叉树:每个节点子节点不超过两个:查找快(比链表),添加,删除快(比数组): BST:二 ...

  3. 线索化二叉树的构建与先序,中序遍历(C++版)

    贴出学习C++数据结构线索化二叉树的过程, 方便和我一样的新手进行测试和学习 同时欢迎各位大神纠正. 不同与普通二叉树的地方会用背景色填充 //BinTreeNode_Thr.h enum Point ...

  4. ID3算法 决策树 C++实现

    人工智能课的实验. 数据结构:多叉树 这个实验我写了好久,开始的时候从数据的读入和表示入手,写到递归建树的部分时遇到了瓶颈,更新样例集和属性集的办法过于繁琐: 于是参考网上的代码后重新写,建立决策树类 ...

  5. 树形动态规划(树状DP)小结

    树状动态规划定义 之所以这样命名树规,是因为树形DP的这一特殊性:没有环,dfs是不会重复,而且具有明显而又严格的层数关系.利用这一特性,我们可以很清晰地根据题目写出一个在树(型结构)上的记忆化搜索的 ...

  6. 【清北学堂2018-刷题冲刺】Contest 8

    Task 1:关联点 [问题描述]  ⼆叉树是⼀种常用的数据结构,⼀个⼆叉树或者为空,或者由根节点.左⼦树.右⼦树构成,其中左⼦树和右⼦树都是⼆叉树. 每个节点a 可以存储⼀个值val.  显然,如果 ...

  7. web(三)html标签

    标签的层级特性 闭合的html标签内可以包含一个或多个子标签,因此html的标签是一个多叉树的数据结构,多叉树的根是html标签. 标签的属性描述 每个标签都具备一组公用或当前标签独有的属性,属性的作 ...

  8. 数据结构与算法系列研究五——树、二叉树、三叉树、平衡排序二叉树AVL

    树.二叉树.三叉树.平衡排序二叉树AVL 一.树的定义 树是计算机算法最重要的非线性结构.树中每个数据元素至多有一个直接前驱,但可以有多个直接后继.树是一种以分支关系定义的层次结构.    a.树是n ...

  9. Python Treelib 多叉树 数据结构 中文使用帮助文档

    树,对于计算机编程语言来说是一个重要的数据结构.它具有广泛的应用,比如文件系统的分层数据结构和机器学习中的一些算法.这里创建了treelib来提供Python中树数据结构的高效实现. 官方文档:htt ...

随机推荐

  1. 一个字体,大小,颜色可定义的自绘静态框控件-XColorStatic 类(比较好看,一共19篇自绘文章)

    翻译来源:https://www.codeproject.com/Articles/5242/XColorStatic-a-colorizing-static-control XColor Stati ...

  2. TCPClient组件和TCPServer组件的主要方法和属性

    IdTCPClient属性1 : IOHandler 如果有相应的输入/输出操作,那么IOHandler相对应的组件或接口将提供一个虚拟/抽象的输入/输出接口给相应的网络连接2 : Intercept ...

  3. iOS NSString追加字符串的方法

    第一种: NSArray *array = [NSArray arrayWithObjects:@"Hello",@" ",@"world" ...

  4. MIPS开发板的“不二”选择——Creator Ci20单板计算机评测(芯片是君正JZ4780 ,也就是MIPS R3000,系统推荐Debian或深度,官网就有,其它语言有FreePascal和Go和Java和Python)

    在MIPS架构的CPU上开发软件,当然需要使用MIPS专用的工具链来编译代码.不过一般的LINUX发行版内都有相应的配套工具链供用户使用.Ci20出厂时的LINUX发行版为DEBIAN 7.5,相应的 ...

  5. C#try catch块

    try..catch块的出现是为了异常处理. 格式为:try{...可能发生异常的代码...} catch{...对异常的处理...} finaly{...无论如何都会执行的代码..} 上面的只是一般 ...

  6. zyltimer与ZylIdleTimer

    http://www.zylsoft.com/zyltimer.htmhttp://www.zylsoft.com/products.htm

  7. Mysql 自定义HASH索引带来的巨大性能提升

    有这样一个业务场景,需要在2个表里比较存在于A表,不存在于B表的数据.表结构如下: T_SETTINGS_BACKUP | CREATE TABLE `T_SETTINGS_BACKUP` ( `FI ...

  8. .Net上传文件处理三大范式,及开发注意事项

    最近工作内容涉及到一点前端的内容,把学习到的内容记录下来,在今后的开发过程中,不要犯错.本篇只针对一些刚入职的小白及前端开发人员,大牛请绕道!~ 刚开始我们先不讲上传文件的防范问题,先通过一个例子,让 ...

  9. Zookeeper详解-工作流和leader选举(三)

    一.工作流 一旦ZooKeeper集合启动,它将等待客户端连接.客户端将连接到ZooKeeper集合中的一个节点.它可以是leader或follower节点.一旦客户端被连接,节点将向特定客户端分配会 ...

  10. 08 JS的事件流的概念(重点)

    在学习jQuery的事件之前,大家必须要对JS的事件有所了解.看下文 事件的概念 HTML中与javascript交互是通过事件驱动来实现的,例如鼠标点击事件.页面的滚动事件onscroll等等,可以 ...