题目链接
代码随想录文章讲解链接
用时:28m10s
遇到数字就入栈,遇到运算符就出栈两个数字,将两个数字的运算结果入栈。
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<long long> s;
long long n1 = 0;
long long n2 = 0;
for (string& token : tokens) {
if (token != "+" && token != "-" && token != "*" && token != "/") s.push(stoll(token));
else {
n2 = s.top();
s.pop();
n1 = s.top();
s.pop();
if (token == "+") s.push(n1 + n2);
else if (token == "-") s.push(n1 - n2);
else if (token == "*") s.push(n1 * n2);
else s.push(n1 / n2);
}
}
return s.top();
}
};
无。
又是数值溢出问题…
题目链接
代码随想录文章讲解链接
用时:13m51s
遍历每个滑动窗口,然后每次都统计一次最大值。暴力解法在力扣上超出时间限制了…
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int size = nums.size();
int left = 0;
int right = k - 1;
vector<int> res(size - k + 1, 0);
while (right < size) {
int maxNum = nums[left];
for (int i = left + 1; i <= right; ++i) {
if (maxNum < nums[i]) maxNum = nums[i];
}
res[left++] = maxNum;
++right;
}
return res;
}
};
用时:32m25s
自定义一个单调队列类,用deque实现,维护deque中的元素,使其从队头到队尾单调递减。成员函数:
用单调队列q来维护滑动窗口。滑动窗口移动过程,当左边界移除窗口的元素与q队头元素一致时,执行pop操作,然后将窗口右边界新增的元素push进q中。由于q是单调队列,所以q的队头一定是当前滑动窗口的最大值。
class MyQueue {
public:
MyQueue() {
this->d = new deque<int>();
}
void push(int val) {
while (!this->d->empty() && this->d->back() < val) this->d->pop_back();
this->d->push_back(val);
}
void pop() {
this->d->pop_front();
}
int front() {
return this->d->front();
}
private:
deque<int>* d;
};
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
MyQueue q;
int size = nums.size();
int right = k - 1;
vector<int> res(size - k + 1, 0);
for (int i = 0; i <= right; ++i) q.push(nums[i]);
res[0] = q.front();
while (right < size - 1) {
if (nums[right++ - k + 1] == q.front()) q.pop();
q.push(nums[right]);
res[right - k + 1] = q.front();
}
return res;
}
};
妙啊单调队列。
无。
题目链接
代码随想录文章讲解链接
用时:50m10s
MyComparision
,根据pair.second(元素出现的频次)来比较大小。将哈希表的键值对保存在优先队列中,优先队列的大小限制为k,当元素数量超出k时,弹出队首元素,由于是小顶堆的优先队列,队首元素是出现频次最少的元素。将全部键值对遍历完后,优先队列中留下来的k个元素即为出现频次最高的k个元素。class Solution {
public:
class MyComparision {
public:
// 小顶堆的比较函数
bool operator()(const pair<int, int>& left, const pair<int, int>& right) {
return left.second > right.second;
}
};
vector<int> topKFrequent(vector<int>& nums, int k) {
// 统计每个元素出现的频次
unordered_map<int, int> hashMap;
for (int& n : nums) ++hashMap[n];
// 将元素和对应的频次保存到小顶堆的优先队列中,只保留k个,当大于k个元素时,将队首元素(频次最少的元素)弹出
// 优先队列,存储的数据类型是pair,使用vector>来存储,使用MyComparison来比较元素优先级
// unordered_map的键值对是pair类型
priority_queue<pair<int, int>, vector<pair<int, int>>, MyComparision> q;
for (unordered_map<int, int>::iterator it = hashMap.begin(); it != hashMap.end(); ++it) {
q.push(*it);
if (q.size() > k) q.pop();
}
// 优先队列中留下来的k个元素即是出现频率前k高的元素,将其存储至vector中返回
vector<int> res(k, 0);
// 最先弹出来的是出现频次最小的,所以逆序保存至vector
for (int i = k - 1; i >= 0; --i) {
res[i] = q.top().first;
q.pop();
}
return res;
}
};
之前没学过堆和优先队列,正好去学习了一波堆的数据结构。
无。
题目链接
代码随想录文章讲解链接
用时:6m6s
先记录当前节点的元素,再遍历左节点,再遍历右节点。递归结束的条件是当前节点为空。
/**
* 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<int> preorderTraversal(TreeNode* root) {
vector<int> res;
traversal(root, res);
return res;
}
void traversal(TreeNode* node, vector<int>& v) {
if (node == nullptr) return;
v.push_back(node->val);
traversal(node->left, v);
traversal(node->right, v);
}
};
用时:10m23s
迭代法本质上与递归法一致,只是递归法是隐式地使用了栈,迭代法是显式地使用栈。
先将根节点入栈,前序遍历的顺序是“中左右”,所以在循环中,先将栈顶的节点弹出并记录元素(中),然后将弹出的节点的右节点入栈,再将弹出的节点的左节点入栈。因为左节点后入栈,所以在下次循环中,左节点位于栈顶,先出栈并被记录,所以顺序就是“中左右”。
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> sta;
vector<int> res;
if (root == nullptr) return res;
sta.push(root);
while (!sta.empty()) {
TreeNode* node = sta.top();
sta.pop();
res.push_back(node->val);
if (node->right != nullptr) sta.push(node->right);
if (node->left != nullptr) sta.push(node->left);
}
return res;
}
};
无。
无。
题目链接
代码随想录文章讲解链接
用时:3m1s
先遍历左节点,再记录当前节点的元素,再遍历右节点。递归结束的条件是当前节点为空。
/**
* 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<int> inorderTraversal(TreeNode* root) {
vector<int> res;
traversal(root, res);
return res;
}
void traversal(TreeNode* node, vector<int>& v) {
if (node == nullptr) return;
traversal(node->left, v);
v.push_back(node->val);
traversal(node->right, v);
}
};
迭代法与递归法本质上是一样的。中序遍历的迭代法的逻辑与前序遍历的不同。
中序遍历的顺序是“左中右”。先是不断将当前节点入栈,然后将当前节点更新成左节点,直到当前节点为空时,记录并弹出栈顶节点,将弹出节点的值添加到结果数组中,此时的值即为最左下边的节点,然后再将弹出节点的右节点入栈。语言很难描述清楚,建议根据代码实际模拟一遍。
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> sta;
TreeNode* cur = root;
while (cur != nullptr || !sta.empty()) {
if (cur != nullptr) {
sta.push(cur);
cur = cur->left;
} else {
cur = sta.top();
sta.pop();
res.push_back(cur->val);
cur = cur->right;
}
}
return res;
}
};
方法二的迭代法的代码逻辑与前序遍历的迭代法的代码逻辑相差较大,可不可以像递归法一样,写出一种统一的代码逻辑,只用修改一下顺序。
前序遍历由于顺序是“中左右”,所以在迭代法中是直接先记录当前节点,再将子节点入栈;而中序遍历和后续遍历虽然遍历到了当前节点,但是第一次遍历到的时候并不记录,而是要先去记录子节点,所以当第一次遍历到当前节点时在后面添加一个空节点作为标志,等子节点记录完回到当前节点时,看到空节点标志再将当前节点记录。
中序遍历顺序是“左中右”,所以对于当前节点,先弹出,然后将右节点入栈,再将当前节点和一个空节点入栈,最后将左节点入栈。
后续遍历顺序是“左右中”,所以对于当前节点,先将一个空节点入栈,再将右节点入栈,最后将左节点入栈。
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> st;
if (root == nullptr) return res;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
if (node != nullptr) {
st.pop();
if (node->right != nullptr) st.push(node->right);
st.push(node);
st.push(nullptr);
if (node->left != nullptr) st.push(node->left);
} else {
st.pop();
node = st.top();
st.pop();
res.push_back(node->val);
}
}
return res;
}
};
迭代法有点绕,之后得多敲几遍。
无。
题目链接
代码随想录文章讲解链接
用时:59s
跟前序和中序遍历差不多。
/**
* 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<int> postorderTraversal(TreeNode* root) {
vector<int> res;
traversal(root, res);
return res;
}
void traversal(TreeNode* node, vector<int>& v) {
if (node == nullptr) return;
traversal(node->left, v);
traversal(node->right, v);
v.push_back(node->val);
}
};
在前序遍历的迭代法中,可以能得到“中左右”顺序的结果,在前序遍历的迭代法的代码的基础上调整一下左右节点的入栈顺序,就能得到“中右左”顺序的结果,再将得到的结果翻转,即可得到“左右中”顺序的结果,即后序遍历的结果。
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> st;
if (root == nullptr) return res;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
res.push_back(node->val);
if (node->left != nullptr) st.push(node->left);
if (node->right != nullptr) st.push(node->right);
}
reverse(res.begin(), res.end());
return res;
}
};
见上一题(94e)方法三。
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> st;
if (root == nullptr) return res;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
if (node != nullptr) {
st.push(nullptr);
if (node->right != nullptr) st.push(node->right);
if (node->left != nullptr) st.push(node->left);
} else {
st.pop();
node = st.top();
st.pop();
res.push_back(node->val);
}
}
return res;
}
};
无。
无。
进入二叉树章节了,好多内容啊这一章。今天状态比前几天稍微好一点,但也只是“一点”,老是想着想着就去刷刷手机啥的,已经把知乎、小红书、微博卸载了,我还不信了。专注!专注!还题目是专注!