佩玉
"朝搴阰之木兰兮,夕揽洲之宿莽。
日月忽其不淹兮,春与秋其代序。"
暴雨来的匆匆,绵绵地,像是欲将这片历史上的霸都泡成烟雨江南。像是歌楼戏子,弦歌不辍,哀转久绝。九月初的太阳昙花一般,一绽即逝,转而陷入长达一个月地阴雨。太匆匆!谢了林红。方才秋分过去,就听闻冬天的踵音,一派“苒苒物华休”的肃秋气象。在这番寒冷的溽气中,我不愿接受这份冷冽的问候,转而躲入那青灯白墙的小楼,自称一统。
珠帘
结束了哈希表的学习,已经在C++的学习进度上欠了很大一笔债。主要忙于补考事物,由于教务处效率低下,排期拉垮,也是拖到现在(2026-10-12)才慢慢有所产出(其实是太懒了,你真是一个大懒虫啊奶油苏打和冕形灯!),然后加上现在赶进度节奏紧凑,到了现在才有所阶段性总结———惭愧··· ···
由于栈与队列的数据结构比较简单,加之现在学习的二叉树与之交集颇多,故而一并付之。
![力扣个人主页:已解答 48 题,过去一年提交 237 次]()
首先是数据结构类型上,最基础的、不含迭代器的结构有 queue, stack ,这两者作为初学者最开始碰到的容器,特性非常简单。queue是FIFO(先进先出)、有两端、保序;stack是LIFO(后进先出)、只有一端、逆序。
对于栈与队列,接口也比较简单,易于记忆,双手可数:empty,top,back,front,push,emplace,pop———没了。接口作用也可以望文生义———但是要注意一点,C++中要使用top/front和pop搭配才相当于python中的pop,top/front/back都是访问,pop才是弹出。
然后,由于栈与队列的操作大多数是对string进行操作,所以大多数题目相对简单(所以刷题的时候顿感 just so so~),当然有兴趣的也可以学我用双栈法完成 Leetcode160———单纯地为了验证自己的想法,误打误撞用到了栈(srds并无必要)。
二叉树的类型比较简单,但是很多时候并无必要分辨,我们着重要讲的是搜索树。其中涉及到四种思想:递归、迭代、深度优先搜索(Depth−Firstsearch,也作DFS)、广度优先搜索(Breadth−Firstsearch,也作BFS)。
以下是我根据不同的搜索顺序和访问顺序的不同组合,完成的层序遍历:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class BFSQueue { public: vector<vector<int>> levelOrder(TreeNode* root) { queue<TreeNode*> que; if (root != nullptr) que.push(root); vector<vector<int>> result;
while (!que.empty()) { int levelSize = static_cast<int>(que.size()); vector<int> vec;
for (int i = 0; i < levelSize; ++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(std::move(vec)); } return result; } };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class BFSRecursive { public: vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> result; if (root != nullptr) bfsNextLevel({root}, result); return result; }
private: void bfsNextLevel(const vector<TreeNode*>& level, vector<vector<int>>& result) { if (level.empty()) return;
vector<int> vec; vector<TreeNode*> nextLevel;
for (TreeNode* node : level) { vec.push_back(node->val);
if (node->left) nextLevel.push_back(node->left); if (node->right) nextLevel.push_back(node->right); } result.push_back(std::move(vec));
bfsNextLevel(nextLevel, result); } };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class DFSRecursive { public: vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> result; dfs(root, 0, result); return result; }
private:
void dfs(TreeNode* node, int depth, vector<vector<int>>& result) { if (node == nullptr) return;
if (depth == static_cast<int>(result.size())) result.push_back({}); result[depth].push_back(node->val);
dfs(node->left, depth + 1, result); dfs(node->right, depth + 1, result); } };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class DFSStack { public: vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> result; if (root == nullptr) return result;
stack<pair<TreeNode*, int>> st; st.push({root, 0});
while (!st.empty()) { TreeNode* node = st.top().first; int depth = st.top().second; st.pop();
if (depth == static_cast<int>(result.size())) result.push_back({});
result[depth].push_back(node->val);
if (node->right) st.push({node->right, depth + 1}); if (node->left) st.push({node->left, depth + 1}); } return result; } };
|
看完这些,头晕是正常的,接下来你可以一个打十个(点题目即跳转 LeetCode):
其中每个题目的答案模板都出自以上四个方法,不同之处只在输出的答案。当我们在面对一个算法题时应该如何思考呢?
首先我们应该考虑:我需不需要跨节点?我需要求树深吗?我的返回类型是什么?我应该选择什么容器?从而我们就可以确定下我们是选择DFS还是BFS。选择不恰当的容器在进行计算的时候可能会出现容器溢出的报错,从而降低程序的可运行性。
闲云
写到此处,"否"的那一个月忽然不那么难堪了。排期拉垮的补考、欠下的进度、被惰性啃掉的夜晚,都算不得劫难,不过是少年人账本上一笔记不住的亏空。现在所做,不过以往的踵音。
仍记得高中背的一篇小散文,王佐良先生翻译的Youth:“青春气贯长虹,勇锐盖过怯弱,进取压倒苟安。”这并非是社会达尔文主义抑或优绩主义,只是为了逃避那个用试卷就能被定义的怪圈,那个被形式主义浸泡的校园,那个无时无刻不被人定义的过去。
反动的、盲目的、片面的、冰冷的、否定的,进步的对立面矗立于眼前,我无法忽视这头客厅里的大象,无法任由其压迫我的呼吸。大雪深埋中,我无法得知我是否能够如同彼时的姚刑部一样,探得那“苍山负雪,明烛天南”。
十月的夜,雨歇了,青灯白墙的小楼仍是那小楼。抬颌时,墙上映着两个影子,一个伏案,一个长明。所谓否极泰来,从来不是天光忽然回转,而是一个人走到最暗处的路上,仍未搁笔。