要求

  • 二叉树的前序遍历

实现

  • 递归

  • 栈模拟

        

  • 定义结构体 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的优化

[刷题] 144 Binary Tree Preorder Traversal的更多相关文章

  1. 二叉树前序、中序、后序非递归遍历 144. Binary Tree Preorder Traversal 、 94. Binary Tree Inorder Traversal 、145. Binary Tree Postorder Traversal 、173. Binary Search Tree Iterator

    144. Binary Tree Preorder Traversal 前序的非递归遍历:用堆来实现 如果把这个代码改成先向堆存储左节点再存储右节点,就变成了每一行从右向左打印 如果用队列替代堆,并且 ...

  2. C++版 - LeetCode 144. Binary Tree Preorder Traversal (二叉树先根序遍历,非递归)

    144. Binary Tree Preorder Traversal Difficulty: Medium Given a binary tree, return the preorder trav ...

  3. 【LeetCode】144. Binary Tree Preorder Traversal (3 solutions)

    Binary Tree Preorder Traversal Given a binary tree, return the preorder traversal of its nodes' valu ...

  4. [LeetCode] 144. Binary Tree Preorder Traversal 二叉树的先序遍历

    Given a binary tree, return the preorder traversal of its nodes' values. For example:Given binary tr ...

  5. 刷题94. Binary Tree Inorder Traversal

    一.题目说明 题目94. Binary Tree Inorder Traversal,给一个二叉树,返回中序遍历序列.题目难度是Medium! 二.我的解答 用递归遍历,学过数据结构的应该都可以实现. ...

  6. 【LeetCode】144. Binary Tree Preorder Traversal

    题目: Given a binary tree, return the preorder traversal of its nodes' values. For example:Given binar ...

  7. Java for LeetCode 144 Binary Tree Preorder Traversal

    Given a binary tree, return the preorder traversal of its nodes' values. For example: Given binary t ...

  8. 144. Binary Tree Preorder Traversal

    Given a binary tree, return the preorder traversal of its nodes' values. For example:Given binary tr ...

  9. leetcode 144. Binary Tree Preorder Traversal ----- java

    Given a binary tree, return the preorder traversal of its nodes' values. For example:Given binary tr ...

随机推荐

  1. Redis系列-存储篇sorted set主要操作命令

    Redis系列-存储篇sorted set主要操作函数小结 redis支持有序集合,即sorted set.sorted set在set的基础上,增加了排序属性,是set的升级版.这里简要谈谈sort ...

  2. java面试-谈谈你对OOM的理解

    一.OOM(OutOfMemoryError): 对象无法释放或无法被垃圾回收,造成内存浪费,导致程序运行速度减慢,甚至系统崩溃等严重后果,就是内存泄漏.多个内存泄漏造成可使用内存变少,会导致内存溢出 ...

  3. tensorflow学习--数据加载

    文章主要来自Tensorflow官方文档,同时加入了自己的理解以及部分代码 数据读取 TensorFlow程序读取数据一共有3种方法: 供给数据(Feeding): 在TensorFlow程序运行的每 ...

  4. 逆向初级-PE(五)

    5.1.PE文件结构 1.什么是可执行文件? 可执行文件(executable fle)指的是可以由操作系统进行加载执行的文件. 可执行文件的格式: Windows平台: PE(Portable Ex ...

  5. 在 Docker Desktop 中启用 K8s 服务

    Overview 作为目前事实上的容器编排系统标准,K8s 无疑是现代应用的基石,很多同学入门可能直接就被卡到第一关,从哪去弄个 K8s 的环境 自己搭吧,要求的硬件资源太高,基本上搭建一个 K8s ...

  6. .Net程序内存泄漏解析

    一.概要 大概在今年三月份的时候突然被紧急调到另外一个项目组解决线上内存泄漏问题.经过两周的玩命奋战终于解决了这个问题这里把心路历程及思路分享给大家.希望可以帮助到各位或现在正遇到这样事情的小伙伴提供 ...

  7. 6. linux 专业词汇

    什么是交换分区? 交换分区是一个特殊的分区,他的作用相当于windows下的虚拟内存,这个分区的大小一般设置为物理内存的两倍. 什么是Grub? Grub是一个系统引导工具,通过它可以加载内核,从而引 ...

  8. (十八)VMware Harbor 镜像同步

    为什么需要镜像同步 由于对镜像的访问是一个核心的容器概念,在实际使用过程中,一个镜像库可能是不够用的,下例情况下,我们可能会需要部署多个镜像仓库: 国外的公有镜像下载过慢,需要一个中转仓库进行加速 容 ...

  9. 15个问题告诉你如何使用Java泛型

    摘要:Java泛型其本质是参数化类型,也就是说所操作的数据类型被指定为一个参数(type parameter)这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口.泛型方法. Java ...

  10. JDK8新特性(二) 流式编程Stream

    流式编程是1.8中的新特性,基于常用的四种函数式接口以及Lambda表达式对集合类数据进行类似流水线一般的操作 流式编程分为大概三个步骤:获取流 → 操作流 → 返回操作结果 流的获取方式 这里先了解 ...