bintree_1(改天复习再来慢慢移动。。。。)
(1)点结构
struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode(int x): val(x), left(NULL), right(NULL) { } }
(2)深度优先遍历有前中后,都可以使用递归或迭代。可以借助栈实现非递归的方式。广度优先遍历是使用队列实现的。
(3)确定递归函数的返回数和函数的形参,有些像列表那种就不用返回,直接在形参里面搞个引用就行;确定递归的终止条件;最后确定单层递归的逻辑。
(4)前序遍历
class Solution { public: void traverse_qian(TreeNode* cur, vector<int> &vec) { if(cur == null) return; vec. push_back(cur->val); traverse_qian(cur->left, vec); traverse_qian(cur->right, vec); } vector<int> process(TreeNode* root) { vector<int> veca; traverse_qian(root, veca); return veca; } }
中序和后序就是把顺序调换一下。
(5)前序迭代栈实现。中左右
首先把中节点进站,出站,然后右节点进站,然后左节点进站,这时候左节点就变成了中节点,中节点出站,中节点的右节点进站,左节点进站。这时候左节点又变成了中节点。
class Solution { public: vector<int> traverse_qian(TreeNode* root) { stack<TreeNode*> st; vector<int> result; st. push(root); while(!st.empty()) { TreeNode* node = st. pop(); //中节点出站要先把它记到数组里面 //之后就是中节点的左右 if (node != null) result. push_back(node->data); else continue; //如果说这个已经是空的了就说明早退出这个循环了 st. push(node->right); st. push(node->left); //之后就把这个左边的数值当作中节点 } return result; } }
重点来了,后序遍历就是直接把“先进right再进left”的这个操作改成“先进left再进right”的操作。
(6)中序遍历迭代比较复杂,明天再看。
(7)层序遍历一个二叉树,就是从左到右一层一层遍历二叉树。需要借助队列来实现。队列先进先出,符合一层一层遍历的逻辑。而栈先进后出,符合深度优先遍历也就是递归的逻辑。过程:第一层的第一个先进来,然后第一个出去,它的左右子树进来。左子树出去,它的左右子树进来,然后一直都是队列的头部出去,它的左右子树进来。
class Solution { public: vector<vector<int>> level order(TreeNode *root) { que<TreeNode*> que; if(root != null) que. push(root); vector<vector<int>> result; while(!que.empty()) { int size = que. size(); vector<int> vec; //这里一定要使用固定大小size不要用que. size,因为后者是一直不断变化的 for(int i=0; i < size; i++) { TreeNode* node=que. front(); que. pop(); vec. push_back(node->val); if(node->left) que. push(node->left); if(node->right) que. push(node->right); } result. push_back(vec); } return result; } }
(8)返回二叉树的右视图,就相当于自己站在二叉树的最右侧,返回每一层最右边的元素。在层序遍历的时候,判断是否遍历到单层的最后面的元素,如果是的话就放到数组里面。
class Solution { public: vector<int> rightSideView(TreeNode *root) { vector<int> result; queue<TreeNode *> que; if(root != null) que. push(root); while(!que.empty()) { int size = que. size(); for(int i=0; i < size; i++) { TreeNode* node = que. front(); que. pop(); if(i == (size-1)) result. push_back(node->val); if(node->left) que. push(node->left); if(node->right) que. push(node->right); } } return result; } }
(9)给定一个二叉树,返回由每一层的数值的平均值组成的数组。
class Solution { public: vector<float> averageoflevel(TreeNode* root) { queue<TreeNode *> que; vector<float> result; if(root != null) que. push(root); while(!que.empty()) { int size = que. size(); int sum = 0; for(int i=0; i < size; i++) { TreeNode* node=que. front(); que. pop(); sum += node. val; if(node->left) que. push(node->left); if(node->right) que. push(node->right); } result. push_back(sum / size); } return result; } }
(10)给定一棵n叉树,返回节点层序遍历
class Solution { public: vector<vector<int>> nlevelOrder(TreeNode* root) { queue<TreeNode *> que; vector<vector<int>> result; if(root != null) que. push(root); while(!que.empty()) { int size = que. size(); vector<int> vec; for(int i=0; i < size; i++) { TreeNode* node = que. front(); que.pop(); vec. push_back(node->val); for(int j=0; j < node->children.size(); j++) { if (node->children[j]) que. push(node->children[j]); } } result. push_back(vec); } return result; } }
(11)翻转二叉树,相当中序遍历倒过来。
想要翻转,其实就是把每一个节点的左右孩子调换过来,遍历顺序可以选取先序遍历,后序遍历,层次遍历。中序遍历不行,因为中序遍历会把某些节点左右孩子翻转过来。
TreeNode* invertTree(TreeNode* root)
{
if(root == null) return root;
swap(root->left, root->right);
invertTree(root->left);
invertTree(root->right);
return root;
}
采用栈的迭代写法如下。
TreeNode* invertTree(TreeNode* root)
{
if(root == null) return root;
stack<TreeNode*> st;
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;
}
采用队列实现层次遍历进行翻转的方式如下
TreeNode* invertTree(TreeNode* root)
{
queue<TreeNode*> que;
if(root != null) que. push(root);
while(!que.empty())
{
int size = que. size();
for(int i=0; i < size; i++)
{
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;
}
(12)判断一棵树是不是对称的。
第一种是采用递归方法的后序遍历,左边利用左右中,右边利用右左中。递归三部曲,首先确定函数的形参和返回值,然后确定递归的终止条件,然后确定单层递归的逻辑。
bool compare(TreeNode* left, TreeNode* right)
{
if(left==null && right==null) return true;
elif(left==null && right!=null) return false;
elif(left!=null && right==null) return false;
elif(left->value != right->value) return false;
bool outside = compare(left->left, right->right);
bool inside = compare(left->right, right->left);
bool issame = outside && inside;
return issame;
}
bool isSymmetric(TreeNode* node)
{
if(root==null) return true;
return compare(root->left, root->right);
}
第二种是采用迭代法,这里的迭代法可不是采用前中序的迭代写法,可以使用队列来比较两棵树(根节点的左右子树是否翻转),但一定要注意这个不是在做层序遍历。
下面就是通过队列来判断根节点的左子树和右子树的内侧和外侧是否相等。
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 && !rightNode)
{
continue;
}
//左右一个节点不为空,或者两个节点都不为空但是数值不一样,返回false
if(!leftNode || !rightNode || (leftNode->value != rightNode->value))
return false;
que. push(leftNode->left);
que. push(rightNode->right);
que. push(leftNode->right);
que. push(rightNode->left);
}
return true;
}
第三种是使用栈,可以发现,上面使用队列迭代法就是把左右两个子树要比较的内容放到一个容器里面,然后再成对取出来,那么其实使用栈也是可以的。
bool isSymmetric(TreeNode* root)
{
if(root == null) return true;
stack<TreeNode*> sta;
sta. push(root->left);
sta. push(root->right);
while(!sta.empty()
{
TreeNode* leftNode = sta. top();
sta. pop();
TreeNode* rightNode = sta.top();
sta. pop();
if(!leftNode && !rightNode) continue;
if(!leftNode || !rightNode || (leftNode->value != rightNode->value))
{
return false;
}
sta. push(leftNode->left);
sta. push(rightNode->right);
sta. push(leftNode->right);
sta. push(rightNode->left);
}
return true;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下