This commit is contained in:
krahets
2026-04-14 18:06:14 +08:00
parent 065a978848
commit e53a7f2498
93 changed files with 565 additions and 570 deletions
@@ -19,7 +19,7 @@ comments: true
Временное пространство можно дополнительно разделить на три части.
- **Временные данные**: используются для хранения различных констант, переменных, объектов и т.д., возникающих во время выполнения алгоритма.
- **Пространство кадров стека**: используется для хранения контекстных данных вызываемых функций. При каждом вызове функции система создает на вершине стека новый кадр; после возврата функции пространство этого кадра освобождается.
- **Пространство кадров стека**: используется для хранения контекстных данных вызываемых функций. При каждом вызове функции система создает на вершине стека новый кадр. После возврата функции пространство этого кадра освобождается.
- **Пространство инструкций**: используется для хранения скомпилированных инструкций программы и в реальном подсчете обычно не учитывается.
При анализе пространственной сложности программы **обычно учитываются временные данные, пространство стека и выходные данные**, как показано на рисунке 2-15.
@@ -376,7 +376,7 @@ comments: true
Рассмотрим следующий код. Понятие худшей пространственной сложности здесь имеет два значения.
1. **Ориентир на худшие входные данные**: когда $n < 10$ , пространственная сложность равна $O(1)$ ; но когда $n > 10$ , инициализированный массив `nums` занимает $O(n)$ пространства, поэтому худшая пространственная сложность равна $O(n)$ .
2. **Ориентир на пиковое использование памяти во время выполнения**: например, до выполнения последней строки программа занимает $O(1)$ пространства; при инициализации массива `nums` она занимает $O(n)$ пространства, поэтому худшая пространственная сложность также равна $O(n)$ .
2. **Ориентир на пиковое использование памяти во время выполнения**: например, до выполнения последней строки программа занимает $O(1)$ пространства. При инициализации массива `nums` она занимает $O(n)$ пространства, поэтому худшая пространственная сложность также равна $O(n)$ .
=== "Python"
@@ -803,7 +803,7 @@ comments: true
Функции `loop()` и `recur()` имеют временную сложность $O(n)$ , но их пространственная сложность различается.
- Функция `loop()` вызывает `function()` в цикле $n$ раз; на каждой итерации `function()` возвращается и освобождает пространство своего кадра стека, поэтому пространственная сложность по-прежнему равна $O(1)$ .
- Функция `loop()` вызывает `function()` в цикле $n$ раз. На каждой итерации `function()` возвращается и освобождает пространство своего кадра стека, поэтому пространственная сложность по-прежнему равна $O(1)$ .
- Рекурсивная функция `recur()` во время выполнения одновременно содержит $n$ еще не завершившихся экземпляров `recur()` , поэтому занимает $O(n)$ пространства кадров стека.
## 2.4.3 &nbsp; Распространенные типы
@@ -1865,7 +1865,7 @@ $$
<div style="height: 405px; width: 100%;"><iframe class="pythontutor-iframe" src="https://pythontutor.com/iframe-embed.html#code=def%20quadratic%28n%3A%20int%29%3A%0A%20%20%20%20%22%22%22%D0%9A%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%B8%D1%87%D0%BD%D0%B0%D1%8F%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C%22%22%22%0A%20%20%20%20%23%20%D0%94%D0%B2%D1%83%D0%BC%D0%B5%D1%80%D0%BD%D1%8B%D0%B9%20%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA%20%D0%B7%D0%B0%D0%BD%D0%B8%D0%BC%D0%B0%D0%B5%D1%82%20O%28n%5E2%29%20%D0%BF%D0%B0%D0%BC%D1%8F%D1%82%D0%B8%0A%20%20%20%20num_matrix%20%3D%20%5B%5B0%5D%20%2A%20n%20for%20_%20in%20range%28n%29%5D%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20print%28%22%D0%A0%D0%B0%D0%B7%D0%BC%D0%B5%D1%80%20%D0%B2%D1%85%D0%BE%D0%B4%D0%BD%D1%8B%D1%85%20%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%20n%20%3D%22%2C%20n%29%0A%0A%20%20%20%20%23%20%D0%9A%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%B8%D1%87%D0%BD%D0%B0%D1%8F%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C%0A%20%20%20%20quadratic%28n%29&codeDivHeight=472&codeDivWidth=350&cumulative=false&curInstr=16&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe></div>
<div style="margin-top: 5px;"><a href="https://pythontutor.com/iframe-embed.html#code=def%20quadratic%28n%3A%20int%29%3A%0A%20%20%20%20%22%22%22%D0%9A%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%B8%D1%87%D0%BD%D0%B0%D1%8F%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C%22%22%22%0A%20%20%20%20%23%20%D0%94%D0%B2%D1%83%D0%BC%D0%B5%D1%80%D0%BD%D1%8B%D0%B9%20%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA%20%D0%B7%D0%B0%D0%BD%D0%B8%D0%BC%D0%B0%D0%B5%D1%82%20O%28n%5E2%29%20%D0%BF%D0%B0%D0%BC%D1%8F%D1%82%D0%B8%0A%20%20%20%20num_matrix%20%3D%20%5B%5B0%5D%20%2A%20n%20for%20_%20in%20range%28n%29%5D%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20n%20%3D%205%0A%20%20%20%20print%28%22%D0%A0%D0%B0%D0%B7%D0%BC%D0%B5%D1%80%20%D0%B2%D1%85%D0%BE%D0%B4%D0%BD%D1%8B%D1%85%20%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85%20n%20%3D%22%2C%20n%29%0A%0A%20%20%20%20%23%20%D0%9A%D0%B2%D0%B0%D0%B4%D1%80%D0%B0%D1%82%D0%B8%D1%87%D0%BD%D0%B0%D1%8F%20%D1%81%D0%BB%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D1%8C%0A%20%20%20%20quadratic%28n%29&codeDivHeight=800&codeDivWidth=600&cumulative=false&curInstr=16&heapPrimitives=nevernest&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false" target="_blank" rel="noopener noreferrer">Во весь экран ></a></div>
Как показано на рисунке 2-18, глубина рекурсии этой функции равна $n$ , и в каждой рекурсивной функции инициализируется массив длины $n$ , $n-1$ , $\dots$ , $2$ , $1$ ; его средняя длина равна $n / 2$ , поэтому в сумме используется $O(n^2)$ пространства:
Как показано на рисунке 2-18, глубина рекурсии этой функции равна $n$ , и в каждой рекурсивной функции инициализируется массив длины $n$ , $n-1$ , $\dots$ , $2$ , $1$. Его средняя длина равна $n / 2$ , поэтому в сумме используется $O(n^2)$ пространства:
=== "Python"
@@ -2058,7 +2058,7 @@ $$
### 4. &nbsp; Экспоненциальная сложность $O(2^n)$ {data-toc-label="4. &nbsp; Экспоненциальная сложность"}
Экспоненциальная сложность часто встречается у бинарных деревьев. Полное бинарное дерево с $n$ уровнями содержит $2^n - 1$ узлов и занимает $O(2^n)$ пространства:
Экспоненциальная сложность часто встречается у бинарных деревьев. Как видно на рисунке 2-19, полное бинарное дерево с $n$ уровнями содержит $2^n - 1$ узлов и занимает $O(2^n)$ пространства:
=== "Python"
@@ -2267,7 +2267,7 @@ $$
### 5. &nbsp; Логарифмическая сложность $O(\log n)$ {data-toc-label="5. &nbsp; Логарифмическая сложность"}
Логарифмическая сложность часто встречается в алгоритмах "разделяй и властвуй". Например, при сортировке слиянием входной массив длины $n$ на каждом шаге рекурсии делится пополам, образуя рекурсивное дерево высоты $\log n$ и используя $O(\log n)$ пространства кадров стека.
Логарифмическая сложность часто встречается в алгоритмах «разделяй и властвуй». Например, при сортировке слиянием входной массив длины $n$ на каждом шаге рекурсии делится пополам, образуя рекурсивное дерево высоты $\log n$ и используя $O(\log n)$ пространства кадров стека.
Еще один пример - преобразование числа в строку. Если задано положительное целое число $n$ , то количество его цифр равно $\lfloor \log_{10} n \rfloor + 1$ , то есть длина соответствующей строки тоже равна $\lfloor \log_{10} n \rfloor + 1$ , следовательно, пространственная сложность составляет $O(\log_{10} n + 1) = O(\log n)$ .
@@ -2275,6 +2275,6 @@ $$
В идеальных условиях хотелось бы, чтобы и временная, и пространственная сложность алгоритма были оптимальными. Однако на практике одновременно оптимизировать и время, и память обычно очень трудно.
**Снижение временной сложности обычно достигается ценой увеличения пространственной сложности, и наоборот**. Подход, при котором жертвуют памятью ради ускорения работы алгоритма, называется обменом пространства на время; обратный подход называется обменом времени на пространство.
**Снижение временной сложности обычно достигается ценой увеличения пространственной сложности, и наоборот**. Подход, при котором жертвуют памятью ради ускорения работы алгоритма, называется обменом пространства на время. Обратный подход называется обменом времени на пространство.
Выбор между этими двумя идеями зависит от того, что важнее в конкретной задаче. В большинстве случаев время ценнее памяти, поэтому стратегия обмена пространства на время используется чаще. Но при очень больших объемах данных контроль пространственной сложности тоже становится крайне важным.