二叉树刷题笔记(2)迭代实现二叉树的中序遍历

本文介绍了使用迭代方式解决二叉树中序遍历问题,通过栈实现遍历逻辑,包括从根节点开始、利用栈存储节点、回溯处理等步骤。作者还分享了原始代码和优化后的代码,突出了代码简化过程中的关键改进。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前用递归的方法实现了二叉树的中序遍历,今天用迭代的方式做了一遍。题不难,但是相比递归算法会稍微复杂一些,这篇文章我想记录下我的思路、出的错是什么以及完成代码的精简。

解题思路

迭代的方法解决这道题的关键是栈。

1.从根节点(或者当前节点)开始,遍历每个节点的左节点,直到找到最左的那个叶子节点,把节点的值放入res(存放排好序的值的数组),

2.然后把这个最左叶子节点的父节点的值放入res,

3.之后,让这个父节点的右节点也重复1,2操作;如果没有右节点,那么回溯到这个节点的父节点,重复1,2,操作,直到遍历完整个二叉树

但是,单向链表,只能父节点指向子节点,子节点不能指向父节点,在我们的解题思路中第2、3步,“把最左叶子节点的父节点的值放入res”,以及让父节点的右节点重复1,2操作就需要栈来是实现。

我们在进行第1步:从根节点(或者当前节点)开始,遍历每个节点的左节点时,把每个节点(以结构体指针的形式)依次压入栈中,如:root,root->left,root->left->left...,直到找到最左叶子节点K(此时K->left==NULL)

第2步:按照栈“先入后出”的特点,此时的栈顶就是最左叶子节点K的父节点,出栈,先把此节点的值放入res

第3步:判断此节点有无右节点,

有,让右节点作为当前节点重复1,2,操作。

无,回溯到此节点的父节点,让其父节点作为当前节点重复2,3操作。

直到遍历完整个二叉树:如何判断二叉树是否遍历完?只需判断栈是否为空。初始先将root压入栈中,开始循环,期间只要二叉树没有遍历完,栈就不为空。

初版代码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
 //迭代算法:
//用栈
 int* inorderTraversal(struct TreeNode* root, int* returnSize) {
    int* res=malloc(sizeof(int)*501);
    struct TreeNode**tem=(struct TreeNode**)malloc(sizeof(struct TreeNode*)*101);
    struct TreeNode* p=(struct TreeNode*)malloc(sizeof(struct TreeNode));
    struct TreeNode* q=(struct TreeNode*)malloc(sizeof(struct TreeNode));//两个指针变量
    // if(!root) return res;//无法搞定?当root等于
    int top=0,bottom=0;//栈下标
    *returnSize=0;//关键一步我嘞个去!
    p=root;//初值
    tem[0]=root;
    int tag=1; //用于判断当前节点的左节点是否找过.1:没有,0:找过
    if(root) //无法搞定?当root等于
    {
        
        while(bottom<=top)//栈不空,不结束
        {
            if((tag==1)&&(p->left!=NULL))
            {
                q=p->left;
                tem[++top]=q;
                p=q;
                
            }
            else //两种情况:1.左节点为NULL,2.回溯到父节点,此时都要把当前值存入res中,并且,开始遍历右边子树
            {
                
                res[(*returnSize)++]=p->val;//存值
                if(p->right)//右节点入栈
                {
                    tem[top]=p->right;
                // tag=1;//新的右节点,左子树都没查过,所以需要将左节点都入栈

                }
                //如果已经无右节点,那么回溯,这时候的tag置0,因为不能在便利左子树了。
                if(!(p->right))//
                {
                    top--;
                    tag=0;
                }
                p=tem[top];
            }
        }
    }
    return res;   
}

改进

刚开始有思路就直接写了,凡是要用到变量就直接新定义,所以初始代码写得很冗长。于是改进如下:

1.结构体指针q和p,没必要定义,直接用root表示当前节点。

2.不用flag,重写while循环如下:

while(top>0||root)
    {
        while(root)//直到将叶子节点入栈
        {
            tem[top++]=root;
            root=root->left;
        }
        root=tem[--top];
        res[(*returnSize)++]=root->val;
        root=root->right;
    }
    return res;  

在第1步,从当前节点开始,依次把节点和左子结点压入栈中,直到root==NULL,也就是叶子节点也压入栈中。

然后再从栈顶取出一个节点,将这个节点的值放入res。

接着取这个节点的右节点作为当前节点,如果右节点为NULL,则会跳过入栈的while循环,直接到出栈(也就是回溯到了父节点),如果右节点不为NULL,则会继续重复步骤1.

3.关于while(top>0||root):表示top>0或者root不为空时,进入循环。为什么不直接用top>=0?如果用了,那么在最后一个叶子节点出栈后,top=0,root=root->right为空,之后会进入root=tem[- - top],此时会数组下标越界。虽然我的第一版代码通过了测试,但是我感觉仍然有这个风险。

所以最终版代码如下:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
/*void inorder(struct TreeNode* root, int*res,int*resSize) {
    //左根右
    //递归
    //如果当前节点为null,返回
    //反之 遍历左边,打印本身,然后遍历右边
    //每遍历到第一个左节点为null的节点就要把节点存入res
     if(!root) return ;
    inorder(root->left,res,resSize);
    res[(*resSize)++]=root->val;
    inorder(root->right,res,resSize);

 }
 */

//迭代算法:
//用栈
 int* inorderTraversal(struct TreeNode* root, int* returnSize) {
    int* res=malloc(sizeof(int)*501);
    struct TreeNode**tem=(struct TreeNode**)malloc(sizeof(struct TreeNode*)*101);
    int top=0;//栈下标
    *returnSize=0;//关键一步我嘞个去!    
    while(top>0||root)
    {
        while(root)//直到将叶子节点入栈
        {
            tem[top++]=root;
            root=root->left;
        }
        root=tem[--top];
        res[(*returnSize)++]=root->val;
        root=root->right;
    }
    return res;   

 }
    

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我要好好学cs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值