4.6 Упражнения¶
4.6.1 Вопросы для самопроверки¶
1. Как массив и связный список находят элемент¶
В массиве и односвязном списке по порядку хранятся [A, B, C, D, E]. Требуется прочитать 4-й элемент D.
- Какой индекс можно напрямую использовать в массиве?
- Через какие узлы нужно последовательно пройти по
nextв односвязном списке, начиная с головного узлаA? - Как меняется число шагов в каждой структуре по мере удаления нужного элемента от начала? Какая структура лучше подходит для многократного доступа по позиции и почему?
Ответ
-
Если индексация начинается с 0, индекс 4-го элемента равен 3, поэтому в массиве можно напрямую обратиться к
arr[3]. -
В односвязном списке нужно начинать с головы. Путь имеет вид
A → B → C → D, то есть поnextнужно перейти 3 раза. -
Массив позволяет напрямую определить положение элемента по адресу начала и индексу, поэтому временная сложность доступа по позиции равна \(O(1)\). Чтобы обратиться к \(k\)-му узлу односвязного списка, нужно начать с головного узла и перейти по
next\(k-1\) раз, что в худшем случае требует \(O(n)\) времени.Здесь сравнивается только доступ по позиции; это не означает, что связный список медленнее при всех операциях.
2. Как вставить элемент в массив и связный список¶
В массиве и односвязном списке хранятся A, B, C, D. Требуется вставить X после B.
- емкость массива равна 5, его текущее состояние —
[A, B, C, D, _]; - связный список имеет вид
A → B → C → D, и ссылка на узелBуже получена.
- Какие элементы массива нужно переместить? Запишите массив после вставки.
- В каком порядке следует изменить
X.nextиB.next? Запишите связный список после вставки. - Почему при сравнении эффективности вставки важно уточнить, что «ссылка на узел B уже получена»?
Ответ
-
В массиве сначала нужно сдвинуть
Dна одну позицию вправо, затем так же сдвинутьCи, наконец, поместитьXпо индексу 2. Получится[A, B, X, C, D]. -
Изначально
B.nextуказывает наC. Сначала следует выполнитьX.next = B.next, чтобыXуказывал наC, а затемB.next = X. ПолучитсяA → B → X → C → D. Если сначала перезаписатьB.next, не сохранив прежнюю связь, узелCможно потерять. -
Если положение
Bизвестно, для вставки в связный список достаточно изменить две связи, что занимает \(O(1)\) времени. Если жеBеще нужно искать от головы списка, сам поиск может потребовать \(O(n)\) времени.
3. Как растет емкость списка¶
Список на основе массива сейчас содержит [A, B, C], его длина size = 3, а емкость capacity = 4.
При нехватке места емкость нового массива увеличивается в 2 раза.
- Чему будут равны длина и емкость списка после добавления
D? Потребуется ли увеличить емкость? - Затем добавляют
E. Какой станет емкость и сколько прежних элементов потребуется скопировать? - Длина базового массива неизменна. Почему тогда кажется, что емкость списка может расти?
Ответ
-
Dможно поместить в последнюю свободную ячейку. Теперь содержимое равно[A, B, C, D],size = 4,capacity = 4, и увеличивать емкость не требуется. -
Для
Eсвободного места уже нет, поэтому нужно создать новый массив емкостью 8, скопировать в него 4 прежних элементаA, B, C, D, а затем добавитьE. После этогоsize = 5,capacity = 8. -
Исходный массив сам по себе не удлиняется. Список создает новый массив большего размера, копирует прежние элементы и затем использует новый массив как базовое хранилище. Поэтому для пользователя емкость выглядит увеличившейся.
4.6.2 Задачи по программированию¶
1. Прибавление единицы к большому числу в массиве¶
Массив digits хранит слева направо цифры неотрицательного целого числа. Например, [3, 0, 8] обозначает 308.
Число 0 представлено как [0]; у всех остальных входных данных первая цифра не равна 0.
Смоделируйте сложение столбиком в десятичной системе: увеличьте это число на 1 и верните результат в том же формате массива.
Массив digits можно изменять непосредственно. Если перед первой цифрой появляется новый перенос, можно вернуть более длинный массив.
Подсказки
- Как при сложении столбиком, начните с последней цифры массива
- Если текущая цифра меньше 9, увеличьте ее на один и сразу верните результат
- Если текущая цифра равна 9, замените ее на 0; если все цифры равны 9, добавьте 1 в начало
2. Разворот односвязного списка¶
Дан головной узел head односвязного списка. Каждый узел содержит значение и поле next, указывающее на следующий узел.
Итеративно разверните все связи между узлами и верните новый головной узел. Не создавайте новые узлы связного списка.
Подсказки
- Нарисуйте на бумаге три связанных узла и два указателя prev и cur
- Прежде чем изменить cur.next, сохраните исходный следующий узел в nxt
- Развернув cur.next, выполните prev = cur, затем cur = nxt и продолжайте с очередным узлом исходного списка