代码随想录算法训练营第十五天| 226.翻转二叉树 (优先掌握递归) 、 101. 对称二叉树 (优先掌握递归)

226.翻转二叉树 (优先掌握递归) 

题目链接/文章讲解/视频讲解:代码随想录

题目:给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

1.前序遍历

1.1分析及思路

对每一个结点进行左后孩子交换,也就是遍历每一个结点,即可以选择前中后序遍历以及层序遍历,来实现结点的调换。

递归法实现前序遍历

递归的思路:

1.确定递归函数的参数和返回值

返回值类型:最终是要传回一个root,所以是struct TreeNode*类型

参数:传入时,传入的是根节点,以及左右孩子。所以参数类型为struct TreeNode*

2.确定终止条件

当传入NULL时终止

3.确定单层递归的逻辑

对单个结点我们要做的是,判断它是否为NULL,是则直接return,否则则交换它左右孩子,然后传入它的左右孩子。

1.2代码及注释

/*功能:用于交换两个指针指向的节点
*参数:left: 指向左节点的指针 right: 指向右节点的指针
*/
void swap(struct TreeNode** left, struct TreeNode** right){
    struct TreeNode* temp; // 定义一个临时变量用于交换节点
    temp = *left; // 将左节点指向的节点赋值给临时变量
    *left = *right; // 将右节点指向的节点赋值给左节点指向的节点
    *right = temp; // 将临时变量赋值给右节点指向的节点
}

// 定义一个函数用于翻转二叉树
struct TreeNode* invertTree(struct TreeNode* root) {
    if(root == NULL) // 如果根节点为空,则返回空
        return NULL;
    
    swap(&(root->left),&(root->right)); // 交换当前节点的左右子树
    invertTree(root->left); // 递归翻转左子树
    invertTree(root->right); // 递归翻转右子树
    return root; // 返回根节点
}

2.后序遍历

2.1分析及思路

与上题目类似,交换的顺序变了。

2.2代码及注释

/*功能:用于交换两个指针指向的节点
*参数:left: 指向左节点的指针 right: 指向右节点的指针
*/
void swap(struct TreeNode** left, struct TreeNode** right){
    struct TreeNode* temp; // 定义一个临时变量用于交换节点
    temp = *left; // 将左节点指向的节点赋值给临时变量
    *left = *right; // 将右节点指向的节点赋值给左节点指向的节点
    *right = temp; // 将临时变量赋值给右节点指向的节点
}

// 定义一个函数用于翻转二叉树
struct TreeNode* invertTree(struct TreeNode* root) {
    if(root == NULL) // 如果根节点为空,则返回空
        return NULL;
    
    invertTree(root->left); // 递归翻转左子树
    invertTree(root->right); // 递归翻转右子树
    swap(&(root->left),&(root->right)); // 交换当前节点的左右子树
    return root; // 返回根节点
}

3.中序遍历

3.1分析及思路

中序遍历有所不同,它是先传入左孩子,然后交换,在传入右孩子,会发生传入两次左孩子的错误。只需要传入两次左孩子即可。

3.2代码及注释

/*功能:用于交换两个指针指向的节点
*参数:left: 指向左节点的指针 right: 指向右节点的指针
*/
void swap(struct TreeNode** left, struct TreeNode** right){
    struct TreeNode* temp; // 定义一个临时变量用于交换节点
    temp = *left; // 将左节点指向的节点赋值给临时变量
    *left = *right; // 将右节点指向的节点赋值给左节点指向的节点
    *right = temp; // 将临时变量赋值给右节点指向的节点
}

// 定义一个函数用于翻转二叉树
struct TreeNode* invertTree(struct TreeNode* root) {
    if(root == NULL) // 如果根节点为空,则返回空
        return NULL;
    
    invertTree(root->left); // 递归翻转左子树
    swap(&(root->left),&(root->right)); // 交换当前节点的左右子树
    invertTree(root->left); // 递归翻转左子树
    return root; // 返回根节点
}

4.层序遍历

4.1分析及思路

思路是一样的,对每一个结点交换左右孩子即可。

4.2代码及注释

typedef struct Node ElemType;  // 定义结构体 Node 为 ElemType
#define MaxSize 101  // 定义队列的最大容量为 101
 
typedef struct {
    ElemType* data[MaxSize];  // 存储元素的数组
    int size;  // 队列当前元素个数
    int front, rear;  // 队列头尾指针
} SqQueue;
 
void InitQueue(SqQueue* Queue) {
    Queue->rear = Queue->front = 0;  // 初始化队列头尾指针
    Queue->size = 0;  // 初始化队列元素个数为 0
}
 
bool isEmpty(SqQueue Queue) {
    if (Queue.rear == Queue.front)  // 判断队列是否为空
        return true;
    return false;
}
 
bool GetTopQueue(SqQueue Queue, ElemType** x) {
    if ((Queue.size == 0) || (Queue.rear == Queue.front))  // 判断队列是否为空或已满
    return false;
    
    *x = Queue.data[Queue.front];  // 获取队首元素
    return true;
}
 
bool EnQueue(SqQueue* Queue, ElemType* x) {
    if ((Queue->rear + 1) % MaxSize == Queue->front)  // 判断队列是否已满
        return false;
    
    Queue->data[Queue->rear] = x;  // 入队
    Queue->rear = (Queue->rear + 1) % MaxSize;  // 更新 rear 指针
    Queue->size++;  // 更新队列元素个数
    return true;
}
 
bool DeQueue(SqQueue* Queue, ElemType** data) {
    if ((Queue->size == 0) || (Queue->rear == Queue->front))  // 判断队列是否为空
        return false;
    
    *data = (*Queue).data[Queue->front];  // 出队
    Queue->front = (Queue->front + 1) % MaxSize;  // 更新 front 指针
    Queue->size--;  // 更新队列元素个数
    return true;
}

/*功能:用于交换两个指针指向的节点
*参数:left: 指向左节点的指针 right: 指向右节点的指针
*/
void swap(struct TreeNode** left, struct TreeNode** right){
    struct TreeNode* temp; // 定义一个临时变量用于交换节点
    temp = *left; // 将左节点指向的节点赋值给临时变量
    *left = *right; // 将右节点指向的节点赋值给左节点指向的节点
    *right = temp; // 将临时变量赋值给右节点指向的节点
}

// 定义一个函数用于翻转二叉树
struct TreeNode* invertTree(struct TreeNode* root) {
    if(root == NULL) // 如果根节点为空,则返回空
        return NULL;
    //定义一个队列
    SqQueue Queue;
    InitQueue(&Queue);//初始化队列
    EnQueue(&Queue,root);//传入根节点
    //队列不为空,则继续循环
    while(!isEmpty(Queue)){
        int QueueLen = Queue.size;//取每一层的元素个数
        for(int i=0;i<QueueLen;i++){
            struct TreeNode* Node;//暂存要访问的结点
            DeQueue(&Queue,&Node);//取出元要访问的结点
            swap(&(Node->left),&(Node->right));//交换其左右孩子
            if(Node->left != NULL)//若其左孩子不为空则传入队列
                EnQueue(&Queue,Node->left);
            if(Node->right != NULL)//若其右孩子不为空则传入队列
                EnQueue(&Queue,Node->right);
        }
    }
    return root;
}

101. 对称二叉树 (优先掌握递归)  

题目链接/文章讲解/视频讲解:代码随想录

1.分析及思路

判断一个结点是不是对称的,是从下向上判断的,因为只有下面的结点符合对称,这个结点才能是对称的。即使用后续遍历,结点有对称,就返回它的父节点。

怎么样算不符合那:

1.左右结点不同时为空

2.左右结点不为空但值不相等

3.左孩子的右孩子与右孩子的左孩子不相等

4.左孩子的左孩子与右孩子的右孩子不相等

怎么样算符合那:

1.左右孩子全为空

2.左孩子的左孩子与右孩子的右孩子相等

3.左孩子的右孩子与右孩子的左孩子相等。

递归法:

1.确定返回值及参数

返回值,bool即结点是否是对称的。参数判断时,判断的是其左右孩子,所以传入其左右孩子

2.终止条件

1.左右结点不同时为空false

2.左右结点不为空但值不相等false

3.左右结点都为空返回true

3.确定单层递归的逻辑

此时左右结点相等,就开始判断左结点的左孩子与右节点的右孩子,是否相等

左节点的右孩子与右结点的左孩子是否相等,相等返回true,否则返回false

2.代码及注释

//比较两个树的对称性,参数为左子树和右子树的根节点
bool compare(struct TreeNode* left,struct TreeNode*right){
    //如果左子树为空且右子树不为空,则返回false
    if(left == NULL && right != NULL) return false;
    //如果左子树不为空且右子树为空,则返回false
    else if(left != NULL && right == NULL) return false;
    //如果左子树和右子树都为空,则返回true
    else if(left == NULL && right == NULL) return true;
    //如果左子树和右子树的值不相等,则返回false
    else if(left->val != right->val) return false;

    //递归比较左子树的左节点和右子树的右节点,以及左子树的右节点和右子树的左节点
    bool outside = compare(left->left,right->right);
    bool inside = compare(left->right,right->left);
    //返回两个比较结果的逻辑与
    return (outside && inside);
}

//判断给定的二叉树是否对称,参数为根节点
bool isSymmetric(struct TreeNode* root) {
    //如果根节点为空,则返回false
    if(root == NULL)
        return false;
    //调用compare函数比较根节点的左子树和右子树
    return compare(root->left,root->right);
}

  • 10
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值