第一次尝试
给定一个二叉树,返回它的 后序 遍历。LeetCode链接
- 方法:非递归法,递归的方法在我前面二叉树博客中写过,感兴趣的可以看看,这次主要是使用栈来完成遍历的过程,其实也就是相当于使用栈模拟递归;
- 主要操作:①从根节点开始遍历,遍历最左路径,遇到的每一个节点都入栈;②直到左路为空,取栈顶元素并用临时变量保存,如果该节点可以访问并出栈,那么就访问出栈,如果该节点不能访问且出栈,那么就让 root 结点走到栈顶元素的右子树,接着执行第①步;判断是否可以访问出栈的条件是:该节点没有右子树或者右子树已访问完成;③直到结点为空且栈为空结束遍历。
//使用这种方式来重命名数据类型,这样可以很方便的修改后续数据的数据类型,相当于#define的作用
typedef struct TreeNode* StackType;
//创建栈
typedef struct Stack {
//使用指针指向一块动态开辟的内存
StackType* _date;
//表示栈中的有效数据,也代表了栈顶
size_t _size;
//表示栈的最大容量
size_t _capacity;
}Stack;
//包含所有函数的声明
//栈初始化
void StackInit(Stack* s1);
//检查栈是否已满
void CheckCapacity(Stack* s1);
//进栈
void StackPush(Stack* s1, StackType val);
//出栈
void StackPop(Stack* s1);
// 获取栈顶元素
StackType StackTop(Stack* s1);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* s1);
//栈初始化
void StackInit(Stack* s1) {
//参数合法性检验
if (s1 == NULL) {
return;
}
s1->_date = NULL;
s1->_capacity = s1->_size = 0;
}
//检查栈是否已满
void CheckCapacity(Stack* s1) {
//该函数是在其他函数内部被调用的,所以就不用参数合法性检验了,因为前面已经检查过了
//如果有效数据等于最大容量,那么需要增容
if (s1->_capacity == s1->_size) {
//每次以2倍的空间进行增容
s1->_capacity = s1->_capacity == 0 ? 1 : 2 * s1->_capacity;
//用realloc函数进行动态增容
s1->_date = (StackType*)realloc(s1->_date, sizeof(StackType) * s1->_capacity);
}
}
//进栈
void StackPush(Stack* s1, StackType val) {
//参数合法性检验
if (s1 == NULL) {
return;
}
//检查容量是否够,不够的话就增容
CheckCapacity(s1);
s1->_date[s1->_size] = val;
s1->_size++;
}
//出栈
void StackPop(Stack* s1) {
//参数合法性检验,如果没有数据就直接返回
if (s1 == NULL || s1->_size == 0) {
return;
}
//这是顺序表,所以不用像链表一样,删除一个数据就要释放一个空间,只需有效数据个数减一即可
s1->_size--;
}
// 获取栈顶元素
StackType StackTop(Stack* s1) {
//参数合法性检验不好做,因为不能以任何值作为出错的返回值,以后学了抛出异常就可解决
return s1->_date[s1->_size - 1];
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* s1) {
//参数合法性检验
if (s1 == NULL || s1->_size == 0) {
return 1;
}
return 0;
}
//获取二叉树结点个数,以确定动态开辟的数组大小
int getSize(struct TreeNode* root){
if(root == NULL){
return 0;
}
return getSize(root->left) + getSize(root->right) + 1;
}
//将遍历结果保存在数组中,并且将参数中的 returnSize 赋值为数组大小
int* postorderTraversal(struct TreeNode* root, int* returnSize){
//参数合法性检验
if(root == NULL){
*returnSize = 0;
return NULL;
}
//创建栈
Stack s;
StackInit(&s);
//获取二叉树大小
int sz = getSize(root);
//动态开辟数组,保存遍历结果
int* ret = (int*)malloc(sizeof(int) * sz);
int i = 0;
//用来保存上一次访问的节点;作用是可以判断右子树是否访问过
struct TreeNode* prve = NULL;
//开始循环遍历
while(root || !StackEmpty(&s)){
//访问左子树,若果不为空,那么就将其入栈
while(root){
prve = root;
StackPush(&s,root);
root = root->left;
}
//设置临时变量保存栈顶元素
struct TreeNode* top = StackTop(&s);
//如果栈顶元素满足访问条件,那么就访问并出栈
if(top->right == NULL || top->right == prve){
ret[i++] = top->val;
StackPop(&s);
//更新变量
prve = top;
}
//如果不能访问,那就继续遍历栈顶右子树
else{
root = top->right;
}
}
//返回所需的值
*returnSize = sz;
return ret;
}