要求
- 二叉树的前序遍历
实现
- 递归
- 栈模拟
- 定义结构体 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的优化