写在前面
既然你能搜到这篇文章说明你对二叉树和递归方式的二叉树遍历方式肯定是已经了解了,不然也不会找到这里,那也就不多说,迭代方式的遍历同样也是借助于递归思想的方式实现,但是我们不需要构造递归函数,而是借助于数据结构栈来进行模拟递归的过程,不断进行压栈和弹栈操作,直到栈为空就可以起返回。
由于每个过程我都用到了计算树节点个数的函数,所以这里先贴一下计算树节点个数的代码,这个是用递归版本写的,当然也可以以迭代的方式求,不过为了简单就用递归了
struct TreeNode
{
int val;
struct TreeNode* left;
struct TreeNode* right;
};
// 求二叉树节点的个数
int sizeOfTree(struct TreeNode* root) {
if (root == NULL) return 0;
return sizeOfTree(root->left) + sizeOfTree(root->right) + 1;
}
前序遍历
二叉树的前序遍历迭代版本应该算是比较简单,思想就是先把根节点压入栈,然后不断判断栈是否为空,取出栈顶元素,把元素的值加在数组中,如果当前节点右子树节点不为空,那么,把右子树压入栈中,同时判断当前节点的左子树为不为空,如果不为空,那么直接把当前节点的左子树压入栈中。这里一定是先压入右子树节点然后再压入左子树节点。
可以假想一个最简单的只有三个节点的满二叉树,你就会明白为什么需要先压入右子树再压入左子树,因为对于栈来说,我们只能先操作栈顶的元素,也就是先进后出特性。
// 前序遍历迭代版本
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
int len = sizeOfTree(root); // 计算节点个数
int* ans = (int*)malloc(sizeof(int)*len); // 分配返回数组内存
*returnSize = len;
if (len == 0) return ans;// 特判
struct TreeNode** stack = (struct TreeNode**)malloc(sizeof(struct TreeNode*)*(len + 1));// 为栈分配内存 +1为了防止出现非法访问情况
int top = 0;
stack[top] = root;
struct TreeNode* cur = NULL;
int i = 0;
while (top != -1) { // 栈的非空判断
cur = stack[top--];
ans[i++] = cur->val;
if (cur->right != NULL) {
stack[++top] = cur->right;
}
if (cur->left != NULL) {
stack[++top] = cur->left;
}
}
return ans;
}
二叉树后续遍历
有了前序遍历的思想,我们想是不是把前序遍历过程中二叉树的左右子树压栈的顺序反过来就行了呢?其实不行,虽然这样出栈的时候先是右子树先出,但是需要考虑一个问题,根节点在这之前已经进行了遍历,所以需要在往数组中添加元素的时候从后边往前进行添加,同样可以想象一个只有3个节点的满二叉树,这样是不是就解决了这个问题了呢。答案是肯定的,相信简单推导一遍就可以得出结论。
// 后续遍历迭代版本
int *postorderTraversal(struct TreeNode *root, int *returnSize) {
int len = sizeOfTree(root);
*returnSize = len;
int* ans = (int*)malloc(sizeof(int)*len);
if (len == 0) return ans;
struct TreeNode** stack = (struct TreeNode**)malloc(sizeof(struct TreeNode*)*(len + 1));
int top = 0;
struct TreeNode* cur = NULL;
stack[top] = root;
while (top != -1) {
cur = stack[top--];
ans[--len] = cur->val;
if (cur->left!=NULL){
stack[++top] = cur->left;
}
if (cur->right != NULL) {
stack[++top] = cur->right;
}
}
return ans;
}
二叉树中序遍历
中序遍历和前序后序遍历的套路就不一样了,不过大致思路是一样的,还是借助于栈,只是在取出栈顶元素时,需要判断栈顶节点的左子节点是否为空,如果不为空的话说明这个元素还是不要填加的元素,可以想象递归写法的过程,是不是先判断左子树不为空的时候一直调用递归函数。那么同样地,这里需要把栈顶元素的左子树不断压入到栈中,直到栈顶元素的左子节点为空。然后就弹出栈顶元素,把值加入到数组中,同时把弹出节点的右子树压入栈中。
int *inorderTraversal(struct TreeNode* root, int* returnSize) {
int len = sizeOfTree(root);
int* ans = (int*)malloc(sizeof(int)*len);
*returnSize = 0;
if (len == 0) return ans;
struct TreeNode** stack = (struct TreeNode**)malloc(sizeof(struct TreeNode*)*(len+1));
int top = 0;
struct TreeNode* cur = root;
while (top != 0||cur != NULL) { // 这里需要斟酌一下为什么是top!=0
// 因为还有cur!=null 判断 进来后top自然会大于0
while (cur!=NULL) {
stack[top++] = cur;
cur = cur->left;
}
cur = stack[--top];
ans[(*returnSize)++] = cur->val;
cur = cur->right;
}
return ans;
}
或者也可以这么写
int *inorderTraversal(struct TreeNode* root, int* returnSize) {
int len = sizeOfTree(root);
int* ans = (int*)malloc(sizeof(int)*len);
*returnSize = 0;
if (len == 0) return ans;
struct TreeNode** stack = (struct TreeNode**)malloc(sizeof(struct TreeNode*)*(len+1));
int top = 0;
struct TreeNode* cur = root;
stack[0] = root;
while (top != -1) {
while (stack[top]!=NULL) {
stack[++top] = stack[top]->left;
}
top--;
if(top !=-1){
cur = stack[top];
ans[(*returnSize)++] = cur->val;
stack[top] = cur->right;
}
}
return ans;
}
关于top的判断自己可以简单想一个只有三个节点的满二叉树模拟一下过程就可以发现规律,需要自己理解压栈弹栈的过程。