代码随想录算法训练营第十三天|今天量大管饱144、145、94、102、107、199、637、429、515、116、117、104、111
1.代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素2.代码随想录算法训练营第二天| 977. 有序数组的平方、209. 长度最小的子数组、59. 螺旋矩阵 II3.代码随想录算法训练营第四天| 203. 移除链表元素、707.设计链表、206.反转链表4.代码随想录算法训练营第五天| 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交(同160题)、142.环形链表Ⅱ5.代码随想录算法训练营第六天| 242. 有效的字母异位词、349.两个数组的交集、202.快乐数、1.两数之和6.代码随想录算法训练营第七天| 454. 两数相加Ⅱ、383.赎金信、15.三数之和、18.四数之和7.代码随想录算法训练营第八天|344.反转字符串、541.反转字符串Ⅱ、54.替换数字(卡码网是真滴不好用)8.代码随想录算法训练营第九天|151.反转字符串中的单词、55.右旋字符串、28.找出字符串中第一个匹配项的下标、459.重复的子字符串9.代码随想录算法训练营第十一天|232.用栈实现队列、225.用队列实现栈、20.有效的括号、1047.删除字符串中的所有相邻重复项10.代码随想录算法训练营第十二天|150.逆波兰表达式求值、239.滑动窗口最大值、347.前k个高频元素
11.代码随想录算法训练营第十三天|今天量大管饱144、145、94、102、107、199、637、429、515、116、117、104、111
12.代码随想录算法训练营第十四天| 226.翻转二叉树 、101. 对称二叉树、104.二叉树的最大深度 (优先掌握递归)、111.二叉树的最小深度13.代码随想录算法训练营第十五天|110.平衡二叉树、257.二叉树的所有路径、404.左叶子之和、222.完全二叉树的节点个数今天来处理二叉树part1、2、3,顶级享受,一次到位。
完全二叉树和满二叉树概念没问题。
二叉搜索树,左子树所有结点的值小于它的根结点的值,右子树上所有结点的值大于它的根结点的值
平衡二叉搜索树,它是一棵空树或它的左右两个子树的高度差的绝对值不超过1。
二叉树的存储方式:链式存储、顺序存储。
链式存储 左指针 右指针
顺序存储 如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。
二叉树的遍历方式
深度优先遍历,先往深走,遇到叶子就回来。
广度优先遍历 一层层去遍历
深度优先遍历
- 前序遍历 中左右
- 中序遍历 左中右
- 后序遍历 左右中
广度优先遍历
- 层次遍历
C++中 二叉树的节点定义
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x),left(NULL),right(NULL){}
//构造函数 初始化的时候用
};
递归算法三个要素
- 确定递归函数的参数和返回值
- 确定终止条件
- 确定单层递归的逻辑
//前中后序递归遍历
class Solution {
public:
void preorder(TreeNode *root, vector<int> &res) {
if (root == nullptr) {
return;
}
res.push_back(root->val);
preorder(root->left, res);
preorder(root->right, res);
}
vector<int> preorderTraversal(TreeNode *root) {
vector<int> res;
preorder(root, res);
return res;
}
};
class Solution {
public:
void postorder(TreeNode *root, vector<int> &res) {
if (root == nullptr) {
return;
}
postorder(root->left, res);
postorder(root->right, res);
res.push_back(root->val);
}
vector<int> postorderTraversal(TreeNode *root) {
vector<int> res;
postorder(root, res);
return res;
}
};
class Solution {
public:
void inorder(TreeNode *root, vector<int> &res) {
if (root == nullptr) {
return;
}
inorder(root->left, res);
res.push_back(root->val);
inorder(root->right, res);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
inorder(root, res);
return res;
}
};
//前中后序非递归遍历
//前序
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> s;
TreeNode* tmp;
s.push(root);
vector<int> ans;
if (root == NULL) return ans;
while (!s.empty()) {
tmp = s.top();
s.pop();
ans.push_back(tmp->val);
if(tmp->right) s.push(tmp->right);
if(tmp->left) s.push(tmp->left);
}
return ans;
}
};
//后序
//先序遍历是中左右,后序遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了
//挺新奇的构思
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
if (root == NULL) return result;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
result.push_back(node->val);
if (node->left) st.push(node->left); // 相对于前序遍历,这更改一下入栈顺序 (空节点不入栈)
if (node->right) st.push(node->right); // 空节点不入栈
}
reverse(result.begin(), result.end()); // 将结果反转之后就是左右中的顺序了
return result;
}
};
//中序我想想
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
TreeNode* cur = root;
while (cur != NULL || !st.empty()) {
if (cur != NULL) { // 指针来访问节点,访问到最底层
st.push(cur); // 将访问的节点放进栈
cur = cur->left; // 左
} else {
cur = st.top(); // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)
st.pop();
result.push_back(cur->val); // 中
cur = cur->right; // 右
}
}
return result;
}
};
二叉树的层次遍历
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
queue<TreeNode*> que;
if (root == NULL) return res;
que.push(root);
while (!que.empty()) {
int size = que.size();
vector<int> level;
for (int i = 0; i < size; i++) {
TreeNode* cur = que.front();
que.pop();
level.push_back(cur -> val);
if (cur -> left == NULL && cur -> right == NULL) continue;
if (cur -> left) {
que.push(cur -> left);
}
if (cur -> right) {
que.push(cur -> right);
}
}
res.push_back(level);
}
return res;
}
};
//不知道啥时候写的代码 现在 我要换一种思路了
//20240705开写代码
//想了半天还是回去看代码了 哈哈哈 笑死 确实得这么写
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<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(),因为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;
}
};
//层序遍历相关题目
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
vector<vector<int>> res;
queue<TreeNode*> que;
if (root == NULL) return res;
que.push(root);
while (!que.empty()) {
int size = que.size();
vector<int> level;
for (int i = 0; i < size; i++) {
TreeNode* cur = que.front();
que.pop();
level.push_back(cur->val);
if (cur->left) {
que.push(cur->left);
}
if (cur->right) {
que.push(cur->right);
}
}
res.push_back(level);
}
reverse(res.begin(), res.end());
return res;
}
};
class Solution {
public:
vector<int> rightSideView(TreeNode* root) {
vector<int> ans;
vector<vector<int>> res;
queue<TreeNode*> que;
if (root == NULL) return ans;
que.push(root);
while (!que.empty()) {
int size = que.size();
vector<int> level;
for (int i = 0; i < size; i++) {
TreeNode* cur = que.front();
que.pop();
level.push_back(cur -> val);
if (cur -> left == NULL && cur -> right == NULL) continue;
if (cur -> left) {
que.push(cur -> left);
}
if (cur -> right) {
que.push(cur -> right);
}
}
ans.push_back(level.back());
res.push_back(level);
}
return ans;
}
};
class Solution {
public:
vector<double> averageOfLevels(TreeNode* root) {
queue<TreeNode*> que;
vector<double> ans;
double ave;
double sum=0;
if (root != NULL) que.push(root);
while (!que.empty()) {
int size = que.size();
vector<int> vec;
// 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
sum += node->val;
que.pop();
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
ave = sum / size;
sum = 0;
ans.push_back(ave);
}
return ans;
}
};
class Solution {
public:
vector<vector<int>> levelOrder(Node* root) {
queue<Node*> que;
if (root != NULL) que.push(root);
vector<vector<int>> result;
while (!que.empty()) {
int size = que.size();
vector<int> vec;
for (int i = 0; i < size; i++) {
Node* node = que.front();
que.pop();
vec.push_back(node->val);
for (Node* child : node->children) {
//这个循环写的 好用 爱用
que.push(child);
}
}
result.push_back(vec);
}
return result;
}
};
class Solution {
public:
void dfs(vector<int>& res, TreeNode* root, int curHeight) {
if (curHeight == res.size()) {
res.push_back(root->val);
} else {
res[curHeight] = max(res[curHeight], root->val);
}
if (root->left) {
dfs(res, root->left, curHeight + 1);
}
if (root->right) {
dfs(res, root->right, curHeight + 1);
}
}
vector<int> largestValues(TreeNode* root) {
if (!root) {
return {};
}
vector<int> res;
dfs(res, root, 0);
return res;
}
};
//层次遍历然后记录一下最大值即可
class Solution {
public:
Node* connect(Node* root) {
if (root == nullptr) {
return root;
}
// 初始化队列同时将第一层节点加入队列中,即根节点
queue<Node*> Q;
Q.push(root);
// 外层的 while 循环迭代的是层数
while (!Q.empty()) {
// 记录当前队列大小
int size = Q.size();
// 遍历这一层的所有节点
for(int i = 0; i < size; i++) {
// 从队首取出元素
Node* node = Q.front();
Q.pop();
// 连接
if (i < size - 1) {
node->next = Q.front();
}
// 拓展下一层节点
if (node->left != nullptr) {
Q.push(node->left);
}
if (node->right != nullptr) {
Q.push(node->right);
}
}
}
// 返回根节点
return root;
}
};
//还是层次遍历 加一步操作
class Solution {
public:
Node* connect(Node* root) {
if (!root) {
return nullptr;
}
queue<Node*> q;
q.push(root);
while (!q.empty()) {
int n = q.size();
Node *last = nullptr;
for (int i = 1; i <= n; ++i) {
Node *f = q.front();
q.pop();
if (f->left) {
q.push(f->left);
}
if (f->right) {
q.push(f->right);
}
if (i != 1) {
last->next = f;
}
last = f;
}
}
return root;
}
};
class Solution {
public:
int maxDepth(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);
vector<vector<int>> result;
int depth = 0;
while (!que.empty()) {
int size = que.size();
vector<int> vec;
// 这里一定要使用固定大小size,不要使用que.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);
depth++;
}
return depth;
}
};
class Solution {
public:
int minDepth(TreeNode* root) {
if (!root) {
return 0;
}
queue<TreeNode*> que;
que.push(root);
int depth = 0;
while (!que.empty()) {
int size = que.size();
vector<int> vec;
// 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
vec.push_back(node->val);
if (!node->left && !node->right) {
return ++depth;
}
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
depth++;
}
return depth;
}
};
自在飞花轻似梦
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)