leetcode 94二叉树的中序遍历
递归算法C++代码:
1 /** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<int> inorderTraversal(TreeNode* root) { 13 vector<int> tra; 14 Morder(root,tra); 15 return tra; 16 } 17 void Morder(TreeNode* root,vector<int> &tra){ 18 if(root==NULL) return; 19 if(root->left) 20 Morder(root->left,tra); 21 tra.push_back(root->val); 22 if(root->right) 23 Morder(root->right,tra); 24 } 25 };
非递归方法(迭代):通过stack容器
C++代码:O(n)空间复杂度,O(n)时间复杂度
自己写的,实际上为将递归方法代码用stack具体化,需要注意的是加上了回溯与向下递归的判别;
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ /** 迭代法:具象描述递归执行过程 1)检查当前p是否为NULL,如果非NULL,当前节点入栈,只要当前节点有左子节点,左子节点入栈; 2)1之后,取栈顶元素p,由于1的操作,p没有左子节点,那么访问当前节点的值,且p出栈; 3)接下来访问右节点,p=p->right,如果右子节点存在,那么入栈,循环结束; **/ class Solution { public: vector<int> inorderTraversal(TreeNode* root) { if(root==NULL) return {}; vector<int>res; stack<TreeNode*>s; TreeNode*p=root; s.push(p); while(!s.empty()){ while(p&&p->left){ p=p->left;s.push(p); } p=s.top(); res.push_back(p->val);s.pop(); //此处先令p=p->right,p可用作第一个while判断是向下递归还是回溯,如果递归过程p为非空,如果为NULl则代表回溯,那么下一轮不用向左递归; p=p->right; if(p) s.push(p); } return res; } };
别人的代码:
1 /** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<int> inorderTraversal(TreeNode* root) { 13 //使用栈的非递归方法 14 vector<int> res; 15 stack<TreeNode*> st; 16 TreeNode* T=root; 17 while(T||!st.empty()){ 18 //将T的所有左孩子入栈 19 while(T){ 20 st.push(T); 21 T=T->left; 22 } 23 //访问T的元素,然后转到T的右孩子 24 if(!st.empty()){ 25 T=st.top(); 26 st.pop(); 27 res.push_back(T->val); 28 T=T->right; 29 } 30 } 31 return res; 32 } 33 };
方法三:Morris Traversal
由于需要用到Thread Binary Tree(线索二叉树),参考 透彻理解线索二叉树 彻底理解线索二叉树
Morris Traversal方法遍历二叉树(非递归,不用栈,O(1)空间)
对于n个节点的二叉树,左右孩子指针为2n个,利用的指针为n-1个,没有利用的指针为n+1个;利用空链域存储前驱和后继:
记ptr指向二叉链表中的一个结点,以下是建立线索的规则:
(1)如果ptr->lchild为空,则存放指向中序遍历序列中该结点的前驱结点。这个结点称为ptr的中序前驱;
(2)如果ptr->rchild为空,则存放指向中序遍历序列中该结点的后继结点。这个结点称为ptr的中序后继;
当然,Morris遍历只用到了线索二叉树的思想和部分操作,线索二叉树的更细节的东西此处不赘述。对于本题,实际上只用到了对叶节点的右指针的线索化,以及通过线索化的指针进行回溯。主要有以下两点:
#1 对所有叶节点的右指针的线索化,令其指向中序遍历的后继节点;
#2 通过线索化的节点,访问中序遍历的后继节点,并恢复被线索化的节点;
参考:二叉树的遍历:先序中序后序遍历的递归与非递归实现及层序遍历
C++代码如下:O(1)空间复杂度,O(n)时间复杂度
1 /** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 vector<int> inorderTraversal(TreeNode* root) { 13 //Morris Traversal:右线索化+回溯 14 vector<int> res; 15 if(!root) return res; 16 TreeNode *p=root; 17 18 while(p){ 19 //定义两个节点指针变量p和p的左孩子pLeft 20 TreeNode *pLeft=p->left; 21 if(pLeft){ 22 //访问p的左孩子的最右子孩子(即pLeft右孩子的右孩子的右孩子...) 23 //线索化之前pLeft的最右子孩子的right指针指向NULL, 24 //线索化之后pLeft的最右子孩子的right指向中序遍历中该节点的后继节点p 25 //所以循环终止条件为pLeft->right==NULL 或 pLeft->right==p 26 while(pLeft->right && pLeft->right!=p){ 27 pLeft=pLeft->right; 28 } 29 //此时pLeft代表p的左孩子的最右子孩子 30 //pLeft->right==NULL代表没有被线索化,进行线索化然后访问p的左孩子 31 if(pLeft->right==NULL){ 32 pLeft->right=p; 33 p=p->left; 34 continue; 35 } 36 //pLeft->right!=NULL代表已经被线索化,此时已经回溯到原来的节点p(第2次访问),所以要恢复被线索化的pLeft的最右子孩子 37 else{ 38 pLeft->right=NULL; 39 } 40 } 41 res.push_back(p->val); 42 p=p->right;//访问右孩子(对非叶节点),回溯到中序遍历的后续节点(对叶节点); 43 //因为线索化的操作最终是对所有的叶节点进行的,所以上述语句实际有访问右孩子和回溯两个功能; 44 } 45 return res; 46 } 47 };