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);
}