[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fIH4dJnCA80i-PjINxxhV4U5mkeVCTWzOyxMnRLUMSVE":3,"$fJU-4tot_gC5fDkujNeoE-cGsdMy5V_KcdUXLuAnTFgw":16,"$fNy9BKBYB7Y7zWnyTCJVL38VKPW75RtPB7_noGYhDccY":423},{"slug":4,"title":5,"description":6,"content":7,"content_html":8,"pub_date":9,"tags":10,"draft":15},"data-structures-fundamentals","数据结构基础：从数组到红黑树","系统梳理常用数据结构的核心原理、时间复杂度和适用场景。数组、链表、栈、队列、哈希表、二叉树、堆、图，每种结构附实现要点和 C++ 代码片段。","import Chart from '..\u002F..\u002Fcomponents\u002FChart.vue'\n\nexport const lookupComplexityData = {\n  labels: ['数组', '链表', '哈希表', 'BST(平衡)', '堆', '图(BFS)'],\n  datasets: [\n    {\n      label: '平均查找复杂度（1=O(1), 2=O(log n), 3=O(n)）',\n      data: [1, 3, 1, 2, 2, 3],\n      backgroundColor: [\n        'rgba(0,212,255,0.7)',\n        'rgba(255,0,170,0.7)',\n        'rgba(0,255,128,0.7)',\n        'rgba(255,200,0,0.7)',\n        'rgba(180,0,255,0.7)',\n        'rgba(255,100,0,0.7)',\n      ],\n      borderColor: [\n        'rgba(0,212,255,1)',\n        'rgba(255,0,170,1)',\n        'rgba(0,255,128,1)',\n        'rgba(255,200,0,1)',\n        'rgba(180,0,255,1)',\n        'rgba(255,100,0,1)',\n      ],\n      borderWidth: 2,\n    },\n  ],\n}\n\nexport const lookupComplexityOptions = {\n  indexAxis: 'y',\n  plugins: {\n    legend: { display: false },\n    tooltip: {\n      callbacks: {\n        label: (ctx) => {\n          const map = { 1: 'O(1)', 2: 'O(log n)', 3: 'O(n)' }\n          return ` ${map[ctx.raw] || ctx.raw}`\n        },\n      },\n    },\n  },\n  scales: {\n    x: {\n      ticks: {\n        stepSize: 1,\n        callback: (val) => ({ 1: 'O(1)', 2: 'O(log n)', 3: 'O(n)' }[val] || val),\n      },\n      min: 0,\n      max: 3.5,\n    },\n  },\n}\n\n选数据结构不是为了背面试题——是为了**做出正确的工程决策**。\n\n用错了数据结构，代码不会报错，但会慢。慢到你追不上来。\n\n一个真实案例：某游戏引擎在每帧遍历 `std::list\u003CEntity>` 做碰撞检测，改成 `std::vector` 之后帧率翻倍，原因是链表节点在内存里不连续，CPU cache miss 代价在高频循环里被放大了数十倍。\n\n数据结构的价值在于：**同样的逻辑，不同的结构，性能可以差 100 倍**。\n\n---\n\n## 数组 Array\n\n**核心思路**：连续内存块，下标直接寻址。静态数组大小固定；动态数组（`std::vector`）在容量不足时以 2x 因子扩容，均摊 O(1) push_back。\n\n| 操作 | 时间复杂度 |\n|------|-----------|\n| 随机访问（按下标） | O(1) |\n| 尾部插入（均摊） | O(1) |\n| 中间插入 \u002F 删除 | O(n) |\n| 搜索（无序） | O(n) |\n\n**Cache Locality 优势**：数组元素在内存中紧密排列，CPU 预取一次缓存行（64 bytes）可以装下多个元素。链表节点散落各处，每次指针跳转都可能触发 cache miss，在密集遍历场景下代价极高。\n\n```cpp\n#include \u003Cvector>\n#include \u003Calgorithm>\n\n\u002F\u002F 动态数组：预分配容量避免频繁扩容\nstd::vector\u003Cint> v;\nv.reserve(1000);           \u002F\u002F 预分配，不改变 size\nv.push_back(42);           \u002F\u002F O(1) 均摊\nv.insert(v.begin(), 0);    \u002F\u002F O(n)，头插代价高\n\n\u002F\u002F 随机访问\nint x = v[3];              \u002F\u002F O(1)，不检查边界\nint y = v.at(3);           \u002F\u002F O(1)，抛 out_of_range\n\n\u002F\u002F 排序后二分查找\nstd::sort(v.begin(), v.end());\nbool found = std::binary_search(v.begin(), v.end(), 42);  \u002F\u002F O(log n)\n```\n\n**适用场景**：\n- 频繁随机访问、下标遍历\n- 数据量可预估，需要 cache 友好的密集计算（矩阵、图像像素）\n- 作为其他结构（堆、哈希表槽）的底层存储\n\n---\n\n## 链表 Linked List\n\n**核心思路**：节点通过指针串联，无需连续内存。单链表每节点存 `next`，双链表额外存 `prev`，支持 O(1) 双向删除。\n\n| 操作 | 时间复杂度 |\n|------|-----------|\n| 随机访问 | O(n) |\n| 头 \u002F 尾插入（已知位置） | O(1) |\n| 中间插入（已知迭代器） | O(1) |\n| 搜索 | O(n) |\n\n**list vs vector 的权衡**：\n\n```cpp\n#include \u003Clist>\n\nstd::list\u003Cint> lst = {1, 2, 3, 4, 5};\n\n\u002F\u002F 在已知迭代器位置插入是 O(1)\nauto it = std::find(lst.begin(), lst.end(), 3);\nlst.insert(it, 99);   \u002F\u002F 在 3 前面插入 99，不移动其他元素\n\n\u002F\u002F 删除也是 O(1)（已知迭代器）\nlst.erase(it);        \u002F\u002F 删除 3\n\n\u002F\u002F splice：O(1) 把另一个 list 的元素移过来，不拷贝\nstd::list\u003Cint> other = {10, 20};\nlst.splice(lst.end(), other);  \u002F\u002F 把 other 全部接到 lst 末尾\n```\n\n**何时选 `std::list` 而不是 `std::vector`**：\n- 需要在容器中间**频繁插入\u002F删除**，且持有迭代器不失效（如 LRU Cache 实现）\n- 使用 `splice` 做 O(1) 节点转移（如任务调度队列）\n\n**何时坚持用 `std::vector`**：\n- 几乎所有顺序遍历场景——cache locality 优势通常超过 list 的插入优势\n- 数据量 \u003C 几百个元素时，vector 搜索甚至比 map 快（SIMD 顺序扫描）\n\n---\n\n## 栈 Stack\n\n**核心思路**：LIFO（Last In First Out）。只操作栈顶，push\u002Fpop 均 O(1)。\n\n| 操作 | 时间复杂度 |\n|------|-----------|\n| push \u002F pop | O(1) |\n| peek（查看栈顶） | O(1) |\n\n**Call Stack 类比**：函数调用天然是栈结构——调用函数时压栈（保存局部变量 + 返回地址），返回时弹栈。递归爆栈（stack overflow）就是压栈次数超出系统限制。\n\n```cpp\n#include \u003Cstack>\n#include \u003Cstring>\n#include \u003Cstdexcept>\n\n\u002F\u002F 括号匹配：经典栈应用\nbool isValid(const std::string& s) {\n    std::stack\u003Cchar> st;\n    for (char c : s) {\n        if (c == '(' || c == '[' || c == '{') {\n            st.push(c);\n        } else {\n            if (st.empty()) return false;\n            char top = st.top(); st.pop();\n            if (c == ')' && top != '(') return false;\n            if (c == ']' && top != '[') return false;\n            if (c == '}' && top != '{') return false;\n        }\n    }\n    return st.empty();\n}\n\n\u002F\u002F 单调栈：O(n) 解决\"下一个更大元素\"\nstd::vector\u003Cint> nextGreater(const std::vector\u003Cint>& nums) {\n    int n = nums.size();\n    std::vector\u003Cint> res(n, -1);\n    std::stack\u003Cint> st;  \u002F\u002F 存下标\n    for (int i = 0; i \u003C n; i++) {\n        while (!st.empty() && nums[i] > nums[st.top()]) {\n            res[st.top()] = nums[i];\n            st.pop();\n        }\n        st.push(i);\n    }\n    return res;\n}\n```\n\n**适用场景**：括号\u002F表达式解析、DFS 迭代实现、撤销\u002F重做（Undo\u002FRedo）、函数调用模拟。\n\n---\n\n## 队列 Queue\n\n**核心思路**：FIFO（First In First Out）。队尾入队，队头出队，均 O(1)。双端队列（deque）两端都可操作。\n\n| 操作 | 时间复杂度 |\n|------|-----------|\n| enqueue（push_back） | O(1) |\n| dequeue（pop_front） | O(1) |\n| peek | O(1) |\n\n```cpp\n#include \u003Cqueue>\n#include \u003Cvector>\n\n\u002F\u002F BFS 模板：图的层序遍历\nvoid bfs(int start, const std::vector\u003Cstd::vector\u003Cint>>& adj) {\n    int n = adj.size();\n    std::vector\u003Cbool> visited(n, false);\n    std::queue\u003Cint> q;\n\n    visited[start] = true;\n    q.push(start);\n\n    while (!q.empty()) {\n        int node = q.front(); q.pop();\n        \u002F\u002F 处理 node\n        for (int neighbor : adj[node]) {\n            if (!visited[neighbor]) {\n                visited[neighbor] = true;\n                q.push(neighbor);\n            }\n        }\n    }\n}\n```\n\n**适用场景**：BFS、任务调度（先来先服务）、消息队列、滑动窗口（配合 `std::deque`）。\n\n---\n\n## 哈希表 Hash Table\n\n**核心思路**：通过哈希函数将 key 映射到数组槽，实现近似 O(1) 的增删查。冲突解决主要有两种策略：\n\n- **链地址法（Chaining）**：每个槽是链表，`std::unordered_map` 采用此方案\n- **开放地址法（Open Addressing）**：冲突时探测其他槽，cache 友好但删除复杂\n\n| 操作 | 平均 | 最坏（哈希碰撞退化） |\n|------|------|---------------------|\n| 插入 | O(1) | O(n) |\n| 删除 | O(1) | O(n) |\n| 查找 | O(1) | O(n) |\n\n**Load Factor（负载因子）**：`元素数 \u002F 槽数`。`unordered_map` 默认最大 load factor 为 1.0，超过时触发 rehash（扩容 + 重新哈希），代价 O(n)。\n\n```cpp\n#include \u003Cunordered_map>\n#include \u003Cstring>\n\n\u002F\u002F 词频统计\nstd::unordered_map\u003Cstd::string, int> freq;\nstd::string words[] = {\"hello\", \"world\", \"hello\", \"cpp\"};\nfor (const auto& w : words) {\n    freq[w]++;   \u002F\u002F 不存在则默认构造为 0\n}\n\n\u002F\u002F 预分配桶，减少 rehash\nfreq.reserve(1000);          \u002F\u002F 至少 1000 个桶\nfreq.max_load_factor(0.7f);  \u002F\u002F 提前触发扩容，减少碰撞\n\n\u002F\u002F 遍历\nfor (const auto& [key, val] : freq) {\n    \u002F\u002F key: \"hello\"=2, \"world\"=1, \"cpp\"=1\n}\n\n\u002F\u002F 自定义哈希（用于自定义 key 类型）\nstruct PairHash {\n    size_t operator()(const std::pair\u003Cint,int>& p) const {\n        return std::hash\u003Cint>{}(p.first) ^ (std::hash\u003Cint>{}(p.second) \u003C\u003C 32);\n    }\n};\nstd::unordered_map\u003Cstd::pair\u003Cint,int>, int, PairHash> grid;\n```\n\n**适用场景**：O(1) 查找（缓存、去重）、词频\u002F计数、图的邻接表、两数之和类问题。\n\n**注意**：哈希表无序，需要有序遍历时用 `std::map`（红黑树）。\n\n---\n\n## 时间复杂度汇总\n\n\u003CChart client:only=\"vue\" type=\"bar\" data={lookupComplexityData} options={lookupComplexityOptions} height={240} \u002F>\n\n---\n\n## 二叉搜索树 BST\n\n**核心思路**：左子树所有节点 \u003C 根 \u003C 右子树所有节点。中序遍历输出有序序列。\n\n| 操作 | 平均 | 最坏（退化为链表） |\n|------|------|-------------------|\n| 插入 | O(log n) | O(n) |\n| 删除 | O(log n) | O(n) |\n| 查找 | O(log n) | O(n) |\n\n**退化问题**：按有序序列插入时，BST 退化为链表，所有操作变 O(n)。这是 AVL 树和红黑树的动机。\n\n```cpp\nstruct TreeNode {\n    int val;\n    TreeNode* left = nullptr;\n    TreeNode* right = nullptr;\n    TreeNode(int v) : val(v) {}\n};\n\n\u002F\u002F BST 插入\nTreeNode* insert(TreeNode* root, int val) {\n    if (!root) return new TreeNode(val);\n    if (val \u003C root->val)\n        root->left = insert(root->left, val);\n    else if (val > root->val)\n        root->right = insert(root->right, val);\n    return root;\n}\n\n\u002F\u002F 中序遍历（输出有序序列）\nvoid inorder(TreeNode* root, std::vector\u003Cint>& result) {\n    if (!root) return;\n    inorder(root->left, result);\n    result.push_back(root->val);\n    inorder(root->right, result);\n}\n```\n\n---\n\n## AVL 树 vs 红黑树\n\n两者都是**自平衡 BST**，保证树高 O(log n)，但平衡策略不同：\n\n| 特性 | AVL 树 | 红黑树 |\n|------|--------|--------|\n| 平衡条件 | 左右子树高度差 ≤ 1（严格） | 5 条颜色规则（宽松） |\n| 查找性能 | 略优（树更矮） | 略差 |\n| 插入 \u002F 删除旋转次数 | 多 | 少（最多 3 次旋转） |\n| 适合场景 | 读多写少 | 读写均衡 |\n\n**为什么 `std::map` \u002F `std::set` 用红黑树**：\n标准库容器需要同时支持高效插入、删除、查找。红黑树的插入\u002F删除旋转次数有严格上界（插入最多 2 次，删除最多 3 次），而 AVL 树在删除时可能需要 O(log n) 次旋转。对于通用容器，红黑树在修改操作上更稳定。\n\n```cpp\n#include \u003Cmap>\n#include \u003Cset>\n\n\u002F\u002F std::map 底层红黑树，有序，O(log n) 操作\nstd::map\u003Cstd::string, int> scores;\nscores[\"Alice\"] = 95;\nscores[\"Bob\"] = 87;\n\n\u002F\u002F 范围查询（利用有序性）\nauto it = scores.lower_bound(\"Alice\");  \u002F\u002F >= \"Alice\" 的第一个\n\n\u002F\u002F std::set：唯一有序集合\nstd::set\u003Cint> nums = {3, 1, 4, 1, 5, 9};  \u002F\u002F 自动去重并排序\n\u002F\u002F 结果：{1, 3, 4, 5, 9}\n```\n\n---\n\n## 堆 Heap\n\n**核心思路**：完全二叉树，用数组存储（节点 i 的左孩子为 2i+1，右孩子为 2i+2）。最大堆保证父节点 ≥ 子节点，根节点是最大值。\n\n| 操作 | 时间复杂度 |\n|------|-----------|\n| 插入（push） | O(log n) |\n| 删除最大值（pop） | O(log n) |\n| 查看最大值（peek） | O(1) |\n| 建堆（heapify） | O(n) |\n\n**Heapify O(n) 建堆原理**：从最后一个非叶节点向上 sift-down，大部分节点在树的底部高度小，总代价收敛到 O(n)（而非 n 次 O(log n) 插入的 O(n log n)）。\n\n```cpp\n#include \u003Cqueue>\n#include \u003Cvector>\n#include \u003Cfunctional>  \u002F\u002F std::greater\n\n\u002F\u002F 最大堆（默认）\nstd::priority_queue\u003Cint> maxHeap;\nmaxHeap.push(3);\nmaxHeap.push(1);\nmaxHeap.push(4);\nint top = maxHeap.top();  \u002F\u002F 4\nmaxHeap.pop();\n\n\u002F\u002F 最小堆\nstd::priority_queue\u003Cint, std::vector\u003Cint>, std::greater\u003Cint>> minHeap;\n\n\u002F\u002F 自定义比较（按第二元素排序）\nusing P = std::pair\u003Cint, int>;\nauto cmp = [](const P& a, const P& b) { return a.second > b.second; };\nstd::priority_queue\u003CP, std::vector\u003CP>, decltype(cmp)> pq(cmp);\n\n\u002F\u002F 手动 heapify（从无序数组 O(n) 建堆）\nstd::vector\u003Cint> arr = {3, 1, 4, 1, 5, 9, 2, 6};\nstd::make_heap(arr.begin(), arr.end());  \u002F\u002F 最大堆，O(n)\nstd::sort_heap(arr.begin(), arr.end()); \u002F\u002F 堆排序，O(n log n)\n```\n\n**适用场景**：Top-K 问题、Dijkstra 最短路、任务调度（优先级队列）、中位数维护（双堆）。\n\n---\n\n## 图 Graph\n\n**核心思路**：节点（Vertex）+ 边（Edge）。存储方式影响空间和遍历效率：\n\n| 存储方式 | 空间 | 查询边是否存在 | 遍历邻居 |\n|---------|------|---------------|---------|\n| 邻接矩阵 | O(V²) | O(1) | O(V) |\n| 邻接表 | O(V+E) | O(degree) | O(degree) |\n\n**选择原则**：稠密图（E 接近 V²）用矩阵；稀疏图（E 远小于 V²，如路网、社交网络）用邻接表。\n\n```cpp\n#include \u003Cvector>\n#include \u003Cqueue>\n#include \u003Cstack>\n\n\u002F\u002F 邻接表建图\nint V = 6;\nstd::vector\u003Cstd::vector\u003Cint>> adj(V);\nauto addEdge = [&](int u, int v) {\n    adj[u].push_back(v);\n    adj[v].push_back(u);  \u002F\u002F 无向图\n};\n\n\u002F\u002F BFS：最短路径（无权图）\nstd::vector\u003Cint> bfsShortestPath(int src, int dst) {\n    std::vector\u003Cint> dist(V, -1);\n    std::vector\u003Cint> parent(V, -1);\n    std::queue\u003Cint> q;\n\n    dist[src] = 0;\n    q.push(src);\n\n    while (!q.empty()) {\n        int u = q.front(); q.pop();\n        for (int v : adj[u]) {\n            if (dist[v] == -1) {\n                dist[v] = dist[u] + 1;\n                parent[v] = u;\n                q.push(v);\n            }\n        }\n    }\n\n    \u002F\u002F 回溯路径\n    std::vector\u003Cint> path;\n    for (int v = dst; v != -1; v = parent[v])\n        path.push_back(v);\n    std::reverse(path.begin(), path.end());\n    return path;\n}\n\n\u002F\u002F DFS：迭代实现（避免递归栈溢出）\nvoid dfsIterative(int start) {\n    std::vector\u003Cbool> visited(V, false);\n    std::stack\u003Cint> st;\n\n    st.push(start);\n    while (!st.empty()) {\n        int u = st.top(); st.pop();\n        if (visited[u]) continue;\n        visited[u] = true;\n        \u002F\u002F 处理 u\n        for (int v : adj[u]) {\n            if (!visited[v]) st.push(v);\n        }\n    }\n}\n```\n\n**适用场景**：\n- BFS：最短路径（无权）、层序遍历、连通分量\n- DFS：拓扑排序、强连通分量、迷宫\u002F路径搜索\n- 带权图：Dijkstra（非负权）、Bellman-Ford（含负权）\n\n---\n\n## 选型速查表\n\n| 场景 | 推荐数据结构 | 原因 |\n|------|------------|------|\n| 频繁随机访问，顺序遍历 | `std::vector` | cache 友好，O(1) 随机访问 |\n| 频繁中间插入\u002F删除，持有迭代器 | `std::list` | O(1) 插删（已知位置） |\n| LIFO、表达式解析、DFS | `std::stack` | 语义清晰，底层 deque |\n| BFS、任务队列 | `std::queue` | FIFO 语义 |\n| O(1) 查找，无序 | `std::unordered_map` | 哈希 O(1) 均摊 |\n| 有序遍历，范围查询 | `std::map` \u002F `std::set` | 红黑树，有序 O(log n) |\n| Top-K，优先调度 | `std::priority_queue` | 堆，O(log n) push\u002Fpop |\n| 最短路径，连通性 | 邻接表 + BFS\u002FDijkstra | 稀疏图 O(V+E) |\n| 数学矩阵，稠密图 | 二维 `std::vector` | 邻接矩阵 O(1) 查边 |\n| 区间查询，前缀和 | Segment Tree \u002F BIT | O(log n) 区间操作 |\n\n---\n\n理解了这些结构的本质，再看算法题或工程代码，会有一种\"看穿骨架\"的感觉——不是在背答案，是在做选择。","\u003Cp>import Chart from ‘…\u002F…\u002Fcomponents\u002FChart.vue’\u003C\u002Fp>\n\u003Cp>export const lookupComplexityData = {\nlabels: [‘数组’, ‘链表’, ‘哈希表’, ‘BST(平衡)’, ‘堆’, ‘图(BFS)’],\ndatasets: [\n{\nlabel: ‘平均查找复杂度（1=O(1), 2=O(log n), 3=O(n)）’,\ndata: [1, 3, 1, 2, 2, 3],\nbackgroundColor: [\n‘rgba(0,212,255,0.7)’,\n‘rgba(255,0,170,0.7)’,\n‘rgba(0,255,128,0.7)’,\n‘rgba(255,200,0,0.7)’,\n‘rgba(180,0,255,0.7)’,\n‘rgba(255,100,0,0.7)’,\n],\nborderColor: [\n‘rgba(0,212,255,1)’,\n‘rgba(255,0,170,1)’,\n‘rgba(0,255,128,1)’,\n‘rgba(255,200,0,1)’,\n‘rgba(180,0,255,1)’,\n‘rgba(255,100,0,1)’,\n],\nborderWidth: 2,\n},\n],\n}\u003C\u002Fp>\n\u003Cp>export const lookupComplexityOptions = {\nindexAxis: ‘y’,\nplugins: {\nlegend: { display: false },\ntooltip: {\ncallbacks: {\nlabel: (ctx) =&gt; {\nconst map = { 1: ‘O(1)’, 2: ‘O(log n)’, 3: ‘O(n)’ }\nreturn \u003Ccode> ${map[ctx.raw] || ctx.raw}\u003C\u002Fcode>\n},\n},\n},\n},\nscales: {\nx: {\nticks: {\nstepSize: 1,\ncallback: (val) =&gt; ({ 1: ‘O(1)’, 2: ‘O(log n)’, 3: ‘O(n)’ }[val] || val),\n},\nmin: 0,\nmax: 3.5,\n},\n},\n}\u003C\u002Fp>\n\u003Cp>选数据结构不是为了背面试题——是为了\u003Cstrong>做出正确的工程决策\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>用错了数据结构，代码不会报错，但会慢。慢到你追不上来。\u003C\u002Fp>\n\u003Cp>一个真实案例：某游戏引擎在每帧遍历 \u003Ccode>std::list&lt;Entity&gt;\u003C\u002Fcode> 做碰撞检测，改成 \u003Ccode>std::vector\u003C\u002Fcode> 之后帧率翻倍，原因是链表节点在内存里不连续，CPU cache miss 代价在高频循环里被放大了数十倍。\u003C\u002Fp>\n\u003Cp>数据结构的价值在于：\u003Cstrong>同样的逻辑，不同的结构，性能可以差 100 倍\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"数组-array\">数组 Array\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>核心思路\u003C\u002Fstrong>：连续内存块，下标直接寻址。静态数组大小固定；动态数组（\u003Ccode>std::vector\u003C\u002Fcode>）在容量不足时以 2x 因子扩容，均摊 O(1) push_back。\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>操作\u003C\u002Fth>\n\u003Cth>时间复杂度\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>随机访问（按下标）\u003C\u002Ftd>\n\u003Ctd>O(1)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>尾部插入（均摊）\u003C\u002Ftd>\n\u003Ctd>O(1)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>中间插入 \u002F 删除\u003C\u002Ftd>\n\u003Ctd>O(n)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>搜索（无序）\u003C\u002Ftd>\n\u003Ctd>O(n)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>\u003Cstrong>Cache Locality 优势\u003C\u002Fstrong>：数组元素在内存中紧密排列，CPU 预取一次缓存行（64 bytes）可以装下多个元素。链表节点散落各处，每次指针跳转都可能触发 cache miss，在密集遍历场景下代价极高。\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-cpp\">#include &lt;vector&gt;\n#include &lt;algorithm&gt;\n\n\u002F\u002F 动态数组：预分配容量避免频繁扩容\nstd::vector&lt;int&gt; v;\nv.reserve(1000);           \u002F\u002F 预分配，不改变 size\nv.push_back(42);           \u002F\u002F O(1) 均摊\nv.insert(v.begin(), 0);    \u002F\u002F O(n)，头插代价高\n\n\u002F\u002F 随机访问\nint x = v[3];              \u002F\u002F O(1)，不检查边界\nint y = v.at(3);           \u002F\u002F O(1)，抛 out_of_range\n\n\u002F\u002F 排序后二分查找\nstd::sort(v.begin(), v.end());\nbool found = std::binary_search(v.begin(), v.end(), 42);  \u002F\u002F O(log n)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>适用场景\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>频繁随机访问、下标遍历\u003C\u002Fli>\n\u003Cli>数据量可预估，需要 cache 友好的密集计算（矩阵、图像像素）\u003C\u002Fli>\n\u003Cli>作为其他结构（堆、哈希表槽）的底层存储\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Chr>\n\u003Ch2 id=\"链表-linked-list\">链表 Linked List\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>核心思路\u003C\u002Fstrong>：节点通过指针串联，无需连续内存。单链表每节点存 \u003Ccode>next\u003C\u002Fcode>，双链表额外存 \u003Ccode>prev\u003C\u002Fcode>，支持 O(1) 双向删除。\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>操作\u003C\u002Fth>\n\u003Cth>时间复杂度\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>随机访问\u003C\u002Ftd>\n\u003Ctd>O(n)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>头 \u002F 尾插入（已知位置）\u003C\u002Ftd>\n\u003Ctd>O(1)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>中间插入（已知迭代器）\u003C\u002Ftd>\n\u003Ctd>O(1)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>搜索\u003C\u002Ftd>\n\u003Ctd>O(n)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>\u003Cstrong>list vs vector 的权衡\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-cpp\">#include &lt;list&gt;\n\nstd::list&lt;int&gt; lst = {1, 2, 3, 4, 5};\n\n\u002F\u002F 在已知迭代器位置插入是 O(1)\nauto it = std::find(lst.begin(), lst.end(), 3);\nlst.insert(it, 99);   \u002F\u002F 在 3 前面插入 99，不移动其他元素\n\n\u002F\u002F 删除也是 O(1)（已知迭代器）\nlst.erase(it);        \u002F\u002F 删除 3\n\n\u002F\u002F splice：O(1) 把另一个 list 的元素移过来，不拷贝\nstd::list&lt;int&gt; other = {10, 20};\nlst.splice(lst.end(), other);  \u002F\u002F 把 other 全部接到 lst 末尾\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>何时选 \u003Ccode>std::list\u003C\u002Fcode> 而不是 \u003Ccode>std::vector\u003C\u002Fcode>\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>需要在容器中间\u003Cstrong>频繁插入\u002F删除\u003C\u002Fstrong>，且持有迭代器不失效（如 LRU Cache 实现）\u003C\u002Fli>\n\u003Cli>使用 \u003Ccode>splice\u003C\u002Fcode> 做 O(1) 节点转移（如任务调度队列）\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>何时坚持用 \u003Ccode>std::vector\u003C\u002Fcode>\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>几乎所有顺序遍历场景——cache locality 优势通常超过 list 的插入优势\u003C\u002Fli>\n\u003Cli>数据量 &lt; 几百个元素时，vector 搜索甚至比 map 快（SIMD 顺序扫描）\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Chr>\n\u003Ch2 id=\"栈-stack\">栈 Stack\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>核心思路\u003C\u002Fstrong>：LIFO（Last In First Out）。只操作栈顶，push\u002Fpop 均 O(1)。\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>操作\u003C\u002Fth>\n\u003Cth>时间复杂度\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>push \u002F pop\u003C\u002Ftd>\n\u003Ctd>O(1)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>peek（查看栈顶）\u003C\u002Ftd>\n\u003Ctd>O(1)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>\u003Cstrong>Call Stack 类比\u003C\u002Fstrong>：函数调用天然是栈结构——调用函数时压栈（保存局部变量 + 返回地址），返回时弹栈。递归爆栈（stack overflow）就是压栈次数超出系统限制。\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-cpp\">#include &lt;stack&gt;\n#include &lt;string&gt;\n#include &lt;stdexcept&gt;\n\n\u002F\u002F 括号匹配：经典栈应用\nbool isValid(const std::string&amp; s) {\n    std::stack&lt;char&gt; st;\n    for (char c : s) {\n        if (c == '(' || c == '[' || c == '{') {\n            st.push(c);\n        } else {\n            if (st.empty()) return false;\n            char top = st.top(); st.pop();\n            if (c == ')' &amp;&amp; top != '(') return false;\n            if (c == ']' &amp;&amp; top != '[') return false;\n            if (c == '}' &amp;&amp; top != '{') return false;\n        }\n    }\n    return st.empty();\n}\n\n\u002F\u002F 单调栈：O(n) 解决&quot;下一个更大元素&quot;\nstd::vector&lt;int&gt; nextGreater(const std::vector&lt;int&gt;&amp; nums) {\n    int n = nums.size();\n    std::vector&lt;int&gt; res(n, -1);\n    std::stack&lt;int&gt; st;  \u002F\u002F 存下标\n    for (int i = 0; i &lt; n; i++) {\n        while (!st.empty() &amp;&amp; nums[i] &gt; nums[st.top()]) {\n            res[st.top()] = nums[i];\n            st.pop();\n        }\n        st.push(i);\n    }\n    return res;\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>适用场景\u003C\u002Fstrong>：括号\u002F表达式解析、DFS 迭代实现、撤销\u002F重做（Undo\u002FRedo）、函数调用模拟。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"队列-queue\">队列 Queue\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>核心思路\u003C\u002Fstrong>：FIFO（First In First Out）。队尾入队，队头出队，均 O(1)。双端队列（deque）两端都可操作。\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>操作\u003C\u002Fth>\n\u003Cth>时间复杂度\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>enqueue（push_back）\u003C\u002Ftd>\n\u003Ctd>O(1)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>dequeue（pop_front）\u003C\u002Ftd>\n\u003Ctd>O(1)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>peek\u003C\u002Ftd>\n\u003Ctd>O(1)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cpre>\u003Ccode class=\"language-cpp\">#include &lt;queue&gt;\n#include &lt;vector&gt;\n\n\u002F\u002F BFS 模板：图的层序遍历\nvoid bfs(int start, const std::vector&lt;std::vector&lt;int&gt;&gt;&amp; adj) {\n    int n = adj.size();\n    std::vector&lt;bool&gt; visited(n, false);\n    std::queue&lt;int&gt; q;\n\n    visited[start] = true;\n    q.push(start);\n\n    while (!q.empty()) {\n        int node = q.front(); q.pop();\n        \u002F\u002F 处理 node\n        for (int neighbor : adj[node]) {\n            if (!visited[neighbor]) {\n                visited[neighbor] = true;\n                q.push(neighbor);\n            }\n        }\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>适用场景\u003C\u002Fstrong>：BFS、任务调度（先来先服务）、消息队列、滑动窗口（配合 \u003Ccode>std::deque\u003C\u002Fcode>）。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"哈希表-hash-table\">哈希表 Hash Table\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>核心思路\u003C\u002Fstrong>：通过哈希函数将 key 映射到数组槽，实现近似 O(1) 的增删查。冲突解决主要有两种策略：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>链地址法（Chaining）\u003C\u002Fstrong>：每个槽是链表，\u003Ccode>std::unordered_map\u003C\u002Fcode> 采用此方案\u003C\u002Fli>\n\u003Cli>\u003Cstrong>开放地址法（Open Addressing）\u003C\u002Fstrong>：冲突时探测其他槽，cache 友好但删除复杂\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>操作\u003C\u002Fth>\n\u003Cth>平均\u003C\u002Fth>\n\u003Cth>最坏（哈希碰撞退化）\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>插入\u003C\u002Ftd>\n\u003Ctd>O(1)\u003C\u002Ftd>\n\u003Ctd>O(n)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>删除\u003C\u002Ftd>\n\u003Ctd>O(1)\u003C\u002Ftd>\n\u003Ctd>O(n)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>查找\u003C\u002Ftd>\n\u003Ctd>O(1)\u003C\u002Ftd>\n\u003Ctd>O(n)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>\u003Cstrong>Load Factor（负载因子）\u003C\u002Fstrong>：\u003Ccode>元素数 \u002F 槽数\u003C\u002Fcode>。\u003Ccode>unordered_map\u003C\u002Fcode> 默认最大 load factor 为 1.0，超过时触发 rehash（扩容 + 重新哈希），代价 O(n)。\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-cpp\">#include &lt;unordered_map&gt;\n#include &lt;string&gt;\n\n\u002F\u002F 词频统计\nstd::unordered_map&lt;std::string, int&gt; freq;\nstd::string words[] = {&quot;hello&quot;, &quot;world&quot;, &quot;hello&quot;, &quot;cpp&quot;};\nfor (const auto&amp; w : words) {\n    freq[w]++;   \u002F\u002F 不存在则默认构造为 0\n}\n\n\u002F\u002F 预分配桶，减少 rehash\nfreq.reserve(1000);          \u002F\u002F 至少 1000 个桶\nfreq.max_load_factor(0.7f);  \u002F\u002F 提前触发扩容，减少碰撞\n\n\u002F\u002F 遍历\nfor (const auto&amp; [key, val] : freq) {\n    \u002F\u002F key: &quot;hello&quot;=2, &quot;world&quot;=1, &quot;cpp&quot;=1\n}\n\n\u002F\u002F 自定义哈希（用于自定义 key 类型）\nstruct PairHash {\n    size_t operator()(const std::pair&lt;int,int&gt;&amp; p) const {\n        return std::hash&lt;int&gt;{}(p.first) ^ (std::hash&lt;int&gt;{}(p.second) &lt;&lt; 32);\n    }\n};\nstd::unordered_map&lt;std::pair&lt;int,int&gt;, int, PairHash&gt; grid;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>适用场景\u003C\u002Fstrong>：O(1) 查找（缓存、去重）、词频\u002F计数、图的邻接表、两数之和类问题。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>注意\u003C\u002Fstrong>：哈希表无序，需要有序遍历时用 \u003Ccode>std::map\u003C\u002Fcode>（红黑树）。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"时间复杂度汇总\">时间复杂度汇总\u003C\u002Fh2>\n\u003CChart client:only=\"vue\" type=\"bar\" data={lookupComplexityData} options={lookupComplexityOptions} height={240} \u002F>\n\u003Chr>\n\u003Ch2 id=\"二叉搜索树-bst\">二叉搜索树 BST\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>核心思路\u003C\u002Fstrong>：左子树所有节点 &lt; 根 &lt; 右子树所有节点。中序遍历输出有序序列。\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>操作\u003C\u002Fth>\n\u003Cth>平均\u003C\u002Fth>\n\u003Cth>最坏（退化为链表）\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>插入\u003C\u002Ftd>\n\u003Ctd>O(log n)\u003C\u002Ftd>\n\u003Ctd>O(n)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>删除\u003C\u002Ftd>\n\u003Ctd>O(log n)\u003C\u002Ftd>\n\u003Ctd>O(n)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>查找\u003C\u002Ftd>\n\u003Ctd>O(log n)\u003C\u002Ftd>\n\u003Ctd>O(n)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>\u003Cstrong>退化问题\u003C\u002Fstrong>：按有序序列插入时，BST 退化为链表，所有操作变 O(n)。这是 AVL 树和红黑树的动机。\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-cpp\">struct TreeNode {\n    int val;\n    TreeNode* left = nullptr;\n    TreeNode* right = nullptr;\n    TreeNode(int v) : val(v) {}\n};\n\n\u002F\u002F BST 插入\nTreeNode* insert(TreeNode* root, int val) {\n    if (!root) return new TreeNode(val);\n    if (val &lt; root-&gt;val)\n        root-&gt;left = insert(root-&gt;left, val);\n    else if (val &gt; root-&gt;val)\n        root-&gt;right = insert(root-&gt;right, val);\n    return root;\n}\n\n\u002F\u002F 中序遍历（输出有序序列）\nvoid inorder(TreeNode* root, std::vector&lt;int&gt;&amp; result) {\n    if (!root) return;\n    inorder(root-&gt;left, result);\n    result.push_back(root-&gt;val);\n    inorder(root-&gt;right, result);\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Chr>\n\u003Ch2 id=\"avl-树-vs-红黑树\">AVL 树 vs 红黑树\u003C\u002Fh2>\n\u003Cp>两者都是\u003Cstrong>自平衡 BST\u003C\u002Fstrong>，保证树高 O(log n)，但平衡策略不同：\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>特性\u003C\u002Fth>\n\u003Cth>AVL 树\u003C\u002Fth>\n\u003Cth>红黑树\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>平衡条件\u003C\u002Ftd>\n\u003Ctd>左右子树高度差 ≤ 1（严格）\u003C\u002Ftd>\n\u003Ctd>5 条颜色规则（宽松）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>查找性能\u003C\u002Ftd>\n\u003Ctd>略优（树更矮）\u003C\u002Ftd>\n\u003Ctd>略差\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>插入 \u002F 删除旋转次数\u003C\u002Ftd>\n\u003Ctd>多\u003C\u002Ftd>\n\u003Ctd>少（最多 3 次旋转）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>适合场景\u003C\u002Ftd>\n\u003Ctd>读多写少\u003C\u002Ftd>\n\u003Ctd>读写均衡\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>\u003Cstrong>为什么 \u003Ccode>std::map\u003C\u002Fcode> \u002F \u003Ccode>std::set\u003C\u002Fcode> 用红黑树\u003C\u002Fstrong>：\n标准库容器需要同时支持高效插入、删除、查找。红黑树的插入\u002F删除旋转次数有严格上界（插入最多 2 次，删除最多 3 次），而 AVL 树在删除时可能需要 O(log n) 次旋转。对于通用容器，红黑树在修改操作上更稳定。\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-cpp\">#include &lt;map&gt;\n#include &lt;set&gt;\n\n\u002F\u002F std::map 底层红黑树，有序，O(log n) 操作\nstd::map&lt;std::string, int&gt; scores;\nscores[&quot;Alice&quot;] = 95;\nscores[&quot;Bob&quot;] = 87;\n\n\u002F\u002F 范围查询（利用有序性）\nauto it = scores.lower_bound(&quot;Alice&quot;);  \u002F\u002F &gt;= &quot;Alice&quot; 的第一个\n\n\u002F\u002F std::set：唯一有序集合\nstd::set&lt;int&gt; nums = {3, 1, 4, 1, 5, 9};  \u002F\u002F 自动去重并排序\n\u002F\u002F 结果：{1, 3, 4, 5, 9}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Chr>\n\u003Ch2 id=\"堆-heap\">堆 Heap\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>核心思路\u003C\u002Fstrong>：完全二叉树，用数组存储（节点 i 的左孩子为 2i+1，右孩子为 2i+2）。最大堆保证父节点 ≥ 子节点，根节点是最大值。\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>操作\u003C\u002Fth>\n\u003Cth>时间复杂度\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>插入（push）\u003C\u002Ftd>\n\u003Ctd>O(log n)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>删除最大值（pop）\u003C\u002Ftd>\n\u003Ctd>O(log n)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>查看最大值（peek）\u003C\u002Ftd>\n\u003Ctd>O(1)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>建堆（heapify）\u003C\u002Ftd>\n\u003Ctd>O(n)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>\u003Cstrong>Heapify O(n) 建堆原理\u003C\u002Fstrong>：从最后一个非叶节点向上 sift-down，大部分节点在树的底部高度小，总代价收敛到 O(n)（而非 n 次 O(log n) 插入的 O(n log n)）。\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-cpp\">#include &lt;queue&gt;\n#include &lt;vector&gt;\n#include &lt;functional&gt;  \u002F\u002F std::greater\n\n\u002F\u002F 最大堆（默认）\nstd::priority_queue&lt;int&gt; maxHeap;\nmaxHeap.push(3);\nmaxHeap.push(1);\nmaxHeap.push(4);\nint top = maxHeap.top();  \u002F\u002F 4\nmaxHeap.pop();\n\n\u002F\u002F 最小堆\nstd::priority_queue&lt;int, std::vector&lt;int&gt;, std::greater&lt;int&gt;&gt; minHeap;\n\n\u002F\u002F 自定义比较（按第二元素排序）\nusing P = std::pair&lt;int, int&gt;;\nauto cmp = [](const P&amp; a, const P&amp; b) { return a.second &gt; b.second; };\nstd::priority_queue&lt;P, std::vector&lt;P&gt;, decltype(cmp)&gt; pq(cmp);\n\n\u002F\u002F 手动 heapify（从无序数组 O(n) 建堆）\nstd::vector&lt;int&gt; arr = {3, 1, 4, 1, 5, 9, 2, 6};\nstd::make_heap(arr.begin(), arr.end());  \u002F\u002F 最大堆，O(n)\nstd::sort_heap(arr.begin(), arr.end()); \u002F\u002F 堆排序，O(n log n)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>适用场景\u003C\u002Fstrong>：Top-K 问题、Dijkstra 最短路、任务调度（优先级队列）、中位数维护（双堆）。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"图-graph\">图 Graph\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>核心思路\u003C\u002Fstrong>：节点（Vertex）+ 边（Edge）。存储方式影响空间和遍历效率：\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>存储方式\u003C\u002Fth>\n\u003Cth>空间\u003C\u002Fth>\n\u003Cth>查询边是否存在\u003C\u002Fth>\n\u003Cth>遍历邻居\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>邻接矩阵\u003C\u002Ftd>\n\u003Ctd>O(V²)\u003C\u002Ftd>\n\u003Ctd>O(1)\u003C\u002Ftd>\n\u003Ctd>O(V)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>邻接表\u003C\u002Ftd>\n\u003Ctd>O(V+E)\u003C\u002Ftd>\n\u003Ctd>O(degree)\u003C\u002Ftd>\n\u003Ctd>O(degree)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>\u003Cstrong>选择原则\u003C\u002Fstrong>：稠密图（E 接近 V²）用矩阵；稀疏图（E 远小于 V²，如路网、社交网络）用邻接表。\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-cpp\">#include &lt;vector&gt;\n#include &lt;queue&gt;\n#include &lt;stack&gt;\n\n\u002F\u002F 邻接表建图\nint V = 6;\nstd::vector&lt;std::vector&lt;int&gt;&gt; adj(V);\nauto addEdge = [&amp;](int u, int v) {\n    adj[u].push_back(v);\n    adj[v].push_back(u);  \u002F\u002F 无向图\n};\n\n\u002F\u002F BFS：最短路径（无权图）\nstd::vector&lt;int&gt; bfsShortestPath(int src, int dst) {\n    std::vector&lt;int&gt; dist(V, -1);\n    std::vector&lt;int&gt; parent(V, -1);\n    std::queue&lt;int&gt; q;\n\n    dist[src] = 0;\n    q.push(src);\n\n    while (!q.empty()) {\n        int u = q.front(); q.pop();\n        for (int v : adj[u]) {\n            if (dist[v] == -1) {\n                dist[v] = dist[u] + 1;\n                parent[v] = u;\n                q.push(v);\n            }\n        }\n    }\n\n    \u002F\u002F 回溯路径\n    std::vector&lt;int&gt; path;\n    for (int v = dst; v != -1; v = parent[v])\n        path.push_back(v);\n    std::reverse(path.begin(), path.end());\n    return path;\n}\n\n\u002F\u002F DFS：迭代实现（避免递归栈溢出）\nvoid dfsIterative(int start) {\n    std::vector&lt;bool&gt; visited(V, false);\n    std::stack&lt;int&gt; st;\n\n    st.push(start);\n    while (!st.empty()) {\n        int u = st.top(); st.pop();\n        if (visited[u]) continue;\n        visited[u] = true;\n        \u002F\u002F 处理 u\n        for (int v : adj[u]) {\n            if (!visited[v]) st.push(v);\n        }\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>适用场景\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>BFS：最短路径（无权）、层序遍历、连通分量\u003C\u002Fli>\n\u003Cli>DFS：拓扑排序、强连通分量、迷宫\u002F路径搜索\u003C\u002Fli>\n\u003Cli>带权图：Dijkstra（非负权）、Bellman-Ford（含负权）\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Chr>\n\u003Ch2 id=\"选型速查表\">选型速查表\u003C\u002Fh2>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>场景\u003C\u002Fth>\n\u003Cth>推荐数据结构\u003C\u002Fth>\n\u003Cth>原因\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>频繁随机访问，顺序遍历\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>std::vector\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>cache 友好，O(1) 随机访问\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>频繁中间插入\u002F删除，持有迭代器\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>std::list\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>O(1) 插删（已知位置）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>LIFO、表达式解析、DFS\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>std::stack\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>语义清晰，底层 deque\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>BFS、任务队列\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>std::queue\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>FIFO 语义\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>O(1) 查找，无序\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>std::unordered_map\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>哈希 O(1) 均摊\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>有序遍历，范围查询\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>std::map\u003C\u002Fcode> \u002F \u003Ccode>std::set\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>红黑树，有序 O(log n)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Top-K，优先调度\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>std::priority_queue\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>堆，O(log n) push\u002Fpop\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>最短路径，连通性\u003C\u002Ftd>\n\u003Ctd>邻接表 + BFS\u002FDijkstra\u003C\u002Ftd>\n\u003Ctd>稀疏图 O(V+E)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>数学矩阵，稠密图\u003C\u002Ftd>\n\u003Ctd>二维 \u003Ccode>std::vector\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>邻接矩阵 O(1) 查边\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>区间查询，前缀和\u003C\u002Ftd>\n\u003Ctd>Segment Tree \u002F BIT\u003C\u002Ftd>\n\u003Ctd>O(log n) 区间操作\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Chr>\n\u003Cp>理解了这些结构的本质，再看算法题或工程代码，会有一种&quot;看穿骨架&quot;的感觉——不是在背答案，是在做选择。\u003C\u002Fp>\n","2026-05-01",[11,12,13,14],"数据结构","算法","cpp","基础",false,[17,30,41,53,63,70,77,84,91,98,108,117,127,136,142,149,158,167,176,186,192,201,204,211,217,226,233,240,248,258,267,276,286,296,306,314,324,335,345,354,362,368,376,384,392,400,408,415],{"slug":18,"title":19,"description":20,"pub_date":21,"tags":22,"draft":15,"word_count":29},"ide-skills-guide","Agent Skills 完全指南：21 款第三方 Skill 深度评测与使用心得","全面评测 21 款第三方 Agent Skills，涵盖 Vue 生态、前端设计、构建工具、实用工具四大分类。从安装配置到实际使用场景，带你了解每个 Skill 的功能特点、最佳实践与使用心得。","2026-06-15",[23,24,25,26,27,28],"agent","skills","AI","效率工具","前端","Vue",4169,{"slug":31,"title":32,"description":33,"pub_date":34,"tags":35,"draft":15,"word_count":40},"linux-kernel-skeleton-struct-funcptr-container_of","Linux 内核骨架：struct、函数指针与 container_of","读懂 Linux 内核源码的三件套：巨大的 struct 组合代替继承、函数指针表实现虚派发、container_of 宏从嵌入成员找回完整对象。","2026-05-09",[36,37,38,39],"linux","kernel","C","container_of",1369,{"slug":42,"title":43,"description":44,"pub_date":45,"tags":46,"draft":15,"word_count":52},"astro-complete-guide-2025","Astro 5 深度剖析：Islands 架构原理、构建优化与 Cloudflare Workers 边缘部署","从编译器视角解析 Astro 5 的 Islands 架构实现原理，Content Layer API 的 Vite 插件机制，Server Islands 的流式渲染，以及如何在 Cloudflare Workers + D1 边缘环境下榨干性能。","2026-05-08",[47,48,49,50,51],"astro","frontend","cloudflare","performance","architecture",3663,{"slug":54,"title":55,"description":56,"pub_date":57,"tags":58,"draft":15,"word_count":62},"llm-prompt-engineering","Prompt Engineering 实战：让 LLM 真正听话的技巧","System prompt 怎么写、Few-shot 怎么设计、Chain-of-Thought 原理，以及常见失败模式和调试方法。","2026-05-03",[59,60,61],"ai","llm","工程实践",1723,{"slug":64,"title":65,"description":66,"pub_date":57,"tags":67,"draft":15,"word_count":69},"rag-system-design","RAG 系统设计：从 naive 到 production-ready","Retrieval-Augmented Generation 不只是「向量数据库 + LLM」，分块策略、召回质量、重排序、缓存才是工程核心。",[59,68,60,61],"rag",1613,{"slug":71,"title":72,"description":73,"pub_date":57,"tags":74,"draft":15,"word_count":76},"git-advanced-workflow","Git 进阶工作流：rebase、cherry-pick、bisect 的正确使用","merge 会了，但 rebase 总搞错？bisect 找 bug 提交？interactive rebase 整理历史？这篇一次说清楚。",[75,61],"git",1396,{"slug":78,"title":79,"description":80,"pub_date":57,"tags":81,"draft":15,"word_count":83},"docker-practical-guide","Docker 实战：从会用到用好","会 docker run 不够，Dockerfile 最佳实践、多阶段构建、Compose 编排、镜像瘦身才是日常真正需要的。",[82,36,61],"docker",1268,{"slug":85,"title":86,"description":87,"pub_date":57,"tags":88,"draft":15,"word_count":90},"anthropics-skills-guide","anthropics\u002Fskills：Anthropic 官方 Agent Skills 仓库解析","Anthropic 官方开源的 Agent Skills 标准仓库，127k stars，解析 SKILL.md 规范、17 个示例 skill 的设计模式，以及如何在 Claude Code \u002F Claude.ai \u002F API 中使用",[59,89,23,24],"Claude",2090,{"slug":92,"title":93,"description":94,"pub_date":57,"tags":95,"draft":15,"word_count":97},"karpathy-claude-code-guidelines","Karpathy 的 LLM 编码批评与 CLAUDE.md 最佳实践","基于 Andrej Karpathy 对 LLM 编程助手的观察，forrestchang 提炼出一个 CLAUDE.md 文件，4 条原则解决 AI 编码的典型失控问题：乱猜假设、过度设计、乱改代码、目标不清",[59,89,96,61],"Claude Code",2699,{"slug":99,"title":100,"description":101,"pub_date":57,"tags":102,"draft":15,"word_count":107},"typescript-advanced-patterns","TypeScript 高级模式：让类型系统为你工作","基础 TS 会了但类型总是 any？条件类型、映射类型、模板字面量类型、infer 关键字才是 TS 的真正威力。",[103,104,105,106],"typescript","类型系统","前端工程","高级模式",1419,{"slug":109,"title":110,"description":111,"pub_date":57,"tags":112,"draft":15,"word_count":116},"linux-performance-tuning","Linux 性能调优实战：从 top 到 perf 的完整工具链","遇到性能问题不知道从哪下手？这篇建立系统化的排查思路，从 CPU\u002F内存\u002FIO\u002F网络逐层分析。",[36,113,114,115],"性能","运维","系统编程",1524,{"slug":118,"title":119,"description":120,"pub_date":57,"tags":121,"draft":15,"word_count":126},"python-functional-programming","Python 函数式编程：map\u002Ffilter\u002Freduce 之外","Python 不是纯函数式语言，但 functools、itertools、偏函数、闭包这些工具用好了能让代码简洁一个量级。",[122,123,124,125],"python","函数式","闭包","装饰器",1867,{"slug":128,"title":129,"description":130,"pub_date":57,"tags":131,"draft":15,"word_count":135},"python-oop-guide","Python 面向对象：__init__ 之外你需要知道的","Python OOP 不只是 class + __init__，魔术方法、描述符、元类才是真正的武器。",[122,132,133,134],"OOP","面向对象","魔术方法",1792,{"slug":137,"title":138,"description":139,"pub_date":57,"tags":140,"draft":15,"word_count":141},"python-data-structures","Python 内置数据结构深度解析","list、dict、set、tuple 不只是数据容器，搞懂它们的底层实现和时间复杂度，才能写出高性能 Python。",[122,11,113,12],1517,{"slug":143,"title":144,"description":145,"pub_date":57,"tags":146,"draft":15,"word_count":148},"python-basics-quick-start","Python 快速上手：写给有编程基础的人","已经会其他语言，想快速掌握 Python 的语法特性和思维方式，这篇是捷径。",[122,147,14],"入门",1607,{"slug":150,"title":151,"description":152,"pub_date":57,"tags":153,"draft":15,"word_count":157},"python-dataclass-pydantic","Python dataclass vs Pydantic：数据类选型指南","dataclass 是标准库的轻量选择，Pydantic v2 是带验证的重武器，什么时候用哪个，这篇说清楚。",[122,154,155,156],"dataclass","pydantic","数据验证",1323,{"slug":159,"title":160,"description":161,"pub_date":57,"tags":162,"draft":15,"word_count":166},"python-asyncio-practical","Python asyncio 实战：从回调地狱到协程优雅","asyncio 是 Python 异步编程的核心，搞懂 event loop、Task、gather 这些概念才能写出真正高效的异步代码。",[122,163,164,165],"asyncio","并发","网络编程",1258,{"slug":168,"title":169,"description":170,"pub_date":57,"tags":171,"draft":15,"word_count":175},"python-type-hints-guide","Python 类型注解完全指南：从入门到实践","Python 3.5+ 引入类型注解，配合 mypy\u002Fpyright 让 Python 也能享受静态类型检查的好处。",[122,172,173,174],"typescript-style","type-hints","工具链",1102,{"slug":177,"title":178,"description":179,"pub_date":180,"tags":181,"draft":15,"word_count":185},"pwa-install-update-button","PWA 踩坑：为什么安装按钮从来不出现","从 beforeinstallprompt 到 Service Worker waiting，把 PWA 的安装与更新提示真正做对","2026-05-02",[182,183,184],"pwa","javascript","web",1683,{"slug":187,"title":188,"description":189,"pub_date":9,"tags":190,"draft":15,"word_count":191},"openclaw-vs-hermes-agent","OpenClaw vs Hermes Agent：两个本地优先 Agent 的设计差异","OpenClaw（Novita AI）和 Hermes Agent（Nous Research）都是本地运行的个人 AI Agent，但在记忆系统、技能学习、运行环境和模型生态上走了不同的路。深入对比两种架构的核心差异。",[59,23,60],1679,{"slug":193,"title":194,"description":195,"pub_date":9,"tags":196,"draft":15,"word_count":200},"cpp-random-design-patterns","C++ 设计模式实战：RAII、观察者、工厂","用现代 C++（C++17\u002F20）实现三种高频设计模式：RAII 资源管理、观察者模式事件系统、工厂模式插件架构。每种模式给出问题场景、实现代码和真实工程案例。",[13,197,198,199],"设计模式","c++17","工程",2613,{"slug":4,"title":5,"description":6,"pub_date":9,"tags":202,"draft":15,"word_count":203},[11,12,13,14],3004,{"slug":205,"title":206,"description":207,"pub_date":208,"tags":209,"draft":15,"word_count":210},"ai-agent-what-is","什么是 AI Agent？从 LLM 到自主执行","LLM 本身是无状态问答机，Agent 是什么让它’动’起来的？本文深入解析 Agent 的四个核心能力、ReAct 框架、工具调用原理，以及主流框架横向对比。","2026-04-30",[59,23,60],2116,{"slug":212,"title":213,"description":214,"pub_date":208,"tags":215,"draft":15,"word_count":216},"ai-agent-memory","AI Agent 的记忆系统：从上下文窗口到长期记忆","深入拆解 AI Agent 的四种记忆类型、上下文窗口压缩策略、RAG 向量检索原理，以及三种典型失败模式和工程选型建议。",[59,23,68],2052,{"slug":218,"title":219,"description":220,"pub_date":208,"tags":221,"draft":15,"word_count":225},"network-proxy-vpn-guide","代理与翻墙技术原理：从 HTTP 代理到现代协议","深入解析代理与 VPN 的本质区别，梳理从 SOCKS5 到 Shadowsocks、V2Ray\u002FXray、Hysteria2 的协议演进，以及机场订阅的技术本质。",[222,223,224],"网络","代理","协议",2148,{"slug":227,"title":228,"description":229,"pub_date":208,"tags":230,"draft":15,"word_count":148},"algorithm-binary-search","二分查找：永远写不对？记住这个模板","彻底搞清楚二分查找的边界问题：闭区间和左闭右开两套模板、三道经典 LeetCode 题目完整 C++ 实现，以及二分答案的进阶思路。",[12,231,232,13],"二分查找","leetcode",{"slug":234,"title":235,"description":236,"pub_date":208,"tags":237,"draft":15,"word_count":239},"algorithm-sliding-window","滑动窗口算法：从暴力到 O(n) 的思维跃迁","系统讲解滑动窗口算法的核心模板、适用题型，配合三道经典 LeetCode 题目的完整 C++ 实现，彻底理解双指针收缩思路。",[12,238,232,13],"滑动窗口",1943,{"slug":241,"title":242,"description":243,"pub_date":208,"tags":244,"draft":15,"word_count":247},"network-clash-config","Clash \u002F Mihomo 配置详解：规则、策略组与分流","深入解析 Clash\u002FMihomo 的核心配置结构，包括代理节点、策略组类型、规则优先级、DNS fake-ip 模式，以及一份实用的完整配置模板。",[222,245,223,246],"clash","配置",1292,{"slug":249,"title":250,"description":251,"pub_date":252,"tags":253,"draft":15,"word_count":257},"hid-hotplug","HID 设备热插拔检测：从 udev 到 node-hid","在 Linux 上用 node-hid + usb 库实现可靠的 USB HID 设备热插拔检测，踩坑记录","2026-04-28",[13,254,36,255,256],"hid","nodejs","electron",2039,{"slug":259,"title":260,"description":261,"pub_date":262,"tags":263,"draft":15,"word_count":266},"electron-ipc-types","Electron IPC 类型安全：从 any 到完全类型化","用 TypeScript 泛型封装 Electron IPC，彻底消灭 any，preload 契约集中管理","2026-04-25",[256,103,264,265],"ipc","vue",1446,{"slug":268,"title":269,"description":270,"pub_date":271,"tags":272,"draft":15,"word_count":275},"element-plus-popover-hide","手动关闭多个 el-popover（不用 v-model:visible）","通过 ref + Reflect.get 调用 hide() 方法手动关闭 Element Plus Popover，解释 Vue3 Proxy 导致无法直接调用实例方法的原因。","2024-10-25",[265,273,274],"element-plus","vue3",1321,{"slug":277,"title":278,"description":279,"pub_date":280,"tags":281,"draft":15,"word_count":285},"vite-vue3-ts-elementplus-pinia","用 Vite+（vp）从零搭建 Vue3 + TypeScript + Element Plus + Pinia + Vue Router","使用 Vite+ 统一工具链（vp）一条命令搭建 Vue3 全家桶，涵盖按需导入、Pinia store、路由配置，以及常见坑的解决方案。","2024-08-27",[265,282,103,273,283,284],"vite","pinia","vite-plus",1960,{"slug":287,"title":288,"description":289,"pub_date":290,"tags":291,"draft":15,"word_count":295},"cef-lnk2038-iterator-debug-level","CEF LNK2038：解决 _ITERATOR_DEBUG_LEVEL 不匹配错误","分析 CEF（Chromium Embedded Framework）集成时出现的 LNK2038 _ITERATOR_DEBUG_LEVEL 链接错误，从根本原因到解决方案的完整指南。","2024-05-07",[13,292,293,294],"CEF","Visual Studio","链接错误",1509,{"slug":297,"title":298,"description":299,"pub_date":300,"tags":301,"draft":15,"word_count":305},"npm-electron-install-fix","彻底解决 npm 安装 Electron 失败的问题","分析 npm install electron 失败的根本原因（下载二进制超时\u002F被墙），通过国内镜像（npmmirror）彻底解决，并介绍多种备选方案和常见错误排查。","2024-03-01",[256,302,303,304],"npm","前端工具链","国内镜像",1494,{"slug":307,"title":308,"description":309,"pub_date":310,"tags":311,"draft":15,"word_count":313},"git-out-of-memory","解决 git 报错：Fatal: Out of memory, malloc failed","分析 git 大仓库操作时出现 Out of memory malloc failed 的根本原因，通过调整 pack.windowMemory、http.postBuffer 和 git repack 彻底解决。","2024-01-31",[75,36,312],"工具",2244,{"slug":315,"title":316,"description":317,"pub_date":318,"tags":319,"draft":15,"word_count":323},"vmware-tools-install","在 VMware 虚拟机中安装 open-vm-tools 完整指南","详解 VMware Tools 的作用、open-vm-tools 与官方 VMware Tools 的区别，以及在 Ubuntu 虚拟机中安装并生效的完整步骤和常见问题排查。","2023-11-21",[320,36,321,322],"VMware","Ubuntu","虚拟机",2523,{"slug":325,"title":326,"description":327,"pub_date":328,"tags":329,"draft":15,"word_count":334},"load-balancing-algorithms","负载均衡算法完全指南：从轮询到一致性哈希","系统梳理静态与动态负载均衡算法，涵盖轮询、随机、权重、IP Hash、一致性 Hash、最少连接、最快响应等，并对比 Nginx、Dubbo、Spring Cloud LoadBalancer 的实现差异。","2023-11-15",[330,331,332,333],"分布式","负载均衡","Nginx","微服务",1764,{"slug":336,"title":337,"description":338,"pub_date":339,"tags":340,"draft":15,"word_count":344},"win-cw2a-ca2w","ATL 字符串转换：CW2A 与 CA2W 完全指南","详解 ATL 宏 CW2A\u002FCA2W 在 Unicode 与 ANSI 之间的字符串转换用法、头文件依赖、USES_CONVERSION 宏的作用与常见陷阱。","2023-06-09",[13,341,342,343],"windows","ATL","字符串",1665,{"slug":346,"title":347,"description":348,"pub_date":339,"tags":349,"draft":15,"word_count":353},"csharp-sendmessage-cpp","C# 通过 SendMessage 向 C++ 窗口发送消息与字符串","使用 P\u002FInvoke 调用 user32.dll 的 SendMessage，从 C# 发送自定义 WM_USER 消息及字符串指针给 C++ 原生窗口，并在 C++ 侧正确接收和转换。",[350,13,341,351,352],"C#","互操作","PInvoke",1554,{"slug":355,"title":356,"description":357,"pub_date":358,"tags":359,"draft":15,"word_count":361},"win-postmessage-vector","Windows PostMessage 跨线程传递 std::vector 指针","通过 PostMessage 在 Windows 消息队列中传递 std::vector 指针，使用 reinterpret_cast 将指针装入 LPARAM，并在接收方正确释放内存。","2023-05-26",[13,341,360],"WinAPI",1823,{"slug":363,"title":364,"description":365,"pub_date":358,"tags":366,"draft":15,"word_count":367},"exe-dll-single-package","将 EXE 和 DLL 打包成单一可执行文件","介绍两种将 exe 和依赖 dll 打包成单文件的方案：Enigma Virtual Box 和 WinRAR 自解压，适合发布 Windows 桌面程序时简化分发流程。",[341,13,312],1619,{"slug":369,"title":370,"description":371,"pub_date":358,"tags":372,"draft":15,"word_count":375},"cpp-random-mt19937","C++ 现代随机数生成：用 mt19937 彻底告别 rand()","深入讲解为什么 rand() 不够用，以及如何用 C++11 的 \u003Crandom> 库正确生成高质量随机数，涵盖 mt19937、各种分布和线程安全。",[13,373,374],"c++11","random",1549,{"slug":377,"title":378,"description":379,"pub_date":380,"tags":381,"draft":15,"word_count":383},"win-startup-registry","C++ 实现程序开机自启动：注册表方式详解","通过操作 Windows 注册表 Run 键实现程序开机自启动，包括 HKCU 与 HKLM 区别、完整封装代码、工作目录问题和 UAC 权限处理。","2022-12-26",[341,13,382],"registry",1201,{"slug":385,"title":386,"description":387,"pub_date":388,"tags":389,"draft":15,"word_count":391},"mfc-cstring-wparam","MFC 中 CString 与 WPARAM 之间的转换","详解 MFC 消息传递中 CString 无法直接强转为 WPARAM 的原因，以及两种正确的转换方案，并介绍结构体指针传递的正确姿势。","2022-11-25",[390,13,341],"mfc",1546,{"slug":393,"title":394,"description":395,"pub_date":396,"tags":397,"draft":15,"word_count":399},"duilib-static-build","正确编译 Duilib 静态库：避免 ATL 依赖和链接错误","详解如何用 DuiLib_Static.vcxproj 编译 Duilib 静态库，解决 VARIANT 未定义、Unicode 配置不匹配和 ATL 依赖等常见问题。","2022-08-24",[13,398,341,390],"duilib",2639,{"slug":401,"title":402,"description":403,"pub_date":404,"tags":405,"draft":15,"word_count":407},"mfc-dpi-adaptive","MFC 界面自适应不同分辨率","MFC 对话框程序实现控件和字体随分辨率自动缩放的完整方案，附 DPI Awareness 配置说明","2022-08-17",[390,13,341,406],"dpi",1414,{"slug":409,"title":410,"description":411,"pub_date":412,"tags":413,"draft":15,"word_count":414},"mfc-drag-window","MFC 无标题栏窗口客户区拖动：三种方法对比","MFC 对话框去掉标题栏后如何实现拖动移动窗口，三种方案完整实现与适用场景分析","2022-08-16",[390,13,341],1633,{"slug":416,"title":417,"description":418,"pub_date":419,"tags":420,"draft":15,"word_count":422},"algorithm-number-complement","整数的补数：位运算掩码解法","LeetCode 476 题，用掩码 XOR 实现整数补数，附 C++\u002FPython\u002FJava 三种实现及补数与补码的区别","2021-03-08",[12,421,232],"位运算",1374,[]]