This commit is contained in:
krahets
2026-04-14 18:06:19 +08:00
parent 17b2a0b630
commit cf0747ba3e
131 changed files with 604 additions and 609 deletions
+14 -14
View File
@@ -4372,32 +4372,32 @@
</ul>
<h3 id="2-q-a">2. &nbsp; Q &amp; A<a class="headerlink" href="#2-q-a" title="Permanent link">&para;</a></h3>
<p><strong>Q</strong>: Почему хеш-таблица одновременно включает линейные и нелинейные структуры данных?</p>
<p>В основе хеш-таблицы лежит массив, а для разрешения коллизий мы можем использовать "цепочки адресации" (об этом будет рассказано в последующем разделе "Хеш-коллизии"): каждый бакет массива указывает на связный список, а если длина списка превышает некоторый порог, он может быть преобразован в дерево (обычно в красно-черное дерево).</p>
<p>В основе хеш-таблицы лежит массив, а для разрешения коллизий мы можем использовать «цепочки адресации» (об этом будет рассказано в последующем разделе «Хеш-коллизии»): каждый бакет массива указывает на связный список, а если длина списка превышает некоторый порог, он может быть преобразован в дерево (обычно в красно-черное дерево).</p>
<p>С точки зрения хранения данных в основе хеш-таблицы находится массив, где каждый слот бакета может содержать либо отдельное значение, либо связный список, либо дерево. Поэтому хеш-таблица действительно может одновременно включать линейные структуры данных (массивы, списки) и нелинейные структуры данных (деревья).</p>
<p><strong>Q</strong>: Длина типа <code>char</code> равна 1 байту?</p>
<p>Длина типа <code>char</code> определяется используемым в языке программирования способом кодирования. Например, Java, JavaScript, TypeScript и C# используют кодировку UTF-16 (для хранения кодовых точек Unicode), поэтому длина <code>char</code> у них равна 2 байтам.</p>
<p><strong>Q</strong>: Не является ли двусмысленным утверждение, что структуры данных, реализованные на основе массива, также называются "статическими структурами данных"? Ведь стек тоже поддерживает операции push и pop, а они явно "динамические".</p>
<p>Стек действительно может поддерживать динамические операции над данными, но сама структура данных при этом остается "статической" (ее длина неизменна). Хотя структуры на основе массива могут динамически добавлять и удалять элементы, их емкость фиксирована. Если количество данных превышает заранее выделенный размер, приходится создавать новый, более крупный массив и копировать в него содержимое старого.</p>
<p><strong>Q</strong>: При построении стека (очереди) его размер не задается явно, почему же его относят к "статическим структурам данных"?</p>
<p>В языках высокого уровня нам не нужно вручную задавать начальную емкость стека (очереди): это автоматически делает сама реализация класса. Например, начальная емкость <code>ArrayList</code> в Java обычно равна 10. Кроме того, автоматом реализуется и расширение емкости. Подробнее это рассматривается в последующем разделе о "списках".</p>
<p><strong>Q</strong>: Если метод преобразования из прямого кода в дополнительный - это "сначала инвертировать, затем прибавить 1", то обратное преобразование из дополнительного кода в прямой, по идее, должно быть обратной операцией "сначала вычесть 1, затем инвертировать". Почему же дополнительный код также можно перевести в прямой тем же способом "сначала инвертировать, затем прибавить 1"?</p>
<p>Это связано с тем, что взаимное преобразование прямого и дополнительного кодов по сути является вычислением "дополнения". Сначала дадим определение дополнения: если <span class="arithmatex">\(a + b = c\)</span> , то говорят, что <span class="arithmatex">\(a\)</span> является дополнением числа <span class="arithmatex">\(b\)</span> до <span class="arithmatex">\(c\)</span> ; аналогично, <span class="arithmatex">\(b\)</span> является дополнением числа <span class="arithmatex">\(a\)</span> до <span class="arithmatex">\(c\)</span> .</p>
<p>Для двоичного числа длины <span class="arithmatex">\(n = 4\)</span> со значением <span class="arithmatex">\(0010\)</span> , если рассматривать его как прямой код (не учитывая знаковый бит), то его дополнительный код получается правилом "сначала инвертировать, затем прибавить 1":</p>
<p><strong>Q</strong>: Не является ли двусмысленным утверждение, что структуры данных, реализованные на основе массива, также называются «статическими структурами данных»? Ведь стек тоже поддерживает операции push и pop, а они явно «динамические».</p>
<p>Стек действительно может поддерживать динамические операции над данными, но сама структура данных при этом остается «статической» (ее длина неизменна). Хотя структуры на основе массива могут динамически добавлять и удалять элементы, их емкость фиксирована. Если количество данных превышает заранее выделенный размер, приходится создавать новый, более крупный массив и копировать в него содержимое старого.</p>
<p><strong>Q</strong>: При построении стека (очереди) его размер не задается явно, почему же его относят к «статическим структурам данных»?</p>
<p>В языках высокого уровня нам не нужно вручную задавать начальную емкость стека (очереди): это автоматически делает сама реализация класса. Например, начальная емкость <code>ArrayList</code> в Java обычно равна 10. Кроме того, автоматом реализуется и расширение емкости. Подробнее это рассматривается в последующем разделе о «списках».</p>
<p><strong>Q</strong>: Если метод преобразования из прямого кода в дополнительный - это «сначала инвертировать, затем прибавить 1», то обратное преобразование из дополнительного кода в прямой, по идее, должно быть обратной операцией «сначала вычесть 1, затем инвертировать». Почему же дополнительный код также можно перевести в прямой тем же способом «сначала инвертировать, затем прибавить 1»?</p>
<p>Это связано с тем, что взаимное преобразование прямого и дополнительного кодов по сути является вычислением «дополнения». Сначала дадим определение дополнения: если <span class="arithmatex">\(a + b = c\)</span> , то говорят, что <span class="arithmatex">\(a\)</span> является дополнением числа <span class="arithmatex">\(b\)</span> до <span class="arithmatex">\(c\)</span>. Аналогично, <span class="arithmatex">\(b\)</span> является дополнением числа <span class="arithmatex">\(a\)</span> до <span class="arithmatex">\(c\)</span> .</p>
<p>Для двоичного числа длины <span class="arithmatex">\(n = 4\)</span> со значением <span class="arithmatex">\(0010\)</span> , если рассматривать его как прямой код (не учитывая знаковый бит), то его дополнительный код получается правилом «сначала инвертировать, затем прибавить 1»:</p>
<div class="arithmatex">\[
0010 \rightarrow 1101 \rightarrow 1110
\]</div>
<p>Мы видим, что сумма прямого и дополнительного кодов равна <span class="arithmatex">\(0010 + 1110 = 10000\)</span> , то есть дополнительный код <span class="arithmatex">\(1110\)</span> является "дополнением" прямого кода <span class="arithmatex">\(0010\)</span> до <span class="arithmatex">\(10000\)</span> . **Это означает, что описанная выше операция "сначала инвертировать, затем прибавить 1" на самом деле вычисляет дополнение до <span class="arithmatex">\(10000\)</span> **.</p>
<p>Тогда чему равно "дополнение" дополнительного кода <span class="arithmatex">\(1110\)</span> до <span class="arithmatex">\(10000\)</span> ? Мы снова можем получить его правилом "сначала инвертировать, затем прибавить 1":</p>
<p>Мы видим, что сумма прямого и дополнительного кодов равна <span class="arithmatex">\(0010 + 1110 = 10000\)</span> , то есть дополнительный код <span class="arithmatex">\(1110\)</span> является «дополнением» прямого кода <span class="arithmatex">\(0010\)</span> до <span class="arithmatex">\(10000\)</span> . **Это означает, что описанная выше операция «сначала инвертировать, затем прибавить 1» на самом деле вычисляет дополнение до <span class="arithmatex">\(10000\)</span> **.</p>
<p>Тогда чему равно «дополнение» дополнительного кода <span class="arithmatex">\(1110\)</span> до <span class="arithmatex">\(10000\)</span> ? Мы снова можем получить его правилом «сначала инвертировать, затем прибавить 1»:</p>
<div class="arithmatex">\[
1110 \rightarrow 0001 \rightarrow 0010
\]</div>
<p>Иначе говоря, прямой и дополнительный коды являются взаимными "дополнениями" друг друга до <span class="arithmatex">\(10000\)</span> , поэтому и "прямой код -&gt; дополнительный код", и "дополнительный код -&gt; прямой код" можно реализовать одной и той же операцией (сначала инвертировать, затем прибавить 1).</p>
<p>Разумеется, можно получить прямой код из дополнительного кода <span class="arithmatex">\(1110\)</span> и обратной операцией, то есть "сначала вычесть 1, затем инвертировать":</p>
<p>Иначе говоря, прямой и дополнительный коды являются взаимными «дополнениями» друг друга до <span class="arithmatex">\(10000\)</span> , поэтому и «прямой код -&gt; дополнительный код», и «дополнительный код -&gt; прямой код» можно реализовать одной и той же операцией (сначала инвертировать, затем прибавить 1).</p>
<p>Разумеется, можно получить прямой код из дополнительного кода <span class="arithmatex">\(1110\)</span> и обратной операцией, то есть «сначала вычесть 1, затем инвертировать»:</p>
<div class="arithmatex">\[
1110 \rightarrow 1101 \rightarrow 0010
\]</div>
<p>В итоге и "сначала инвертировать, затем прибавить 1", и "сначала вычесть 1, затем инвертировать" - это два эквивалентных способа вычисления дополнения до <span class="arithmatex">\(10000\)</span> .</p>
<p>По сути операция "инвертировать" сама по себе вычисляет дополнение до <span class="arithmatex">\(1111\)</span> (потому что всегда выполняется <code>прямой код + обратный код = 1111</code> ); а дополнительный код, получающийся после добавления 1 к обратному коду, и есть дополнение до <span class="arithmatex">\(10000\)</span> .</p>
<p>В итоге и «сначала инвертировать, затем прибавить 1», и «сначала вычесть 1, затем инвертировать» - это два эквивалентных способа вычисления дополнения до <span class="arithmatex">\(10000\)</span> .</p>
<p>По сути операция «инвертировать» сама по себе вычисляет дополнение до <span class="arithmatex">\(1111\)</span> (потому что всегда выполняется <code>прямой код + обратный код = 1111</code> ). А дополнительный код, получающийся после добавления 1 к обратному коду, и есть дополнение до <span class="arithmatex">\(10000\)</span> .</p>
<p>Приведенный выше пример использовал <span class="arithmatex">\(n = 4\)</span> , но его можно обобщить на двоичные числа любой длины.</p>
<!-- Source file information -->