力扣173
中序cpp
初始化时将包括根节点在内的左链入栈。
栈顶元素即为需要输出的下一个节点,将栈顶元素拿到后,判断该节点有无右子树,如果有,将右子树的左链入栈,左链即为一个树从根开始,看成一个^形状的左侧/这条边上包括根的所有点。
拿到栈顶元素,直接出栈并输出该节点。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class BSTIterator {
//栈顶元素即为需要遍历的下一个节点,将栈顶元素输出后,判断该节点有无右子树,如果有,将右子树的左链入栈,左链
//即为一个树从根开始,看成一个^形状的左侧/这条边上包括根的所有点。
//求中序遍历的后继acwing19(有*father指针)的思路是分为两种情况,(1)当前节点有右子树,则后继节点为右子树中//的最左边的数字(2)无右子树,后继节点为往左上走到尽头后的第一个向右上的拐点。
public:
stack<TreeNode*> stk;
BSTIterator(TreeNode* root) {
while (root) {
stk.push(root);
root = root->left;
}
}
int next() {
auto root = stk.top();
stk.pop();
int val = root->val;
root = root->right;
while (root) {
stk.push(root);
root = root->left;
}
return val;
}
bool hasNext() {
return stk.size();
}
};
中序Java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class BSTIterator {
//实际是考察非递归中序遍历写法
private Deque<TreeNode> stack=new LinkedList<TreeNode>();
public BSTIterator(TreeNode root) {
while(root!=null){
stack.push(root);
root=root.left;
}
}
public int next() {
TreeNode root=stack.pop();
int val=root.val;
root=root.right;
while(root!=null){
stack.push(root);
root=root.left;
}
return val;
}
public boolean hasNext() {
return stack.size()>0;
}
}
/**
* Your BSTIterator object will be instantiated and called as such:
* BSTIterator obj = new BSTIterator(root);
* int param_1 = obj.next();
* boolean param_2 = obj.hasNext();
*/
非该道力扣题解的中序cpp版非递归
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> s;
vector<int> v;
TreeNode* cur=root;
while(cur || !s.empty())
{
while(cur)
{
s.push(cur);
cur=cur->left;
}
//来到这,左路结点全都入栈
//访问栈顶元素
//对栈顶元素的右子树左同样操作
TreeNode* top=s.top();
s.pop();
v.push_back(top->val);
TreeNode* right=top->right;//右子树
cur=right;//模拟递归
}
return v;
}
力扣144
前序cpp
分析:
1.访问所有左路结点,并且入栈
2.取出栈顶结点的右子树,如果它不为空就模拟递归,即将其赋给cur上去(即以该节点访问所有左路结点…),否则就取下一个栈顶元素
3.直到当前结点为空(栈顶右子树),并且栈为空说明所有元素已访问过
初始化时也将包括根节点在内的左链入栈。
栈顶元素不是需要输出的下一个节点,将栈顶元素拿到后,判断该节点有无右子树,如果有,将右子树的左链入栈(入栈之前先将这个节点输出),左链即为一个树从根开始,看成一个^形状的左侧/这条边上包括根的所有点。
每次入栈之前就已经将这个节点输出。
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
//1.先逐个访问左路结点,并将其入栈
//2.再访问栈顶元素的右子树
stack<TreeNode*> s;
vector<int> v;
TreeNode* cur=root;
//只有cur为空,并且栈空,说明所有元素访问完
while(cur || !s.empty())
{
while(cur)//左路结点入栈
{
v.push_back(cur->val);
s.push(cur);
cur=cur->left;
}
//来到这左路结点已入栈
TreeNode* right=s.top()->right;//栈顶的右子树
s.pop();//弹出栈顶
cur=right;//将右子树赋给cur上去继续判断(cur是当前结点的右子树,它是可能为空的,当它为空时,继续取下一个栈顶的右子树,否则要将其赋给cur,再次
//当做关键结点,即左路结点入栈,取栈顶,访问右子树...)
}
return v;
}
};
前序Java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res=new LinkedList<Integer>();
Deque<TreeNode> stack=new LinkedList<TreeNode>();
while(root!=null){
res.add(root.val);
stack.push(root);
root=root.left;
}
while(stack.size()!=0){
root=stack.pop();
root=root.right;
while(root!=null){
res.add(root.val);
stack.push(root);
root=root.left;
}
}
return res;
}
}
后序cpp
和中序也很类似,将所有左路结点入栈后,但是取出栈顶元素这时候也不访问,而是直接模拟递归其右子树(取出栈顶元素可以直接访问的情况有2种,一种是当前栈顶结点的右子树为空,第二种是当前结点的右子树已经访问过了,这个可以用一个指针标记一下)
初始化时也将包括根节点在内的左链入栈。
栈顶元素不是需要输出的下一个节点,将栈顶元素拿到后,判断该节点有无右子树,如果有,将右子树的左链入栈,左链即为一个树从根开始,看成一个^形状的左侧/这条边上包括根的所有点。
拿到栈顶元素时也不将该栈顶节点输出和出栈,只有当右子树为空或者上一次访问过的元素就是栈顶的右子树根结点才出栈并输出该栈顶节点。
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> s;
vector<int> v;
TreeNode* cur=root;
TreeNode* prev=nullptr;
while(cur || !s.empty())
{
while(cur)
{
s.push(cur);
cur=cur->left;
}
//来到这,左路结点都已经入栈
TreeNode* top=s.top();//栈顶元素
TreeNode* right=top->right;//栈顶元素的右子树
if(right==nullptr || top->right==prev)//栈顶元素的右子树为空/或者上一次访问过的元素就是栈顶的右子树根结点(说明访问过回来了),直接可以访问栈顶元素
{
v.push_back(top->val);
prev=top;//记录上一次访问的结点
s.pop();
}
else//栈顶元素不空
{
cur=right;
}
}
return v;
}
总结:
入栈顺序都是,先将一颗树的左链入栈,之后拿到栈顶元素,判断该元素的右子树是否为空,非空则将该右子树的左链入栈。
出栈顺序和输出顺序不一样:
中序遍历:每次访问到栈顶元素一定出栈,出栈同时输出到中序遍历结果数组。
前序遍历:每次访问到栈顶元素一定出栈,入栈的时候就输出到前序遍历结果数组。
后序遍历:每次访问到栈顶元素不一定出栈,只有当栈顶元素的右子树为空或者上一次访问过的元素恰好是栈顶元素的右子树根结点时才出栈,出栈同时输出到后序遍历结果数组。
参考原文链接(为了做笔记和总结自己记录一遍):
https://blog.youkuaiyun.com/lyl194458/article/details/92645093
另:力扣144 方法三Morris 遍历 马一下待看