1.递归遍历
题目链接/文章讲解/视频讲解:代码随想录
1.1前序遍历
1.1.1分析及思路
先访问根节点,在访问左右子树。
1.1.2代码及注释
// 定义一个函数traversal,参数为指向TreeNode结构体的指针root,指向int类型的result和returnSize的指针
void traversal(struct TreeNode* root,int* result,int* returnSize){
// 如果root为空,直接返回
if(root == NULL)
return;
// 将root的值存入result数组中,并将returnSize加1
result[(*returnSize)++] = root->val;
// 递归调用traversal函数,传入左子树的指针和result、returnSize的指针
traversal(root->left,result,returnSize);
// 递归调用traversal函数,传入右子树的指针和result、returnSize的指针
traversal(root->right,result,returnSize);
}
// 定义一个函数preorderTraversal,参数为指向TreeNode结构体的指针root和指向int类型的returnSize的指针
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
// 分配一个大小为101的int类型数组,并将其地址赋给result指针
//题目中给的范围是[0,100]共101个空间
int* result = (int*)malloc(sizeof(int) * 101);
// 将returnSize指向的值设为0
*returnSize = 0;
// 调用traversal函数,传入root指针和result、returnSize的指针
traversal(root,result,returnSize);
// 返回result指针
return result;
}
1.2后序遍历代码
void traversal(struct TreeNode* root,int* result,int* returnSize) {
if(root == NULL)
return;
traversal(root->left,result,returnSize);
traversal(root->right,result,returnSize);
result[(*returnSize)++] = root->val;
}
int* postorderTraversal(struct TreeNode* root, int* returnSize) {
int* result = (int*)malloc(sizeof(int)*101);
*returnSize = 0;
traversal(root,result,returnSize);
return result;
}
1.3中序遍历代码
void traversal(struct TreeNode* root,int* result,int* returnSize) {
if(root == NULL)
return;
traversal(root->left,result,returnSize);
result[(*returnSize)++] = root->val;
traversal(root->right,result,returnSize);
}
int* inorderTraversal(struct TreeNode* root, int* returnSize) {
int* result = (int*)malloc(sizeof(int)*101);
*returnSize = 0;
traversal(root,result,returnSize);
return result;
}
2.迭代遍历
题目链接/文章讲解/视频讲解:代码随想录
2.1前序遍历
2.1.1方法1:
2.1.1.1分析及思路
我们知道,递归的实现是借助栈实现的,所以迭代的实现也借助栈来实现。
我们将结点先入栈再进行访问,然后再讲其左右孩子入栈,由于栈的先进后出的机制,所以入栈时我们先入右孩子。
总结就是每次出栈,就取数据和其左右孩子入栈。结束条件即为栈空。
2.1.1.2顺序栈实现代码及注释
int* preorderTraversal(struct TreeNode* root, int* returnSize) { // 定义一个函数,参数为根节点和返回结果的大小指针
int* res = (int*)malloc(sizeof(int)*101); // 树中节点数目在范围 [0, 100] 内
*returnSize = 0; // 将返回结果的大小指针初始化为0
if(root == NULL){ // 如果根节点为空
return res; // 返回空的遍历结果数组
}
struct TreeNode* stk[101]; // 树中节点数目在范围 [0, 100] 内
struct TreeNode* node = root; // 将当前节点设为根节点
int stk_top = 0; // 初始化栈顶指针为0
stk[stk_top++] = node; // 将根节点入栈
while(stk_top>0){ // 当栈不为空时循环
node = stk[--stk_top]; // 出栈一个节点
res[(*returnSize)++] = node->val; // 将节点的值存入遍历结果数组,并更新返回结果大小
if(node->right) // 如果节点有右子节点
stk[stk_top++] = node->right; // 将右子节点入栈
if(node->left) // 如果节点有左子节点
stk[stk_top++] = node->left; // 将左子节点入栈
}
return res; // 返回遍历结果数组
}
2.1.1.3链式栈实现代码及注释
typedef struct TreeNode ElemType; // 定义结构体TreeNode为ElemType类型
typedef struct Linknode{
ElemType* data; // 数据域,存放ElemType类型的数据
struct Linknode *next; // 指针域,指向下一个Linknode结点的指针
}*LinkStack, LinkNode; // 定义LinkStack为Linknode*类型,LinkNode为Linknode类型
bool StackEmpty(LinkNode Stack){
if(Stack.next == NULL) // 如果栈为空,返回true
return true;
else // 如果栈不为空,返回false
return false;
}
bool Push(LinkStack Stack, ElemType* data){
LinkNode* Node = (LinkNode*)malloc(sizeof(LinkNode)); // 分配一个LinkNode大小的内存空间
if(Node == NULL) // 若分配内存失败则返回false
return false;
Node->data = data; // 给新结点赋值
Node->next = Stack->next; // 设置新结点的next域为头结点的next域
Stack->next = Node; // 设置头结点的next为新结点的地址
return true;
}
bool Pop(LinkStack Stack, ElemType** data){
if(Stack->next == NULL) // 如果栈为空,返回false
return false;
LinkNode* temp = Stack->next; // 临时变量temp指向栈顶元素
*data = temp->data; // 将栈顶元素的数据赋值给data
Stack->next = temp->next; // 更新栈顶指针,指向下一个元素
free(temp); // 释放栈顶元素的内存空间
return true;
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
int* res = (int*)malloc(sizeof(int)*101); // 分配存放结果的内存空间
*returnSize = 0; // 初始化返回结果的大小为0
if(root == NULL){
return res; // 如果根节点为空,直接返回结果数组
}
LinkNode* Stack = (LinkNode*)malloc(sizeof(LinkNode)); // 创建一个栈
struct TreeNode* node = root; // 初始化遍历节点为根节点
Stack->next = NULL; // 初始化栈为空
Push(Stack, node); // 将根节点推入栈中
while(!StackEmpty(*Stack)){// 当栈不为空时循环
Pop(Stack, &node); // 弹出栈顶节点
res[(*returnSize)++] = node->val; // 将节点值存入结果数组中
if(node->right != NULL) // 如果右子节点不为空,将右子节点推入栈中
Push(Stack, node->right);
if(node->left != NULL) // 如果左子节点不为空,将左子节点推入栈中
Push(Stack, node->left);
}
return res; // 返回结果数组
}
2.1.2方法2:
2.1.2.1分析及思路
和递归非常像,递归就是访问完一直向左走,为空就转向右。
void traversal(struct TreeNode* root,int* result,int* returnSize){
// 如果root为空,直接返回
if(root == NULL)
return;
// 将root的值存入result数组中,并将returnSize加1
result[(*returnSize)++] = root->val;
// 递归调用traversal函数,传入左子树的指针和result、returnSize的指针
traversal(root->left,result,returnSize);
// 递归调用traversal函数,传入右子树的指针和result、returnSize的指针
traversal(root->right,result,returnSize);
}
1.访问根,跟入栈,下次访问其左孩子
2.左孩子不为空则转向执行1
3.左孩子为空,则出栈执行1
2.1.2.2顺序栈实现代码及注释
// 定义一个指向整型数组的指针,用于存储前序遍历的结果
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
// 分配内存空间,大小为101个整型的大小
int* res = (int*)malloc(sizeof(int)*101);
// 初始化返回结果的大小为0
*returnSize = 0;
// 如果根节点为空,直接返回结果数组
if(root == NULL){
return res;
}
// 定义一个栈,用于存储遍历过程中的节点
struct TreeNode* Stack[101];
// 初始化栈顶指针为0
int Stack_Top = 0;
// 初始化当前节点为根节点
struct TreeNode* node = root;
// 循环直到栈为空且当前节点为空
while( (Stack_Top != 0) || (node != NULL) ){
// 如果当前节点不为空
if(node != NULL){
// 将当前节点的值存入结果数组,返回结果大小加1
res[(*returnSize)++] = node->val;
// 将当前节点压入栈中
Stack[Stack_Top++] = node;
// 继续遍历左子树
node = node->left;
}
// 如果当前节点为空
else{
// 弹出栈顶节点,并将当前节点指向栈顶节点的右子树
node = Stack[--Stack_Top];
node = node->right;
}
}
// 返回结果数组
return res;
}
2.1.2.3链式栈实现代码及注释
typedef struct TreeNode ElemType; //定义结构体TreeNode并起一个别名ElemType
typedef struct Linknode{
ElemType* data; //数据域,存储ElemType类型的数据
struct Linknode *next;//指针域,指向下一个Linknode结点
}*LinkStack,LinkNode; //定义LinkStack为Linknode*类型,LinkNode为Linknode类型
bool StackEmpty(LinkNode Stack){ //定义函数StackEmpty,判断栈是否为空
if(Stack.next == NULL) //栈为空返回true
return true;
else //不空返回false
return false;
}
bool Push(LinkStack Stack,ElemType* data){ //定义函数Push,向栈中压入数据
LinkNode* Node = (LinkNode*)malloc(sizeof(LinkNode)); //分配内存给新结点
if(Node == NULL) //若分配内存失败则返回false
return false;
Node->data = data; //给新结点赋值
Node->next = Stack->next;//设置新结点的next域为头结点的next域
Stack->next = Node; //设置头结点的next为新结点的地址
return true;
}
bool Pop(LinkStack Stack,ElemType** data){ //定义函数Pop,从栈中弹出数据
if(Stack->next == NULL) //如果栈为空,返回false
return false;
LinkNode* temp = Stack->next; //临时变量temp指向栈顶结点
*data = temp->data; //将栈顶结点的数据赋值给data
Stack->next = temp->next; //将头结点的next指向栈顶结点的下一个结点
free(temp); //释放栈顶结点的内存
return true;
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) { //定义函数preorderTraversal,实现二叉树的前序遍历
int* res = (int*)malloc(sizeof(int)*101); //分配内存给存储结果的数组
*returnSize = 0; //初始化返回结果的大小为0
if(root == NULL){ //如果根节点为空,直接返回结果数组
return res;
}
LinkNode* Stack = (LinkNode*)malloc(sizeof(LinkNode)); //创建一个链栈
struct TreeNode* node = root; //定义一个指向根节点的指针
Stack->next = NULL; //初始化链栈为空
while( !StackEmpty(*Stack) || (node != NULL) ){ //当栈不为空或当前节点不为空时循环
if(node != NULL){ //如果当前节点不为空
res[(*returnSize)++] = node->val; //将当前节点的值存入结果数组
Push(Stack,node); //将当前节点压入栈中
node = node->left; //继续遍历左子树
}
else{ //当前节点为空
Pop(Stack,&node); //从栈中弹出节点
node = node->right; //遍历右子树
}
}
return res; //返回结果数组
}
2.2后序遍历
2.2.1方法1:
2.2.1.1分析及思路
与前序遍历类似,如果我们入栈的时候先入栈右,就会变成根右左,我们再来一个逆置,就会变成左右根也就是后序遍历。
反转字符串可看这里:代码随想录算法训练营第八天| 344.反转字符串 、 541. 反转字符串II、 卡码网:54.替换数字 、 151.翻转字符串里的单词 、卡码网:55.右旋转字符串-CSDN博客
2.2.1.2顺序栈实现代码及注释
// 该函数用于将数组中的元素进行反转
void reserve(int res[], int len){
int left = 0; // 定义左指针初始位置为数组第一个元素
int right = len-1; // 定义右指针初始位置为数组最后一个元素
int temp; // 定义一个临时变量用于交换元素值
while(left < right){ // 当左指针小于右指针时,进行循环
temp = res[left]; // 将左指针指向的元素值存储到临时变量中
res[left] = res[right]; // 将右指针指向的元素值赋给左指针指向的元素
res[right] = temp; // 将临时变量中的值赋给右指针指向的元素
left++; // 左指针向右移动一位
right--; // 右指针向左移动一位
}
}
int* postorderTraversal(struct TreeNode* root, int* returnSize) { // 定义一个函数,参数为根节点和返回结果的大小指针
int* res = (int*)malloc(sizeof(int)*101); // 树中节点数目在范围 [0, 100] 内
*returnSize = 0; // 将返回结果的大小指针初始化为0
if(root == NULL){ // 如果根节点为空
return res; // 返回空的遍历结果数组
}
struct TreeNode* stk[101]; // 树中节点数目在范围 [0, 100] 内
struct TreeNode* node = root; // 将当前节点设为根节点
int stk_top = 0; // 初始化栈顶指针为0
stk[stk_top++] = node; // 将根节点入栈
while(stk_top>0){ // 当栈不为空时循环
node = stk[--stk_top]; // 出栈一个节点
res[(*returnSize)++] = node->val; // 将节点的值存入遍历结果数组,并更新返回结果大小
if(node->left) // 如果节点有左子节点
stk[stk_top++] = node->left; // 将左子节点入栈
if(node->right) // 如果节点有右子节点
stk[stk_top++] = node->right; // 将右子节点入栈
}
reserve(res,(*returnSize));//反转
return res; // 返回遍历结果数组
}
2.2.1.3链式栈实现代码及注释
typedef struct TreeNode ElemType; // 定义结构体TreeNode为ElemType类型
typedef struct Linknode{
ElemType* data; // 数据域,存放ElemType类型的数据
struct Linknode *next; // 指针域,指向下一个Linknode结点的指针
}*LinkStack, LinkNode; // 定义LinkStack为Linknode*类型,LinkNode为Linknode类型
bool StackEmpty(LinkNode Stack){
if(Stack.next == NULL) // 如果栈为空,返回true
return true;
else // 如果栈不为空,返回false
return false;
}
bool Push(LinkStack Stack, ElemType* data){
LinkNode* Node = (LinkNode*)malloc(sizeof(LinkNode)); // 分配一个LinkNode大小的内存空间
if(Node == NULL) // 若分配内存失败则返回false
return false;
Node->data = data; // 给新结点赋值
Node->next = Stack->next; // 设置新结点的next域为头结点的next域
Stack->next = Node; // 设置头结点的next为新结点的地址
return true;
}
bool Pop(LinkStack Stack, ElemType** data){
if(Stack->next == NULL) // 如果栈为空,返回false
return false;
LinkNode* temp = Stack->next; // 临时变量temp指向栈顶元素
*data = temp->data; // 将栈顶元素的数据赋值给data
Stack->next = temp->next; // 更新栈顶指针,指向下一个元素
free(temp); // 释放栈顶元素的内存空间
return true;
}
// 该函数用于将数组中的元素进行反转
void reserve(int res[], int len){
int left = 0; // 定义左指针初始位置为数组第一个元素
int right = len-1; // 定义右指针初始位置为数组最后一个元素
int temp; // 定义一个临时变量用于交换元素值
while(left < right){ // 当左指针小于右指针时,进行循环
temp = res[left]; // 将左指针指向的元素值存储到临时变量中
res[left] = res[right]; // 将右指针指向的元素值赋给左指针指向的元素
res[right] = temp; // 将临时变量中的值赋给右指针指向的元素
left++; // 左指针向右移动一位
right--; // 右指针向左移动一位
}
}
int* postorderTraversal(struct TreeNode* root, int* returnSize) {
int* res = (int*)malloc(sizeof(int)*101); // 分配存放结果的内存空间
*returnSize = 0; // 初始化返回结果的大小为0
if(root == NULL){
return res; // 如果根节点为空,直接返回结果数组
}
LinkNode* Stack = (LinkNode*)malloc(sizeof(LinkNode)); // 创建一个栈
struct TreeNode* node = root; // 初始化遍历节点为根节点
Stack->next = NULL; // 初始化栈为空
Push(Stack, node); // 将根节点推入栈中
while(!StackEmpty(*Stack)){// 当栈不为空时循环
Pop(Stack, &node); // 弹出栈顶节点
res[(*returnSize)++] = node->val; // 将节点值存入结果数组中
if(node->left != NULL) // 如果左子节点不为空,将左子节点推入栈中
Push(Stack, node->left);
if(node->right != NULL) // 如果右子节点不为空,将右子节点推入栈中
Push(Stack, node->right);
}
reserve(res,(*returnSize));//反转
return res; // 返回结果数组
}
2.2.2方法2:
2.2.2.1分析及思路
左右根就是,从根开始看它的左孩子,左孩子有就看它左孩子的左孩子,直到没有左孩子。
然后看它有没有右孩子,有就重复执行看右孩子有没有左右孩子。若根节点没有右孩子就直接输出根节点。
1.沿着根节点的左孩子依次入栈,直到左孩子为空
2.然后看其栈顶结点,看它有没有右孩子,有则执行1,没有则出栈访问它,再次期间还要判断它是否已经被访问过。
每次访问一个结点,就代表结束了一个以这个结点为根的子树,所以要把node设置为NULL。
栈和要访问的结点都为空时,结束循环。
2.2.2.2顺序栈实现代码及注释
int* postorderTraversal(struct TreeNode* root, int* returnSize) {
int* res = (int*)malloc(sizeof(int)*101); // 分配存放结果的内存空间
*returnSize = 0; // 初始化返回结果的大小为0
if(root == NULL){
return res; // 如果根节点为空,直接返回结果数组
}
struct TreeNode* Stack[101];
struct TreeNode* node = root; // 初始化遍历节点为根节点
struct TreeNode* r = NULL;//用于保存上一个访问的结点
int StackTop = 0;
while( StackTop>0 || node != NULL ){// 当栈不为空时循环
if(node != NULL){//结点不为空
Stack[StackTop++] = node;//结点入栈
node = node->left;//转向结点的左孩子
}
else{
node = Stack[StackTop-1];//读取栈顶元素
if( (node->right != NULL) && (r != node->right) ){//右孩子不为空且未访问过
node = node->right;
}
else{
node = Stack[--StackTop];//弹出栈顶元素
res[(*returnSize)++] = node->val; // 将节点值存入结果数组中
r = node;//标记该以被访问的结点
node = NULL;//设置结点为空,为下次获取栈顶元素做准备
}
}
}
return res; // 返回结果数组
}
2.2.2.3链式栈实现代码及注释
typedef struct TreeNode ElemType; // 定义结构体TreeNode为ElemType类型
typedef struct Linknode{
ElemType* data; // 数据域,存放ElemType类型的数据
struct Linknode *next; // 指针域,指向下一个Linknode结点的指针
}*LinkStack, LinkNode; // 定义LinkStack为Linknode*类型,LinkNode为Linknode类型
bool StackEmpty(LinkNode Stack){
if(Stack.next == NULL) // 如果栈为空,返回true
return true;
else // 如果栈不为空,返回false
return false;
}
bool Push(LinkStack Stack, ElemType* data){
LinkNode* Node = (LinkNode*)malloc(sizeof(LinkNode)); // 分配一个LinkNode大小的内存空间
if(Node == NULL) // 若分配内存失败则返回false
return false;
Node->data = data; // 给新结点赋值
Node->next = Stack->next; // 设置新结点的next域为头结点的next域
Stack->next = Node; // 设置头结点的next为新结点的地址
return true;
}
bool Pop(LinkStack Stack, ElemType** data){
if(Stack->next == NULL) // 如果栈为空,返回false
return false;
LinkNode* temp = Stack->next; // 临时变量temp指向栈顶元素
*data = temp->data; // 将栈顶元素的数据赋值给data
Stack->next = temp->next; // 更新栈顶指针,指向下一个元素
free(temp); // 释放栈顶元素的内存空间
return true;
}
bool GetTop(LinkStack Stack, ElemType** data){
if(Stack->next == NULL) // 如果栈为空,返回false
return false;
*data = Stack->next->data; // 将栈顶元素的数据赋值给data
return true;
}
int* postorderTraversal(struct TreeNode* root, int* returnSize) {
int* res = (int*)malloc(sizeof(int)*101); // 分配存放结果的内存空间
*returnSize = 0; // 初始化返回结果的大小为0
if(root == NULL){
return res; // 如果根节点为空,直接返回结果数组
}
LinkNode* Stack = (LinkNode*)malloc(sizeof(LinkNode)); // 创建一个栈
struct TreeNode* node = root; // 初始化遍历节点为根节点
Stack->next = NULL; // 初始化栈为空
struct TreeNode* r = NULL;//用于保存上一个访问的结点
while( !StackEmpty(*Stack) || node != NULL ){// 当栈不为空时循环
if(node != NULL){//结点不为空
Push(Stack, node);//结点入栈
node = node->left;//结点转向其左孩子
}
else{
GetTop(Stack, &node);//读取栈顶元素
if( (node->right != NULL) && (r != node->right) ){//若结点不为空且不是右孩子返回的
node = node->right;//转向右孩子
}
else{
Pop(Stack, &node); // 弹出栈顶节点
res[(*returnSize)++] = node->val; // 将节点值存入结果数组中
r = node;//标记访问过的结点
node = NULL;//一轮结束,下次要从栈顶取值
}
}
}
printf("%d",*returnSize);
return res; // 返回结果数组
}
2.3中序遍历
2.3.1分析及思路
与前序遍历类似,前序遍历是先访问,在入栈,这个是先入栈,在转到其左孩子,为空则出栈访问,不为空则访问左孩子再出栈访问它,再访问它的有孩子。
A入栈,看它有没有左孩子,有B则入栈,看B有没有左孩子有D入栈,看D有没有左孩子没有则D出栈访问它,看D有没有右孩子,没有则再次出栈,访问B,再看B的右孩子,右孩子E入栈,判断有没有左孩子,没有出栈访问E,判断有无右孩子,出栈访问A,判断A有无右孩子,C入栈,判断它有无左孩子,出栈访问它,判断有无右孩子没有则结束。
1.沿着根节点的左孩子,依次入栈,直到左孩子为空
2.出栈栈顶元素并访问,切换为其右孩子,若右孩子存在则执行1,若不存在则继续执行2
2.3.2顺序栈实现代码及注释
// 定义一个指向整型数组的指针,用于存储前序遍历的结果
int* inorderTraversal(struct TreeNode* root, int* returnSize) {
// 分配内存空间,大小为101个整型的大小
int* res = (int*)malloc(sizeof(int)*101);
// 初始化返回结果的大小为0
*returnSize = 0;
// 如果根节点为空,直接返回结果数组
if(root == NULL){
return res;
}
// 定义一个栈,用于存储遍历过程中的节点
struct TreeNode* Stack[101];
// 初始化栈顶指针为0
int Stack_Top = 0;
// 初始化当前节点为根节点
struct TreeNode* node = root;
// 循环直到栈为空且当前节点为空
while( (Stack_Top != 0) || (node != NULL) ){
// 如果当前节点不为空
if(node != NULL){
// 将当前节点压入栈中
Stack[Stack_Top++] = node;
// 继续遍历左子树
node = node->left;
}
// 如果当前节点为空
else{
// 弹出栈顶节点,并将当前节点指向栈顶节点的右子树
node = Stack[--Stack_Top];
// 将当前节点的值存入结果数组,返回结果大小加1
res[(*returnSize)++] = node->val;
node = node->right;
}
}
// 返回结果数组
return res;
}
2.3.3链式栈实现代码及注释
typedef struct TreeNode ElemType; //定义结构体TreeNode并起一个别名ElemType
typedef struct Linknode{
ElemType* data; //数据域,存储ElemType类型的数据
struct Linknode *next;//指针域,指向下一个Linknode结点
}*LinkStack,LinkNode; //定义LinkStack为Linknode*类型,LinkNode为Linknode类型
bool StackEmpty(LinkNode Stack){ //定义函数StackEmpty,判断栈是否为空
if(Stack.next == NULL) //栈为空返回true
return true;
else //不空返回false
return false;
}
bool Push(LinkStack Stack,ElemType* data){ //定义函数Push,向栈中压入数据
LinkNode* Node = (LinkNode*)malloc(sizeof(LinkNode)); //分配内存给新结点
if(Node == NULL) //若分配内存失败则返回false
return false;
Node->data = data; //给新结点赋值
Node->next = Stack->next;//设置新结点的next域为头结点的next域
Stack->next = Node; //设置头结点的next为新结点的地址
return true;
}
bool Pop(LinkStack Stack,ElemType** data){ //定义函数Pop,从栈中弹出数据
if(Stack->next == NULL) //如果栈为空,返回false
return false;
LinkNode* temp = Stack->next; //临时变量temp指向栈顶结点
*data = temp->data; //将栈顶结点的数据赋值给data
Stack->next = temp->next; //将头结点的next指向栈顶结点的下一个结点
free(temp); //释放栈顶结点的内存
return true;
}
int* inorderTraversal(struct TreeNode* root, int* returnSize) { //定义函数preorderTraversal,实现二叉树的前序遍历
int* res = (int*)malloc(sizeof(int)*101); //分配内存给存储结果的数组
*returnSize = 0; //初始化返回结果的大小为0
if(root == NULL){ //如果根节点为空,直接返回结果数组
return res;
}
LinkNode* Stack = (LinkNode*)malloc(sizeof(LinkNode)); //创建一个链栈
struct TreeNode* node = root; //定义一个指向根节点的指针
Stack->next = NULL; //初始化链栈为空
while( !StackEmpty(*Stack) || (node != NULL) ){ //当栈不为空或当前节点不为空时循环
if(node != NULL){ //如果当前节点不为空
Push(Stack,node); //将当前节点压入栈中
node = node->left; //继续遍历左子树
}
else{ //当前节点为空
Pop(Stack,&node); //从栈中弹出节点
res[(*returnSize)++] = node->val; //将当前节点的值存入结果数组
node = node->right; //遍历右子树
}
}
return res; //返回结果数组
}
2.4统一方法
2.4.1中序遍历
2.4.1.1分析及思路
和上面的类似,我们要输出的结点前加上NULL。给一个根节点,我们呢判断它是否有左右孩子,
有右孩子的右孩子先入栈,根节点再入栈,紧接着加一个NULL入栈,然后判断它是否有左孩子,有则入栈。这样出栈时就是左右根的顺序。因为此时的根节点,左右孩子已经判断完毕,所以在它入栈之后接一个NULL,标记为下次访问到NULL时,它就可以出栈进行数据的输出。左右孩子还未判断其是否存在左右孩子,所以它不能跟NULL。
2.4.1.2顺序栈实现代码及注释
int* inorderTraversal(struct TreeNode* root, int* returnSize) { // 中序遍历函数
int* res = (int*)malloc(sizeof(int)*101); // 分配内存空间
*returnSize = 0; // 初始化返回大小为0
if(root == NULL){ // 如果根节点为空
return res; // 返回结果数组
}
struct TreeNode* Stack[101]; // 创建栈数组
int Stack_Top = 0; // 栈顶指针初始化为0
struct TreeNode* node = root; // 初始化节点为根节点
Stack[Stack_Top++] = node; // 根节点入栈
while( (Stack_Top != 0) ){ // 当栈不为空时循环
node = Stack[Stack_Top-1]; // 获取栈顶节点
if(node != NULL){ // 如果节点不为空
node = Stack[--Stack_Top]; // 出栈
if(node->right) // 如果有右子节点
Stack[Stack_Top++] = node->right; // 将右子节点入栈
Stack[Stack_Top++] = node; // 将当前节点入栈
Stack[Stack_Top++] = NULL; // 将空节点入栈
if(node->left) // 如果有左子节点
Stack[Stack_Top++] = node->left; // 将左子节点入栈
}
else{ // 如果节点为空
Stack_Top--; // 栈顶指针减1
node = Stack[--Stack_Top]; // 出栈
res[(*returnSize)++] = node->val; // 将节点值存入结果数组
}
}
return res; // 返回结果数组
}
2.4.1.3链式栈实现代码及注释
typedef struct TreeNode ElemType; // 定义结构体TreeNode的别名ElemType
typedef struct Linknode{ // 定义链栈节点结构体
ElemType* data; // 数据域为ElemType指针
struct Linknode *next; // 指针域指向下一个节点
} *LinkStack, LinkNode; // 定义LinkStack为链栈指针类型,LinkNode为链栈节点类型
bool StackEmpty(LinkNode Stack){ // 判断链栈是否为空
if(Stack.next == NULL) // 如果链栈的下一个节点为空
return true; // 返回true
else
return false; // 返回false
}
bool Push(LinkStack Stack, ElemType* data){ // 入栈操作
LinkNode* Node = (LinkNode*)malloc(sizeof(LinkNode)); // 分配节点内存
if(Node == NULL) // 如果分配失败
return false; // 返回false
Node->data = data; // 将数据存入节点
Node->next = Stack->next; // 将节点插入链栈
Stack->next = Node; // 更新链栈的指向
return true; // 返回true
}
bool Pop(LinkStack Stack, ElemType** data){ // 出栈操作
if(Stack->next == NULL) // 如果链栈为空
return false; // 返回false
LinkNode* temp = Stack->next; // 暂存待出栈节点
*data = temp->data; // 将数据传出
Stack->next = temp->next; // 更新链栈的指向
free(temp); // 释放节点内存
return true; // 返回true
}
bool GetTop(LinkStack Stack, ElemType** data){ // 获取栈顶元素
if(Stack->next == NULL) // 如果链栈为空
return false; // 返回false
*data = Stack->next->data; // 将栈顶元素传出
return true; // 返回true
}
int* inorderTraversal(struct TreeNode* root, int* returnSize) { // 中序遍历函数
int* res = (int*)malloc(sizeof(int)*101); // 分配结果数组内存
*returnSize = 0; // 初始化返回大小为0
if(root == NULL){ // 如果根节点为空
return res; // 返回结果数组
}
LinkNode* Stack = (LinkNode*)malloc(sizeof(LinkNode)); // 分配链栈内存
struct TreeNode* node = root; // 初始化节点为根节点
Stack->next = NULL; // 链栈初始化为空
Push(Stack, node); // 根节点入栈
while( !StackEmpty(*Stack) ){ // 当链栈不为空时循环
GetTop(Stack, &node); // 获取栈顶元素
if(node != NULL){ // 如果节点不为空
Pop(Stack, &node); // 出栈
if(node->right) // 如果有右子节点
Push(Stack,node->right); // 将右子节点入栈
Push(Stack,node); // 将当前节点入栈
Push(Stack,NULL); // 将空节点入栈
if(node->left) // 如果有左子节点
Push(Stack,node->left); // 将左子节点入栈
}
else{ // 如果节点为空
Pop(Stack, &node); // 出栈
Pop(Stack, &node); // 出栈
res[(*returnSize)++] = node->val; // 将节点值存入结果数组
}
}
return res; // 返回结果数组
}
2.4.2前序遍历
2.4.2.1分析及思路
和中序遍历一样我们入栈的顺序改变一下即可,总体思路就是判断该结点是不是有左右孩子,若有则右左孩子入栈,然后它再入栈,最后入栈NULL标记要输出的结点。
2.4.2.2顺序栈实现代码及注释
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* 注意:返回的数组必须使用malloc分配内存,假设调用者会调用free()来释放内存。
*/
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
int* res = (int*)malloc(sizeof(int)*101); // 分配一个大小为101的整型数组作为结果数组
*returnSize = 0; // 初始化返回数组的大小为0
if(root == NULL){ // 如果根节点为空,直接返回结果数组
return res;
}
struct TreeNode* Stack[101]; // 定义一个大小为101的栈数组
int Stack_Top = 0; // 初始化栈顶指针为0
struct TreeNode* node = root; // 初始化当前节点为根节点
Stack[Stack_Top++] = node; // 将根节点入栈
while( (Stack_Top != 0) ){ // 当栈不为空时循环
node = Stack[Stack_Top-1]; // 获取栈顶节点
if(node != NULL){ // 如果节点不为空
node = Stack[--Stack_Top]; // 弹出栈顶节点
if(node->right) // 如果节点有右子节点,将右子节点入栈
Stack[Stack_Top++] = node->right;
if(node->left) // 如果节点有左子节点,将左子节点入栈
Stack[Stack_Top++] = node->left;
Stack[Stack_Top++] = node; // 将当前节点再次入栈
Stack[Stack_Top++] = NULL; // 入栈一个空节点作为标记
}
else{ // 如果节点为空
Stack_Top--; // 弹出空节点
node = Stack[--Stack_Top]; // 弹出当前节点
res[(*returnSize)++] = node->val; // 将当前节点的值存入结果数组
}
}
return res; // 返回结果数组
}
2.4.1.3链式栈实现代码及注释
typedef struct TreeNode ElemType; // 定义结构体TreeNode为ElemType
typedef struct Linknode{ // 定义链栈结构体Linknode
ElemType* data; // 数据域为ElemType指针
struct Linknode *next; // 指针域为Linknode指针
} *LinkStack, LinkNode; // 定义LinkStack为Linknode指针,LinkNode为Linknode
bool StackEmpty(LinkNode Stack){ // 判断链栈是否为空
if(Stack.next == NULL) // 如果链栈的下一个节点为空
return true; // 返回true
else
return false; // 否则返回false
}
bool Push(LinkStack Stack, ElemType* data){ // 入栈操作
LinkNode* Node = (LinkNode*)malloc(sizeof(LinkNode)); // 创建新节点
if(Node == NULL) // 如果新节点为空
return false; // 返回false
Node->data = data; // 将数据压入栈
Node->next = Stack->next; // 新节点指向栈顶节点
Stack->next = Node; // 栈顶指向新节点
return true; // 返回true
}
bool Pop(LinkStack Stack, ElemType** data){ // 出栈操作
if(Stack->next == NULL) // 如果栈顶节点为空
return false; // 返回false
LinkNode* temp = Stack->next; // 临时节点指向栈顶节点
*data = temp->data; // 将栈顶数据赋给data
Stack->next = temp->next; // 栈顶指向下一个节点
free(temp); // 释放栈顶节点的内存
return true; // 返回true
}
bool GetTop(LinkStack Stack, ElemType** data){ // 获取栈顶元素
if(Stack->next == NULL) // 如果栈顶节点为空
return false; // 返回false
*data = Stack->next->data; // 将栈顶数据赋给data
return true; // 返回true
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) { // 前序遍历函数
int* res = (int*)malloc(sizeof(int)*101); // 分配内存空间
*returnSize = 0; // 初始化返回大小为0
if(root == NULL){ // 如果根节点为空
return res; // 返回结果数组
}
LinkNode* Stack = (LinkNode*)malloc(sizeof(LinkNode)); // 创建链栈
struct TreeNode* node = root; // 初始化节点为根节点
Stack->next = NULL; // 栈顶指向空
Push(Stack, node); // 将根节点入栈
while( !StackEmpty(*Stack) ){ // 当栈不为空时循环
GetTop(Stack, &node); // 获取栈顶节点
if(node != NULL){ // 如果节点不为空
Pop(Stack, &node); // 出栈
if(node->right) // 如果有右子节点
Push(Stack,node->right); // 将右子节点入栈
if(node->left) // 如果有左子节点
Push(Stack,node->left); // 将左子节点入栈
Push(Stack,node); // 将当前节点入栈
Push(Stack,NULL); // 将空节点入栈
}
else{ // 如果节点为空
Pop(Stack, &node); // 出栈
Pop(Stack, &node); // 再次出栈
res[(*returnSize)++] = node->val; // 将节点值存入结果数组
}
}
return res; // 返回结果数组
}
2.4.3后序遍历
2.4.3.1分析及思路
和上面的类似,谁可以出栈就在其前面加一个NULL,根节点入栈接上一个NULL,然后右孩子入栈,左孩子入栈。
2.4.3.2顺序栈实现代码及注释
int* postorderTraversal(struct TreeNode* root, int* returnSize) { // 后序遍历函数
int* res = (int*)malloc(sizeof(int)*101); // 分配结果数组内存
*returnSize = 0; // 初始化返回大小为0
if(root == NULL){ // 如果根节点为空
return res; // 返回结果数组
}
struct TreeNode* Stack[101]; // 定义节点指针数组作为栈
int Stack_Top = 0; // 栈顶指针初始化为0
struct TreeNode* node = root; // 初始化节点为根节点
Stack[Stack_Top++] = node; // 根节点入栈
while( (Stack_Top != 0) ){ // 当栈不为空时循环
node = Stack[Stack_Top-1]; // 获取栈顶元素
if(node != NULL){ // 如果节点不为空
node = Stack[--Stack_Top]; // 出栈
if(node->right) // 如果有右子节点
Stack[Stack_Top++] = node->right; // 右子节点入栈
Stack[Stack_Top++] = node; // 当前节点入栈
Stack[Stack_Top++] = NULL; // 空节点入栈
if(node->left) // 如果有左子节点
Stack[Stack_Top++] = node->left; // 左子节点入栈
}
else{ // 如果节点为空
Stack_Top--; // 出栈
node = Stack[--Stack_Top]; // 获取栈顶元素
res[(*returnSize)++] = node->val; // 将节点值存入结果数组
}
}
return res; // 返回结果数组
}
2.4.3.3链式栈实现代码及注释
typedef struct TreeNode ElemType; // 定义结构体TreeNode的别名ElemType
typedef struct Linknode{ // 定义链栈节点结构体
ElemType* data; // 数据域为ElemType指针
struct Linknode *next; // 指针域指向下一个节点
} *LinkStack, LinkNode; // 定义LinkStack为链栈指针类型,LinkNode为链栈节点类型
bool StackEmpty(LinkNode Stack){ // 判断链栈是否为空
if(Stack.next == NULL) // 如果链栈的下一个节点为空
return true; // 返回true
else
return false; // 返回false
}
bool Push(LinkStack Stack, ElemType* data){ // 入栈操作
LinkNode* Node = (LinkNode*)malloc(sizeof(LinkNode)); // 分配节点内存
if(Node == NULL) // 如果分配失败
return false; // 返回false
Node->data = data; // 将数据存入节点
Node->next = Stack->next; // 将节点插入链栈
Stack->next = Node; // 更新链栈的指向
return true; // 返回true
}
bool Pop(LinkStack Stack, ElemType** data){ // 出栈操作
if(Stack->next == NULL) // 如果链栈为空
return false; // 返回false
LinkNode* temp = Stack->next; // 暂存待出栈节点
*data = temp->data; // 将数据传出
Stack->next = temp->next; // 更新链栈的指向
free(temp); // 释放节点内存
return true; // 返回true
}
bool GetTop(LinkStack Stack, ElemType** data){ // 获取栈顶元素
if(Stack->next == NULL) // 如果链栈为空
return false; // 返回false
*data = Stack->next->data; // 将栈顶元素传出
return true; // 返回true
}
int* postorderTraversal(struct TreeNode* root, int* returnSize) { // 后序遍历函数
int* res = (int*)malloc(sizeof(int)*101); // 分配结果数组内存
*returnSize = 0; // 初始化返回大小为0
if(root == NULL){ // 如果根节点为空
return res; // 返回结果数组
}
LinkNode* Stack = (LinkNode*)malloc(sizeof(LinkNode)); // 分配链栈内存
struct TreeNode* node = root; // 初始化节点为根节点
Stack->next = NULL; // 链栈初始化为空
Push(Stack, node); // 根节点入栈
while( !StackEmpty(*Stack) ){ // 当链栈不为空时循环
GetTop(Stack, &node); // 获取栈顶元素
if(node != NULL){ // 如果节点不为空
Pop(Stack, &node); // 出栈
Push(Stack,node); // 将当前节点入栈
Push(Stack,NULL); // 将空节点入栈
if(node->right) // 如果有右子节点
Push(Stack,node->right); // 将右子节点入栈
if(node->left) // 如果有左子节点
Push(Stack,node->left); // 将左子节点入栈
}
else{ // 如果节点为空
Pop(Stack, &node); // 出栈
Pop(Stack, &node); // 出栈
res[(*returnSize)++] = node->val; // 将节点值存入结果数组
}
}
return res; // 返回结果数组
}