今天按照教程在mint上装Anaconda + Pytorch环境,结果包越装越多,home本身只剩40G左右,装完就剩7G了。当初装双系统时候home分区放在磁盘尾部,不能直接扩容。重装系统太麻烦,就试试拿DiskGenius做无损分区,结果报错。最后是用U盘刻录了一个GParted的LiveCD,启动进去把空闲空间移动到磁盘尾部,再合并给home。
忐忑不安地开机,发现grub没丢,正常进系统了,看着home余量90G,开心。
1. 不同的二叉搜索树 II
难度
- Easy
- Medium
- Hard
完成情况
- 独立完成
- 参考思路
- 参考答案
思路
由1-n,要返回一个可能的二叉搜索树,可以分解为,确定一个根节点i后,递归左右范围生成可能的左右子树,并从可能的左右子树中选取孩子拼接上来,便形成了一个可能的树,最后得到答案。
代码
class Solution {
private:
vector<TreeNode*> construct(int start, int end) {
if (start > end) return {nullptr};
vector<TreeNode*> allTrees;
for (int i = start; i <= end; ++i) {
vector<TreeNode*> leftChild = construct(start, i - 1);
vector<TreeNode*> rightChild = construct(i + 1, end);
for (auto& left : leftChild) {
for (auto& right : rightChild) {
TreeNode* root = new TreeNode(i);
root->left = left;
root->right = right;
allTrees.push_back(root);
}
}
}
return allTrees;
}
public:
vector<TreeNode*> generateTrees(int n) {
return construct(1, n);
}
};
2. 恢复二叉搜索树
难度
- Easy
- Medium
- Hard
完成情况
- 独立完成
- 参考思路
- 参考答案
思路
我只考虑到两个错误节点相聚较远时的情形,而当两个错误节点相邻时,只会有一个地方满足前大于后,这种情况要交换第一个错误节点和他相邻下一个结点的值。
代码
class Solution {
private:
TreeNode* pre;
TreeNode* pro1;
TreeNode* pro2;
TreeNode* pro12;
void inorder(TreeNode* root) {
if (!root) return;
inorder(root->left);
if (pre && pre->val > root->val) {
if (!pro1) {
pro1 = pre;
pro12 = root;
}
else if (!pro2) pro2 = root;
// pre->val ^= root->val;
// root->val ^= pre->val;
// pre->val ^= root->val;
}
pre=root;
inorder(root->right);
}
public:
void recoverTree(TreeNode* root) {
pre = nullptr;
pro1 = nullptr;
pro2 = nullptr;
inorder(root);
if (pro1 && pro2) {
pro1->val ^= pro2->val;
pro2->val ^= pro1->val;
pro1->val ^= pro2->val;
} else {
pro1->val ^= pro12->val;
pro12->val ^= pro1->val;
pro1->val ^= pro12->val;
}
return;
}
};
3. 二叉树的锯齿形层序遍历
难度
- Easy
- Medium
- Hard
完成情况
- 独立完成
- 参考思路
- 参考答案
思路
在普通层序遍历的基础上需要实现左右交错的效果,自然想到双端队列。
代码
class Solution {
public:
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
if (!root) return {};
deque<TreeNode*> dq;
vector<vector<int>> ans;
dq.push_back(root);
int seq = 0;
while (!dq.empty()) {
int size = dq.size();
ans.push_back({});
for (int i = 0; i < size; ++i) {
if (seq % 2 == 0) {
TreeNode* cur = dq.front();
dq.pop_front();
ans[ans.size()-1].push_back(cur->val);
if (cur->left) dq.push_back(cur->left);
if (cur->right) dq.push_back(cur->right);
} else {
TreeNode* cur = dq.back();
dq.pop_back();
ans[ans.size()-1].push_back(cur->val);
if (cur->right) dq.push_front(cur->right);
if (cur->left) dq.push_front(cur->left);
}
}
seq++;
}
return ans;
}
};
4. 从前序与中序遍历序列构造二叉树
难度
- Easy
- Medium
- Hard
完成情况
- 独立完成
- 参考思路
- 参考答案
思路
与后序中序建树的思路类似,分割两个数组来递归建立子树。
代码
class Solution {
private:
TreeNode* construct(vector<int>& preorder, vector<int>& inoreder) {
if (inoreder.size() == 0) return nullptr;
TreeNode* root = new TreeNode(preorder[0]);
int i = 0;
for (; i < inoreder.size(); ++i) {
if (inoreder[i] == root->val) break;
}
vector<int> leftIn(inoreder.begin(), inoreder.begin() + i);
vector<int> rightIn(inoreder.begin() + i + 1, inoreder.end());
vector<int> leftPre(preorder.begin() + 1, preorder.begin() + leftIn.size() + 1);
vector<int> rightPre(preorder.begin() + leftIn.size() + 1, preorder.end());
root->left = construct(leftPre, leftIn);
root->right = construct(rightPre, rightIn);
return root;
}
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
return construct(preorder, inorder);
}
};
5. 路径总和 II
难度
- Easy
- Medium
- Hard
完成情况
- 独立完成
- 参考思路
- 参考答案
思路
枚举所有路径,路径中可能有负值,所以当前值大于目标值时,不能直接返回。
代码
class Solution {
private:
vector<vector<int>> ans;
void backtracking(TreeNode* root, int target, vector<int> path, int sum) {
path.push_back(root->val);
sum += root->val;
if (!root->left && !root->right) {
if (sum == target) ans.push_back(path);
return;
}
// if (sum >= target) return;
if (root->left) backtracking(root->left, target, path, sum);
if (root->right) backtracking(root->right, target, path, sum);
}
public:
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
ans.clear();
if (!root) return ans;
backtracking(root, targetSum, vector<int>(), 0);
return ans;
}
};
6. 二叉树展开为链表
难度
- Easy
- Medium
- Hard
完成情况
- 独立完成
- 参考思路
- 参考答案
思路
放到数组中操作。
代码
class Solution {
private:
void trans(TreeNode* root, vector<TreeNode*>& tree) {
if (!root) return;
tree.push_back(root);
trans(root->left, tree);
trans(root->right, tree);
}
public:
void flatten(TreeNode* root) {
if (!root) return;
vector<TreeNode*> tree;
trans(root, tree);
for (int i = 0; i < tree.size() - 1; ++i) {
tree[i]->left = nullptr;
tree[i]->right = tree[i + 1];
}
}
};
7. 填充每个节点的下一个右侧节点指针
难度
- Easy
- Medium
- Hard
完成情况
- 独立完成
- 参考思路
- 参考答案
思路
利用队列层序遍历,只要当前结点不是该层的最后一个,就将其next指向队列头部结点。
代码
class Solution {
public:
Node* connect(Node* root) {
if (!root) return root;
queue<Node*> que;
que.push(root);
while (!que.empty()) {
int size = que.size();
for (int i = 0; i < size; ++i) {
Node* cur = que.front();
que.pop();
if (i < size - 1) cur->next = que.front();
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
}
return root;
}
};
8. 填充每个节点的下一个右侧节点指针 II
难度
- Easy
- Medium
- Hard
完成情况
- 独立完成
- 参考思路
- 参考答案
思路
相比上一题,主要是要求使用常数空间。参考这篇。巧妙之处在于,将每一层作为一个链表,遍历当前层的时候,会自然地串起下一层。 这是基于数据结构特点做的层序遍历。
代码
class Solution {
public:
Node* connect(Node* root) {
if (!root) return root;
Node* cur = root;
while (cur) {
Node* dummy = new Node(0);
Node* pre = dummy;
while (cur) {
if (cur->left) {
pre->next = cur->left;
pre = pre->next;
}
if (cur->right) {
pre->next = cur->right;
pre = pre->next;
}
cur = cur->next;
}
cur = dummy->next;
}
return root;
}
};
9. 二叉树中的最大路径和
难度
- Easy
- Medium
- Hard
完成情况
- 独立完成
- 参考思路
- 参考答案
思路
抽象出一个结点贡献值的概念,表示以其为根节点的路径总和的最大值,可递归计算。则局部路径最大和是结点值+左结点贡献值+右结点贡献值。而当前结点贡献值等于结点值+max(左、右贡献值)。由此可以递归计算。
代码
class Solution {
private:
int ans = INT_MIN;
int maxGain(TreeNode* root) {
if (!root) return 0;
int leftGain = max(maxGain(root->left), 0);
int rightGain = max(maxGain(root->right), 0);
int priceNewPath = root->val + leftGain + rightGain;
ans = max(ans, priceNewPath);
return root->val + max(leftGain, rightGain);
}
public:
int maxPathSum(TreeNode* root) {
ans = INT_MIN;
maxGain(root);
return ans;
}
};
Comments NOTHING