层序遍历
文档讲解:代码随想录
视频讲解:
状态
层序遍历的原理就是利用队列来模拟节点的入和出的问题。从头节点入开始,之后每当一个节点出队列,那么就从队尾加入其左子结点和右子节点。
/**
* 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*> treeque;
//压入头节点
if(root != nullptr) treeque.push(root);
//返回数组
vector<vector<int>> res;
while(!treeque.empty())
{
//记录当前层的节点树
int temp = treeque.size();
//存储当前层的节点数组
vector<int> vec;
for(int i =0;i<temp;i++)
{
TreeNode* cur = treeque.front();
vec.push_back(cur->val);
treeque.pop();
//压入当前节点的左节点和右节点
if(cur->left) treeque.push(cur->left);
if(cur->right) treeque.push(cur->right);
}
//将当前层的结果压入到返回数组中
res.push_back(vec);
}
return res;
}
};
- 递归法
- 递归参数和返回值:当前节点,返回结果的数组以及当前的层数
- 终止条件:当前节点为空节点时,结束
- 递归逻辑:
//初始化,如果当前数组大小和层数相等,则先加入一个数组,该数组用来存放当前层的节点
if(res.size() == loc) res.push_back(vector<int>());
//在数组中的当前层加入当前节点
res[loc].push_back(cur->val);
//递归处理左子节点和右子节点
function1(cur->left,res,loc+1);
function1(cur->right,res,loc+1);
具体函数
void ResTree(TreeNode* cur,vector<vector<int>>& res,int dep)
{
//递归终止条件
if(cur == nullptr) return ;
//初始化当前层的存储数组
if(res.size()==dep) res.push_back(vector<int>());
//存入当前值
res[dep].push_back(cur->val);
//递归为其左节点和右节点
ResTree(cur->left,res,dep+1);
ResTree(cur->right,res,dep+1);
}
vector<vector<int>> levelOrder(TreeNode* root) {
int dep = 0;
vector<vector<int>> res;
ResTree(root,res,dep);
return res;
}
翻转二叉树
文档讲解:代码随想录
视频讲解:
状态:层序遍历
当队列头元素存在左节点或者右节点是,交换。然后再压入。当然也可以先压入再交换。交换相当于是对中节点的操作。
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
queue<TreeNode*> treeque;
if(root) treeque.push(root);
while(!treeque.empty())
{
int tempsize = treeque.size();
for(int i=0;i<tempsize;i++)
{
TreeNode* cur = treeque.front();
treeque.pop();
if(cur->left||cur->right)
{
swap(cur->left,cur->right);
}
if(cur->left) treeque.push(cur->left);
if(cur->right) treeque.push(cur->right);
}
}
return root;
}
};
- 递归
- 函数参数,树的根节点
- 终止条件:翻转节点为空
- 单层逻辑:翻转该节点的左和右,然后调用该函数继续对其现在的左节点和右节点进行翻转。
swap(...);
func1(left);
func1(right);
具体程序
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root==nullptr) return root;
invertTree(root->left); //左
invertTree(root->right); //右 -- 上两步可以看作是遍历获取
swap(root->left,root->right); //中
return root;
}
};
交换要么在两节点递归之前,要么在两节点递归之后。否则可能会出现一方没有交换的情况。交换的操作不应该在遍历操作之间,两者是分开进行的。
3. 迭代
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
stack<TreeNode*> treestk;
if(root) treestk.push(root);
while(!treestk.empty())
{
//记录中节点
TreeNode* cur = treestk.top();
//弹出中节点
treestk.pop();
//遍历
//压入右节点
if(cur->right) treestk.push(cur->right);
//压入左节点
if(cur->left) treestk.push(cur->left);
//交换操作
swap(cur->left,cur->right);
}
return root;
}
};
对称二叉树
文档讲解:代码随想录
视频讲解:
状态:×
比较的其实是两棵子树是否是翻转的,
对于左子树可以采用中左右的方式存入到队列中
对于右子树可以采用中右左的方式存入到队列中。
如果队列的每个元素相等,说明true。
如果采用递归法,应当使用后序遍历,即从子结点开始判断是否相等。我们需要确定
- 参数和返回值:参数就是左子树和右子树对应的节点,返回值就是true或者false
- 终止条件:就是返回bool的情况
//对应左节点和右节点为空 true
if (left == nullptr && right == nullptr) return true;
//对应左节点和右节点有一个不为空 false
else if (left != nullptr && right == nullptr) return false;
else if (left == nullptr && right != nullptr) return false;
//对应左节点和右节点都不为空,但值不等 false
else if (left->val != right->val) return false;
- 单层逻辑
就是处理当前对应节点相同的情况,当前节点就是中节点,中节点的值相同了,还需要考虑以其为父节点的子树对应值是否相同(递归的调用),最后结合在一起才是最终的中节点。
bool out = compare(left->left,right->right);
bool inside = compare(left->right,right->left);
bool mid = true && out && inside; //true代表中节点为true
return mid;
迭代法:注意加入元素的顺序,因为每次比较是取容器前两个元素比较,所以加入的顺序应当是相互对应的,比如
- 加入左子树左节点,那么下一个就加入右子树右节点,然后才是左子树右节点,右子树左节点
本文详细介绍了如何使用层序遍历算法遍历二叉树,以及如何通过迭代和递归方法实现二叉树的翻转,并探讨了对称二叉树的判断方法,涉及队列、栈和递归/迭代的运用。
402





