题目1:102层序遍历
题目链接:层序遍历
对题目的理解
从左到右一层一层的去遍历二叉树,按照每一层进行返回
需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。
这种层序遍历方式就是图论中的广度优先遍历,只不过应用在二叉树上。
这个遍历过程需要变量size记录每一层的节点数量,遍历到每一层开始的时候,size就改变一下
当每一层的各个节点的左右孩子都加入到队列后,才会重新取size,做到对每一层的节点总数统计。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
vector<vector<int>> result;//result二维数组放入整个二叉树的结果
//判断根节点不为空,先将根节点放入队列
if(root!=NULL) que.push(root);
while(!que.empty()){
//记录二叉树每一层的节点数量
int size = que.size();//while循环外面的size变量初始保存每层节点数量,que.size()会变,不能后面while循环直接使用que.size--,
vector<int> vec;//一维数组放入每一层的结果 一定要放在循环里面,每一层都重新定义
while(size--){
TreeNode* node = que.front();
que.pop();
vec.push_back(node->val);
if(node->left!=NULL) que.push(node->left);
if(node->right!=NULL) que.push(node->right);
}
result.push_back(vec);
}
return result;
}
};
题目2:226 翻转二叉树
题目链接:翻转二叉树
对题目的理解
将二叉树的各个左右节点进行翻转交换位置,最终返回根节点root。
交换的是指针,不是数值,其实就把每一个节点的左右孩子交换一下就可以了。
关键在于遍历顺序,前中后序应该选哪一种遍历顺序?
注意只要把每一个节点的左右孩子翻转一下,就可以达到整体翻转的效果
这道题目使用前序遍历和后序遍历都可以,唯独中序遍历不方便,因为中序遍历会把某些节点的左右孩子翻转了两次
那么层序遍历依然可以的!只要把每一个节点的左右孩子翻转一下的遍历方式都是可以的!
递归法
下图是前序遍历的示意图
递归三部曲:
i) 确定递归函数的参数和返回值
参数就是要传入节点的指针,题目中给出的要返回root节点的指针,可以直接使用题目定义好的函数,所以就函数的返回类型为TreeNode*
。
ii) 确定终止条件
当前节点为空的时候,就返回
iii) 确定单层递归的逻辑
前序遍历先交换左右孩子节点,然后反转左子树,反转右子树。
如下给出了递归遍历的前序遍历,后序遍历和中序遍历。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root==NULL) return root;
//前序遍历,中左右
// swap(root->left,root->right);
// invertTree(root->left);
// invertTree(root->right);
// return root;
//后序遍历,左右中
// invertTree(root->left);
// invertTree(root->right);
// swap(root->left,root->right);
// return root;
//中序遍历,左中右,需要注意,左右子树
invertTree(root->left);
swap(root->left,root->right);
invertTree(root->left);//这里还是left,因为是未处理过的先前的右子树
return root;
}
};
迭代法(前序遍历+堆栈)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
stack<TreeNode*> st;
if(root==NULL) return root;
st.push(root);
while(!st.empty()){
TreeNode* node = st.top();
st.pop();
swap(node->left, node->right);
if(node->right) st.push(node->right);
if(node->left) st.push(node->left);
}
return root;
}
};
广度优先遍历(层序遍历)队列
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
queue<TreeNode*> que;
if(root!=NULL) que.push(root);
while(!que.empty()){
int size = que.size();
while(size--){
TreeNode* node = que.front();
que.pop();
swap(node->left, node->right);
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
}
return root;
}
};
题目3:101对称二叉树
题目链接:对称二叉树
对题目的理解
判断二叉树是否轴对称
首先想清楚,判断对称二叉树要比较的是哪两个节点,要比较的不是左右节点!
就是判断根节点的左子树和右子树是否可以相互翻转,就是将二叉树的内侧节点和外侧节点分别进行比较,左子树的外侧节点是否与右子树的外侧节点相等,左子树的内侧节点是否与右子树的内侧节点相等。
要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。
这个二叉树如何遍历是一个难点,即确定遍历的顺序。
递归法
使用递归法进行遍历的话,一共有3种遍历方式,前序遍历(中左右),中序遍历(左中右)和后序遍历(左右中)
本题只能使用后序遍历,因为需要收集节点的左右孩子的信息返回给上一层的中节点,这样才能进行比较,这样才能得到一个小子树整体比较的结果,返回给更上一层的节点。通过递归函数的返回值来判断两个子树的内侧节点和外侧节点是否相等。
若使用前序遍历,则根本没有得到节点的左右孩子信息就比较,无法比较
若使用中序遍历,则只收集了节点的左孩子的信息,没有右孩子的信息,也是无法比较的。
列出本题相关的几种情况
递归三部曲
i)确定递归函数的参数和返回值
因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数是左子树节点和右子树节点,返回值自然是bool类型。
ii)确定终止条件
比较两个节点数值相不相同,首先要把两个节点的情况弄清楚,如下
首先要把两个节点为空的情况弄清楚,否则后面比较数值的时候就会操作空指针了。
节点为空:(注意比较的不是左孩子和右孩子,称之为左节点右节点)
- 左节点为空,右节点不为空,不对称,return false
- 左不为空,右为空,不对称 return false
- 左右都为空,对称,返回true
左右节点不为空:
- 左右都不为空,比较节点数值,不相同就return false
左右节点都不为空,且数值相同:
iii)单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。
- 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
- 比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
- 如果左右都对称就返回true ,有一侧不对称就返回false 。
从代码中可以看出使用的遍历方式,左子树左右中,右子树右左中,所以我把这个遍历顺序也称之为“后序遍历”(尽管不是严格的后序遍历)。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool compare(TreeNode* left, TreeNode* right){
if(left!=NULL && right==NULL) return false;
else if(left==NULL && right!=NULL) return false;
else if(left==NULL && right==NULL) return true;
else if(left->val != right->val) return false;
bool outside = compare(left->left,right->right);//外侧节点比较 左 右
bool inside = compare(left->right,right->left);//内侧节点比较 右 左
bool result = outside && inside; //中 中
return result;
}
bool isSymmetric(TreeNode* root) {
if(root==NULL) return true;
return compare(root->left,root->right);
}
};
迭代法
注意,这里的迭代法可不是前中后序的迭代写法,因为本题的本质是判断两个树是否是相互翻转的,其实已经不是所谓二叉树遍历的前中后序的关系了。
使用队列来比较两个树(根节点的左右子树)是否相互翻转,(注意不是层序遍历)
使用队列的方式书写代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if(root==NULL) return true;
queue<TreeNode*> que;
que.push(root->left);//左子树的头节点
que.push(root->right);//右子树的头节点
while(!que.empty()){//判断两个树是否相等
TreeNode* leftNode = que.front();
que.pop();
TreeNode* rightNode = que.front();
que.pop();
if(leftNode==NULL && rightNode==NULL){
continue;
}
// if((leftNode==NULL || rightNode==NULL || (leftNode->val!=rightNode->val)))
// return false;
if(leftNode==NULL && rightNode!=NULL) return false;
else if(leftNode!=NULL && rightNode==NULL) return false;
else if(leftNode->val!=rightNode->val) return false;
que.push(leftNode->left);//左节点的左孩子
que.push(rightNode->right);//右节点的右孩子
que.push(leftNode->right);//左节点的右孩子
que.push(rightNode->left);//右节点的左孩子
}
return true;
}
};
这个迭代法,其实是把左右两个子树要比较的元素顺序放进一个容器,然后成对成对的取出来进行比较,那么其实使用栈也是可以的,只要把队列原封不动的改成栈就可以了
使用栈的方式书写代码(将上面队列换成栈即可,因为是成对进行比较),即使栈是先进后出的数据结构,成对比较的时候也与队列相同。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if(root==NULL) return true;
stack<TreeNode*> st;
st.push(root->left);//左子树的头节点
st.push(root->right);//右子树的头节点
while(!st.empty()){//判断两个树是否相等
TreeNode* leftNode = st.top();
st.pop();
TreeNode* rightNode = st.top();
st.pop();
if(leftNode==NULL && rightNode==NULL){
continue;
}
// if((leftNode==NULL || rightNode==NULL || (leftNode->val!=rightNode->val)))
// return false;
if(leftNode==NULL && rightNode!=NULL) return false;
else if(leftNode!=NULL && rightNode==NULL) return false;
else if(leftNode->val!=rightNode->val) return false;
st.push(leftNode->left);//左节点的左孩子
st.push(rightNode->right);//右节点的右孩子
st.push(leftNode->right);//左节点的右孩子
st.push(rightNode->left);//右节点的左孩子
}
return true;
}
};