Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ site/.vitepress/cache/
site/.vitepress/.build-cache/
site/.vitepress/.build-tmp/
site/.vitepress/.search-cache/
# animy_maker 每次编译会重新生成 posters/,但 SSG 首帧已接管其兜底职责(docs/09 §7)
site/.vitepress/theme/components/animations/posters/

# Book/PDF pipeline (generated locally or uploaded as CI/Release artifacts)
.pdf-build/
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
---

<!-- COVERAGE_START -->
![English Coverage](https://img.shields.io/badge/en_coverage-97%25-green.svg) 578/598 docs translated
![English Coverage](https://img.shields.io/badge/en_coverage-96%25-green.svg) 578/601 docs translated
<!-- COVERAGE_END -->

## 嘿!这是什么?
Expand Down
24 changes: 24 additions & 0 deletions code/examples/vol3/primer_01_dangling_pointer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// primer_01_dangling_pointer.cpp
// 扩容后旧指针悬空:先看两个地址分道扬镳,再看 UB 是死是活

#include <cstdio>
#include <vector>

int main() {
std::vector<int> v;
v.reserve(2);
v.push_back(41);
v.push_back(1);

int* p = &v[0]; // 缓存一个指针
std::printf("before: p = %p, v.data() = %p\n", static_cast<void*>(p),
static_cast<void*>(v.data()));

v.push_back(99); // 容量已满,触发扩容(缓冲整体搬迁)
std::printf("after : p = %p, v.data() = %p\n", static_cast<void*>(p),
static_cast<void*>(v.data()));

*p = 100; // 往旧地址写:未定义行为
std::printf("*p = %d (this write went to the OLD buffer)\n", *p);
return 0;
}
22 changes: 22 additions & 0 deletions code/examples/vol3/primer_01_vector_growth.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// primer_01_vector_growth.cpp
// 观察 std::vector 的扩容:容量序列 + 缓冲地址变化(搬家现场)

#include <cstdio>
#include <vector>

int main() {
std::vector<int> v;
std::size_t last_cap = v.capacity();
std::printf("born : size=%zu capacity=%zu data=%p\n", v.size(), v.capacity(),
static_cast<void*>(v.data()));

for (int i = 0; i < 40; ++i) {
v.push_back(i);
if (v.capacity() != last_cap) {
std::printf("push #%-2d: size=%zu capacity=%zu data=%p\n", i + 1, v.size(),
v.capacity(), static_cast<void*>(v.data()));
last_cap = v.capacity();
}
}
return 0;
}
29 changes: 29 additions & 0 deletions code/examples/vol3/primer_02_address_stability.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// primer_02_address_stability.cpp
// 链表从不搬家:删掉中间一个节点,其余节点的地址一位都不动

#include <cstdio>
#include <list>

int main() {
std::list<int> L{0, 1, 2, 3, 4, 5, 6, 7};

auto dump = [&L](const char* tag) {
std::printf("%s:\n", tag);
int i = 0;
for (int& x : L) {
std::printf(" [%d] addr=%p val=%d\n", i++, static_cast<void*>(&x), x);
}
};

dump("before erase");

for (auto it = L.begin(); it != L.end(); ++it) {
if (*it == 4) {
L.erase(it); // 只解除这一个节点,其余节点不动
break;
}
}

dump("after erase (node holding 4 removed)");
return 0;
}
36 changes: 36 additions & 0 deletions code/examples/vol3/primer_02_node_chain.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// primer_02_node_chain.cpp
// 手工串一条链:三个节点 + 头插一个 99 + 遍历打印 + 逐个释放

#include <cstdio>

struct Node {
int val;
Node* next;
};

void push_front(Node*& head, int x) {
Node* n = new Node{x, head}; // 新节点的 next 指向原来的头节点
head = n; // head 改指新节点
}

int main() {
Node* c = new Node{30, nullptr}; // 链尾:next 存 nullptr
Node* b = new Node{20, c}; // 中间节点:next 指向 c
Node* a = new Node{10, b}; // 头节点:next 指向 b
Node* head = a; // 入口指向头节点

push_front(head, 99);

for (Node* cur = head; cur != nullptr; cur = cur->next) {
std::printf("%d -> ", cur->val);
}
std::printf("nullptr\n");

Node* cur = head; // 逐个释放节点,防止泄漏
while (cur != nullptr) {
Node* nxt = cur->next;
delete cur;
cur = nxt;
}
return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ flowchart LR

## 扩容这件事:说是摊还常数,单次可能是 O(n)

那往一个 `capacity` 已经塞满的 `vector` 里再 `push_back` 会怎样?会触发一次 *reallocation*——申请新缓冲、把旧元素搬过去、释放旧缓冲。标准对这一步的承诺是 `push_back` 的**摊还常数复杂度**,请各位务必咬住"摊还"这两个字,它不是"常数"。
那往一个 `capacity` 已经塞满的 `vector` 里再 `push_back` 会怎样?会触发一次 *reallocation*——申请新缓冲、把旧元素搬过去、释放旧缓冲。标准对这一步的承诺是 `push_back` 的**摊还常数复杂度**,请各位务必记住"摊还"这两个字,它不是"常数"。

这话太容易被读成"每次 `push_back` 都是 O(1)",于是一些朋友就放心地往热循环里塞 `push_back`,结果某一次扩容直接是一次 O(n) 的搬家,性能曲线上"咔"地掉一个尖峰。摊还分析为什么能成立?关键就在于每次扩容时,容量是按一个大于 1 的几何倍数往上翻的,于是那一次昂贵的搬家费用,就被摊到了之前若干次便宜的 `push_back` 头上。

Expand Down
3 changes: 2 additions & 1 deletion documents/vol3-standard-library/containers/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ sidebar_order: 10

# 容器与数据结构

存放和管理数据的家伙们。这一组把每个容器的内部表示、扩容与迭代器失效规则、复杂度代价讲透,并在「容器选择指南」里给出按操作挑选容器的决策路径。
存放和管理数据的家伙们。这一组把每个容器的内部表示、扩容与迭代器失效规则、复杂度代价讲透,并在「容器选择指南」里给出按操作挑选容器的决策路径。还没摸过动态数组、哈希表这些结构本身的读者,先走一遍「数据结构 primer」子系列,地基打好再上来。

<ChapterNav variant="sub">
<ChapterLink href="primer/">数据结构 primer:容器底下的结构</ChapterLink>
<ChapterLink href="01-container-selection-guide">容器选择指南</ChapterLink>
<ChapterLink href="02-array">array:定长数组</ChapterLink>
<ChapterLink href="03-vector-deep-dive">vector 深入</ChapterLink>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<mxfile host="app.diagrams.net">
<diagram id="amortized-cost" name="页面-1">
<mxGraphModel dx="900" dy="700" grid="1" gridSize="10" page="1" pageScale="1" pageWidth="900" pageHeight="500" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="title" value="翻倍的摊还:绝大多数写入便宜,偶尔贵一次" style="text;html=1;fontSize=20;fontStyle=1" vertex="1" parent="1">
<mxGeometry x="40" y="16" width="640" height="30" as="geometry"/>
</mxCell>
<mxCell id="yaxis" value="" style="endArrow=classic;html=1;strokeWidth=1.5;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="70" y="400" as="sourcePoint"/>
<mxPoint x="70" y="90" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="xaxis" value="" style="endArrow=classic;html=1;strokeWidth=1.5;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="70" y="400" as="sourcePoint"/>
<mxPoint x="840" y="400" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="ylab" value="单次 push_back 的代价(搬走的元素数)" style="text;html=1;fontSize=12;" vertex="1" parent="1">
<mxGeometry x="20" y="70" width="280" height="20" as="geometry"/>
</mxCell>
<mxCell id="xlab" value="第 N 次 push_back(容量从 1 开始翻倍)" style="text;html=1;fontSize=12;align=center;" vertex="1" parent="1">
<mxGeometry x="300" y="420" width="400" height="20" as="geometry"/>
</mxCell>
<mxCell id="b1" value="1" style="rounded=0;html=1;fontSize=11;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="1">
<mxGeometry x="90" y="382" width="26" height="18" as="geometry"/>
</mxCell>
<mxCell id="b2" value="2" style="rounded=0;html=1;fontSize=11;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="1">
<mxGeometry x="128" y="364" width="26" height="36" as="geometry"/>
</mxCell>
<mxCell id="b3" style="rounded=0;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="166" y="382" width="26" height="18" as="geometry"/>
</mxCell>
<mxCell id="b4" value="4" style="rounded=0;html=1;fontSize=11;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="1">
<mxGeometry x="204" y="328" width="26" height="72" as="geometry"/>
</mxCell>
<mxCell id="b5" style="rounded=0;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="242" y="382" width="26" height="18" as="geometry"/>
</mxCell>
<mxCell id="b6" style="rounded=0;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="280" y="382" width="26" height="18" as="geometry"/>
</mxCell>
<mxCell id="b7" style="rounded=0;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="318" y="382" width="26" height="18" as="geometry"/>
</mxCell>
<mxCell id="b8" value="8" style="rounded=0;html=1;fontSize=11;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="1">
<mxGeometry x="356" y="256" width="26" height="144" as="geometry"/>
</mxCell>
<mxCell id="b9" style="rounded=0;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="394" y="382" width="26" height="18" as="geometry"/>
</mxCell>
<mxCell id="b10" style="rounded=0;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="432" y="382" width="26" height="18" as="geometry"/>
</mxCell>
<mxCell id="b11" style="rounded=0;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="470" y="382" width="26" height="18" as="geometry"/>
</mxCell>
<mxCell id="b12" style="rounded=0;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="508" y="382" width="26" height="18" as="geometry"/>
</mxCell>
<mxCell id="b13" style="rounded=0;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="546" y="382" width="26" height="18" as="geometry"/>
</mxCell>
<mxCell id="b14" style="rounded=0;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="584" y="382" width="26" height="18" as="geometry"/>
</mxCell>
<mxCell id="b15" style="rounded=0;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="622" y="382" width="26" height="18" as="geometry"/>
</mxCell>
<mxCell id="b16" value="16" style="rounded=0;html=1;fontSize=11;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="1">
<mxGeometry x="660" y="112" width="26" height="288" as="geometry"/>
</mxCell>
<mxCell id="b17" style="rounded=0;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="698" y="382" width="26" height="18" as="geometry"/>
</mxCell>
<mxCell id="legendHi" value="容量边界的写入:搬走全部旧元素,O(n)" style="rounded=0;html=1;fontSize=12;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="1">
<mxGeometry x="560" y="150" width="280" height="30" as="geometry"/>
</mxCell>
<mxCell id="legendLo" value="其余写入:写一格就完,O(1)" style="rounded=0;html=1;fontSize=12;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="560" y="190" width="280" height="30" as="geometry"/>
</mxCell>
<mxCell id="note" value="高柱的间隔一次比一次远,平均高度被摊薄回常数(≈ 2 次元素写入 / 次)。" style="text;html=1;fontSize=13;whiteSpace=wrap;" vertex="1" parent="1">
<mxGeometry x="80" y="450" width="700" height="20" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
Loading
Loading