代码随想录算法训练营第十四天| 递归遍历 、迭代遍历、统一迭代

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; // 返回结果数组
}

  • 33
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值