要求

  • 二叉树的前序遍历

实现

  • 递归

  • 栈模拟

        

  • 定义结构体 Command 模拟指令,字符串s描述命令,树节点node为指令作用的节点
  • 定义栈 Stack 存储命令
 1 #include <iostream>
2 #include <vector>
3 #include <stack>
4 #include <cassert>
5
6 using namespace std;
7
8 struct TreeNode{
9 int val;
10 TreeNode* left;
11 TreeNode* right;
12 TreeNode(int x): val(x), left(NULL), right(NULL){}
13 };
14
15 //模拟指令,go--访问,print--输出
16 struct Command{
17 string s;
18 TreeNode* node;
19 Command(string s, TreeNode* node):s(s), node(node){}
20 };
21
22 class Solution{
23 public:
24 vector<int> preorderTraversal(TreeNode* root){
25 vector<int> res;
26 if( root == NULL )
27 return res;
28
29 stack<Command> stack;
30 stack.push( Command("go",root) );
31
32 while( !stack.empty() ){
33
34 Command command = stack.top();
35 stack.pop();
36
37 if( command.s == "print" )
38 res.push_back( command.node -> val);
39 else{
40 assert( command.s == "go" );
41 if( command.node -> right )
42 stack.push( Command("go",command.node->right));
43 if( command.node -> left)
44 stack.push( Command("go",command.node->left));
45 stack.push( Command("print",command.node));
46 }
47 }
         return res;
48 }
49 };
50
51 void print_vec(const vector<int>& vec){
52 for(int e:vec)
53 cout<<e<<" ";
54 cout<<endl;
55 }
56
57 int main(){
58 TreeNode* root = new TreeNode(1);
59 root->right = new TreeNode(2);
60 root->right->left = new TreeNode(3);
61 vector<int> res = Solution().preorderTraversal(root);
62 print_vec(res);
63
64 return 0;
65 }

结果

1-2-3

总结

  • 模拟了操作系统中方法栈的实现
  • 入栈顺序与实际执行顺序相反
  • 操作指令,而不是操作对象,把对象的指针封装在指令中
  • LeetCode 94为模拟中序遍历,145为模拟后序遍历
  • 本程序可以很容易地改写为中序和后序,而课本中的写法很难改写

-----------------------------------------------

栈模拟中序遍历

  • Python
 1 class Stack(object):
2 def __init__(self, size_limit = None):
3 self._size_limit = size_limit
4 self.elements = []
5 self._size = 0
6
7 def push(self, value):
8 if self._size_limit is not None and len(self.elements) > self._size_limit:
9 raise IndexError("Stack is full")
10 else:
11 self.elements.append(value)
12 self._size += 1
13
14 def top(self):
15 return self.elements[-1]
16
17 def pop(self):
18 val = self.elements.pop()
19 self._size -=1
20 return val
21
22 def is_empty(self):
23 return self._size == 0
24
25
26 class Node:
27 def __init__(self, val):
28 self.val = val
29 self.lchild = None
30 self.rchild = None
31 self.flag = True
32
33 def dfs(node):
34 if node is None:
35 return
36 dfs(node.lchild)
37 print(node.val)
38 dfs(node.rchild)
39
40 def dfs1(node):
41 stack = Stack()
42 stack.push(node)
43 while not stack.is_empty():
44 tmp = stack.top()
45 while tmp.flag and tmp.lchild is not None:
46 tmp.flag = False
47 stack.push(tmp.lchild)
48 tmp = tmp.lchild
49 tmp = stack.pop()
50 print(tmp.val)
51 if tmp.rchild is not None:
52 stack.push(tmp.rchild)
53
54 def dfs2(node):
55 stack = Stack()
56 tmp = node
57 while tmp is not None or not stack.is_empty():
58 while tmp is not None:
59 stack.push(tmp)
60 tmp = tmp.lchild
61 tmp = stack.pop()
62 print(tmp.val)
63 tmp = tmp.rchild
64
65 root = Node(0)
66 node1 = Node(1)
67 root.lchild = node1
68 node2 = Node(2)
69 root.rchild = node2
70 node3 = Node(3)
71 node1.lchild = node3
72 node4 = Node(4)
73 node1.rchild = node4
74 node5 = Node(5)
75 node2.rchild = node5
76
77 dfs(root)
78 dfs1(root)
79 dfs2(root)

>> 3 1 4 0 2 5

    • dfs是常规递归思路
    • dfs1增加了一个flag变量,判断节点是否访问过,不加会导致死循环
    • dfs2调整了思路,不用flag变量
  • c++方法1
 1 class Solution {
2
3 public:
4 vector<int> inorderTraversal(TreeNode* root) {
5
6 vector<int> res;
7 if( root == NULL )
8 return res;
9
10 stack<TreeNode*> stack;
11 TreeNode* cur = root;
12 while(cur != NULL || !stack.empty()){
13
14 if(cur != NULL){
15 stack.push(cur);
16 cur = cur->left;
17 }
18 else {
19 cur = stack.top();
20 stack.pop();
21 res.push_back(cur->val);
22 cur = cur->right;
23 }
24 }
25 return res;
26 }
27 };
  • c++方法2
 1 class Solution {
2
3 public:
4 vector<int> inorderTraversal(TreeNode* root) {
5
6 vector<int> res;
7 if( root == NULL )
8 return res;
9
10 stack<TreeNode*> stack;
11 TreeNode* cur = root;
12 while(cur != NULL || !stack.empty()){
13
14 while(cur != NULL){
15 stack.push(cur);
16 cur = cur->left;
17 }
18
19 cur = stack.top();
20 stack.pop();
21 res.push_back(cur->val);
22 cur = cur->right;
23
24 }
25 return res;
26 }
27 };

总结

  • 默认的访问顺序是从左到右,意味着左节点优先访问
  • 中序遍历的定义是,从左节点返回时输出,这就意味着如果都是左节点,节点是从下到上输出的(后进先出),故想到利用栈实现;如果都是右节点,则节点从上到下输出,此时需控制栈,使元素不堆积
  • 想清楚了怎么处理左节点,怎么处理右节点,如何移动指针,如何利用栈,就可以写代码了
  • 左节点优先访问反映在代码上就是指针指向左节点的语句在指针指向右节点之前,中序遍历反映在代码上就是节点先入栈出栈再输出
  • 方法2表面上是两层循环,其实内外循环执行次数加起来和方法1是一样的,两种算法时间复杂度取决于节点个数,都是O(n)
  • 方法1的逻辑更顺畅,方法2利用了左节点优先的规则,将左节点连续出现的循环内移,在左节点连续出现的情况下执行效率更高(少一次判断),可看做方法1的优化

最新文章

  1. TAP/TUN(二)
  2. Oracle【IT实验室】数据库备份与恢复之三:OS备份/用户管理的备份与恢复
  3. DDNS 的工作原理及其在 Linux 上的实现--转
  4. &amp;lt;深入理解C指针&amp;gt;学习笔记和总结 第四章 指针和数组
  5. Android Service生命周期 Service里面的onStartCommand()方法详解
  6. 兼容IE、火狐、谷歌的页面关闭事件
  7. QOpenGLTexture 两个纹理叠加
  8. SPOJ DQUERY树状数组离线or主席树
  9. VB 字符串转换为UTF-8
  10. 初识服务器和Linux
  11. 3阶马尔可夫链 自然语言处理python
  12. $mount(“#app”)手动挂载
  13. IDEA对新建java线程池的建议
  14. 添加依赖:https://mvnrepository.com/
  15. org.apache.zookeeper.KeeperException$ConnectionLossException: KeeperErrorCode = ConnectionLoss for /eclipse20171118
  16. 每天一个linux命令(6) ar命令
  17. Material Design系列第四篇——Defining Shadows and Clipping Views
  18. spring @Transactional注解无效
  19. Struts2基本使用(二)--配置文件简述
  20. sed高级用法:模式空间(pattern space)和保持空间(hold space)

热门文章

  1. 洛谷P1290欧几里德游戏
  2. 截取pod ip地址最后一列
  3. 201871030116-李小龙 实验三 结对项目—《D{0-1}KP 实例数据集算法实验平台》项目报告
  4. python进阶(3)--条件判断、用户输入
  5. Python 面像对象编程(上)
  6. BeetleX数据分析中间服务V3
  7. ListBox控件简单的数据绑定
  8. hdu4560 不错的建图,二分最大流
  9. codeforces 229C
  10. LA4636积木艺术