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