1 added
97 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>В этом уроке мы начнем изучать структуры данных и связанные с ними алгоритмы. Чтобы разобраться во всех деталях, мы рассмотрим два примера из реальной жизни - склад и библиотеку.</p>
1
<p>В этом уроке мы начнем изучать структуры данных и связанные с ними алгоритмы. Чтобы разобраться во всех деталях, мы рассмотрим два примера из реальной жизни - склад и библиотеку.</p>
2
<p>На складе кладовщик записывает все пришедшие товары в специальный журнал. Рабочий день кладовщика состоит из приема и выдачи товаров. Очень важно записывать прием и выдачу как можно быстрее, поэтому кладовщик просто пишет все операции друг за другом, подряд.</p>
2
<p>На складе кладовщик записывает все пришедшие товары в специальный журнал. Рабочий день кладовщика состоит из приема и выдачи товаров. Очень важно записывать прием и выдачу как можно быстрее, поэтому кладовщик просто пишет все операции друг за другом, подряд.</p>
3
<p>Из-за специфики работы склада кладовщик составляет базу данных, в которой:</p>
3
<p>Из-за специфики работы склада кладовщик составляет базу данных, в которой:</p>
4
<ul><li>Сложно найти товар на складе</li>
4
<ul><li>Сложно найти товар на складе</li>
5
<li>Просто сделать новую запись в журнале</li>
5
<li>Просто сделать новую запись в журнале</li>
6
-
</ul><p>Ситуация в библиотеке совсем другая. Основная работа библиотекаря - поиск книг по фамилии автора, по названию книги или по тематике. Для быстрого поиска книг библиотека��и используют картотеку. Для каждой книги заводятся несколько карточек и размещаются в разных ящиках. В одном они упорядочены по фамилии автора, в другом - по названию. Благодаря порядку карточки ищутся очень быстро.</p>
6
+
</ul><p>Ситуация в библиотеке совсем другая. Основная работа библиотекаря - поиск книг по фамилии автора, по названию книги или по тематике. Для быстрого поиска книг библиотекари используют картотеку. Для каждой книги заводятся несколько карточек и размещаются в разных ящиках. В одном они упорядочены по фамилии автора, в другом - по названию. Благодаря порядку карточки ищутся очень быстро.</p>
7
<p>Так в библиотеке составляется база данных с совсем другими свойствами:</p>
7
<p>Так в библиотеке составляется база данных с совсем другими свойствами:</p>
8
<ul><li>Просто найти книгу в библиотеке</li>
8
<ul><li>Просто найти книгу в библиотеке</li>
9
<li>Сложно сделать новую запись в картотеке</li>
9
<li>Сложно сделать новую запись в картотеке</li>
10
</ul><p>И на складе, и в библиотеке нам приходится записывать и искать похожую информацию, однако скорость записи и поиска получается разная:</p>
10
</ul><p>И на складе, и в библиотеке нам приходится записывать и искать похожую информацию, однако скорость записи и поиска получается разная:</p>
11
<p>Как видите, мы не можем выбрать один универсальный способ записи, который подойдет для всех случаев. Для склада больше подходит Журнал, а для библиотеки - Картотека.</p>
11
<p>Как видите, мы не можем выбрать один универсальный способ записи, который подойдет для всех случаев. Для склада больше подходит Журнал, а для библиотеки - Картотека.</p>
12
<p>В обоих примерах своя<strong>структура данных</strong>- то есть способ хранения данных в памяти компьютера. Для каждой структуры существует набор основных операций - добавление, поиск, удаление. Из-за особенностей структуры, добавление и поиск могут быть быстрыми или медленными.</p>
12
<p>В обоих примерах своя<strong>структура данных</strong>- то есть способ хранения данных в памяти компьютера. Для каждой структуры существует набор основных операций - добавление, поиск, удаление. Из-за особенностей структуры, добавление и поиск могут быть быстрыми или медленными.</p>
13
<p>Программисты изучают основные структуры данных и запоминают скорость основных операций. Это помогает им выбирать самые подходящие структуры для решения задач пользователя. В этом уроке мы познакомимся со связным списком и сравним его с массивом.</p>
13
<p>Программисты изучают основные структуры данных и запоминают скорость основных операций. Это помогает им выбирать самые подходящие структуры для решения задач пользователя. В этом уроке мы познакомимся со связным списком и сравним его с массивом.</p>
14
<h2>Как устроен массив</h2>
14
<h2>Как устроен массив</h2>
15
<p>Чтобы освоиться с понятием структуры данных и ее операциями, давайте исследуем структуру, которая нам хорошо известна - массив.</p>
15
<p>Чтобы освоиться с понятием структуры данных и ее операциями, давайте исследуем структуру, которая нам хорошо известна - массив.</p>
16
<p>В JavaScript все данные относятся к примитивным или ссылочных типам. Числа и булевы значения хранятся непосредственно в переменных:</p>
16
<p>В JavaScript все данные относятся к примитивным или ссылочных типам. Числа и булевы значения хранятся непосредственно в переменных:</p>
17
-
<p><strong>Javascript</strong></p>
18
-
<p><strong>Python</strong></p>
19
-
<p><strong>PHP</strong></p>
20
-
<p><strong>Java</strong></p>
21
<p>Массивы и объекты - это ссылочные типы, которые хранятся в памяти отдельно. Область хранения называется кучей, и в начале работы программы она пуста:</p>
17
<p>Массивы и объекты - это ссылочные типы, которые хранятся в памяти отдельно. Область хранения называется кучей, и в начале работы программы она пуста:</p>
22
<p>Название "куча" когда-то было сленговым, но прижилось и сейчас используется как официальный термин. Каждая ячейка в куче - это обычная переменная, которая может хранить одно значение. У каждой ячейки есть адрес - ее порядковый номер. Когда мы создаем новый массив, интерпретатор JavaScript размещает его в свободном месте кучи и записывает в переменную адрес массива:</p>
18
<p>Название "куча" когда-то было сленговым, но прижилось и сейчас используется как официальный термин. Каждая ячейка в куче - это обычная переменная, которая может хранить одно значение. У каждой ячейки есть адрес - ее порядковый номер. Когда мы создаем новый массив, интерпретатор JavaScript размещает его в свободном месте кучи и записывает в переменную адрес массива:</p>
23
-
<p><strong>Javascript</strong></p>
24
-
<p><strong>Python</strong></p>
25
-
<p><strong>PHP</strong></p>
26
-
<p><strong>Java</strong></p>
27
<p>На рисунке вы видите, как хранится массив в памяти. В ячейках с номерами 00, 01 и 02 уже есть какие-то данные - они "заняты", поэтому массив начинается с ячейки 03.</p>
19
<p>На рисунке вы видите, как хранится массив в памяти. В ячейках с номерами 00, 01 и 02 уже есть какие-то данные - они "заняты", поэтому массив начинается с ячейки 03.</p>
28
<p>В самой первой ячейке массива хранится его длина или количество элементов, то есть 4. Затем последовательно хранятся сами значения 5, 8, 12 и 3.</p>
20
<p>В самой первой ячейке массива хранится его длина или количество элементов, то есть 4. Затем последовательно хранятся сами значения 5, 8, 12 и 3.</p>
29
<p>Поскольку элементы массива хранятся последовательно, процессор легко определяет адрес элемента по его порядковому номеру в массиве:</p>
21
<p>Поскольку элементы массива хранятся последовательно, процессор легко определяет адрес элемента по его порядковому номеру в массиве:</p>
30
-
<p><strong>Javascript</strong></p>
31
-
<p><strong>Python</strong></p>
32
-
<p><strong>PHP</strong></p>
33
-
<p><strong>Java</strong></p>
34
<p>Массив начинается с адреса 03, где хранится длина массива. Нулевой элемент массива items[0] имеет адрес 04, а второй элемент items[2] - адрес 04 + 2, то есть 06. В этой ячейке находится число 12.</p>
22
<p>Массив начинается с адреса 03, где хранится длина массива. Нулевой элемент массива items[0] имеет адрес 04, а второй элемент items[2] - адрес 04 + 2, то есть 06. В этой ячейке находится число 12.</p>
35
<p>Подобные рассуждения могут запутать, поскольку в ячейках хранятся числа, а адреса ячеек - тоже числа. Если чувствуете, что потеряли нить рассуждений, вернитесь немного назад и повторите.</p>
23
<p>Подобные рассуждения могут запутать, поскольку в ячейках хранятся числа, а адреса ячеек - тоже числа. Если чувствуете, что потеряли нить рассуждений, вернитесь немного назад и повторите.</p>
36
<p>Порядковый номер элемента в массиве обычно называют индексом - это название пришло из математики.</p>
24
<p>Порядковый номер элемента в массиве обычно называют индексом - это название пришло из математики.</p>
37
<p>Определение длины массива и обращение к элементу по индексу - две простые операции, которые мы постоянно используем при работе с массивами. Возьмем для примера функцию, которая вычисляет сумму элементов массива:</p>
25
<p>Определение длины массива и обращение к элементу по индексу - две простые операции, которые мы постоянно используем при работе с массивами. Возьмем для примера функцию, которая вычисляет сумму элементов массива:</p>
38
-
<p><strong>Javascript</strong></p>
39
-
<p><strong>Python</strong></p>
40
-
<p><strong>PHP</strong></p>
41
-
<p><strong>Java</strong></p>
42
<p>Иногда мы хотим расширить массив и добавить к нему несколько элементов. В JavaScript для этого используют метод push():</p>
26
<p>Иногда мы хотим расширить массив и добавить к нему несколько элементов. В JavaScript для этого используют метод push():</p>
43
-
<p><strong>Javascript</strong></p>
44
-
<p><strong>Python</strong></p>
45
-
<p><strong>PHP</strong></p>
46
-
<p><strong>Java</strong></p>
47
<p>В простом случае, если сразу за массивом есть свободное место, новые элементы попадут туда:</p>
27
<p>В простом случае, если сразу за массивом есть свободное место, новые элементы попадут туда:</p>
48
<p>Мы видим, что длина массива теперь равна шести, и в его конце появились числа 7 и 20. А теперь представим, что программист создал после массива еще несколько объектов и свободной памяти сразу за массивом нет:</p>
28
<p>Мы видим, что длина массива теперь равна шести, и в его конце появились числа 7 и 20. А теперь представим, что программист создал после массива еще несколько объектов и свободной памяти сразу за массивом нет:</p>
49
-
<p><strong>Javascript</strong></p>
50
-
<p><strong>Python</strong></p>
51
-
<p><strong>PHP</strong></p>
52
-
<p><strong>Java</strong></p>
53
<p>На рисунке сразу за массивом items в куче находится объект point. Кажется, что в этом случае мы не можем расширить массив, ведь элементы должны располагаться в памяти подряд. На самом деле при вызове метода push() интерпретатор копирует весь массив в свободную область памяти и добавляет к ней несколько элементов. Исходная область памяти помечается как свободная:</p>
29
<p>На рисунке сразу за массивом items в куче находится объект point. Кажется, что в этом случае мы не можем расширить массив, ведь элементы должны располагаться в памяти подряд. На самом деле при вызове метода push() интерпретатор копирует весь массив в свободную область памяти и добавляет к ней несколько элементов. Исходная область памяти помечается как свободная:</p>
54
<p>Расширение массива может привести к копированию большого объема данных и замедлению программы. Есть несколько способов борьбы с таким поведением, но копирование все равно случается. Как быть, если нам предстоит часто добавлять и удалять элементы? Можно применить связный список.</p>
30
<p>Расширение массива может привести к копированию большого объема данных и замедлению программы. Есть несколько способов борьбы с таким поведением, но копирование все равно случается. Как быть, если нам предстоит часто добавлять и удалять элементы? Можно применить связный список.</p>
55
<h2>Связный список</h2>
31
<h2>Связный список</h2>
56
<p>Это структура, которая решает проблему производительности, если нам приходится часто добавлять и удалять данные. Данные в связном списке хранятся не подряд, а вразброс.</p>
32
<p>Это структура, которая решает проблему производительности, если нам приходится часто добавлять и удалять данные. Данные в связном списке хранятся не подряд, а вразброс.</p>
57
<p>Каждое значение хранится в отдельном объекте, который называется<strong>узлом</strong>. Помимо значения, объект хранит ссылку на следующий узел списка. В самом последнем узле вместо ссылки на следующий элемент хранится значение null.</p>
33
<p>Каждое значение хранится в отдельном объекте, который называется<strong>узлом</strong>. Помимо значения, объект хранит ссылку на следующий узел списка. В самом последнем узле вместо ссылки на следующий элемент хранится значение null.</p>
58
<p>Опишем класс, содержащий значение (value) и ссылку на следующий узел (next):</p>
34
<p>Опишем класс, содержащий значение (value) и ссылку на следующий узел (next):</p>
59
-
<p><strong>Javascript</strong></p>
60
-
<p><strong>Python</strong></p>
61
-
<p><strong>PHP</strong></p>
62
-
<p><strong>Java</strong></p>
63
<p>Список из элементов 5, 8, 12 и 3 может быть создан так:</p>
35
<p>Список из элементов 5, 8, 12 и 3 может быть создан так:</p>
64
-
<p><strong>Javascript</strong></p>
65
-
<p><strong>Python</strong></p>
66
-
<p><strong>PHP</strong></p>
67
-
<p><strong>Java</strong></p>
68
<p>Оператор new не только создает объект, но и выделяет для него место в памяти. Самый первый элемент односвязного списка часто называют головой (head), поэтому и переменную со ссылкой на голову мы назвали head.</p>
36
<p>Оператор new не только создает объект, но и выделяет для него место в памяти. Самый первый элемент односвязного списка часто называют головой (head), поэтому и переменную со ссылкой на голову мы назвали head.</p>
69
<p>В поле next самого последнего узла находится null - значит, узлов больше нет. В отличие от массива, узлы списка не размещаются в памяти подряд:</p>
37
<p>В поле next самого последнего узла находится null - значит, узлов больше нет. В отличие от массива, узлы списка не размещаются в памяти подряд:</p>
70
<p>На рисунке узлы списка занимают две ячейки. В первой хранится значение, а во второй - адрес следующего узла или null. Иногда узлы могут располагаться рядом (на рисунке это 12 и 3), но в общем случае это не так.</p>
38
<p>На рисунке узлы списка занимают две ячейки. В первой хранится значение, а во второй - адрес следующего узла или null. Иногда узлы могут располагаться рядом (на рисунке это 12 и 3), но в общем случае это не так.</p>
71
<p>Вся работа со списком производится через ссылку на его первый элемент, так что переменная head - единственное, что нам нужно для реализации алгоритмов.</p>
39
<p>Вся работа со списком производится через ссылку на его первый элемент, так что переменная head - единственное, что нам нужно для реализации алгоритмов.</p>
72
<p>Поскольку мы пишем на языке JavaScript, который поддерживает объектно-ориентированное программирование, мы объединим поле head и все наши функции в один класс. Он будет называться LinkedList, что в переводе с английского означает связный список. При создании нового списка в поле head хранится значение null, что означает, что список пустой:</p>
40
<p>Поскольку мы пишем на языке JavaScript, который поддерживает объектно-ориентированное программирование, мы объединим поле head и все наши функции в один класс. Он будет называться LinkedList, что в переводе с английского означает связный список. При создании нового списка в поле head хранится значение null, что означает, что список пустой:</p>
73
-
<p><strong>Javascript</strong></p>
74
-
<p><strong>Python</strong></p>
75
-
<p><strong>PHP</strong></p>
76
-
<p><strong>Java</strong></p>
77
<h2>Вставка элемента в начало списка</h2>
41
<h2>Вставка элемента в начало списка</h2>
78
<p>В простейшем случае, мы вставляем элемент в пустой список. В самом начале значение поля head равно null:</p>
42
<p>В простейшем случае, мы вставляем элемент в пустой список. В самом начале значение поля head равно null:</p>
79
<p>После вставки head указывает на единственный элемент списка:</p>
43
<p>После вставки head указывает на единственный элемент списка:</p>
80
<p>Мы не рисуем кучу целиком, потому что в реальной программе трудно предугадать, по какому адресу разместится то или иное значение. Конкретные адреса могут сбить с толку.</p>
44
<p>Мы не рисуем кучу целиком, потому что в реальной программе трудно предугадать, по какому адресу разместится то или иное значение. Конкретные адреса могут сбить с толку.</p>
81
<p>Поэтому мы просто отмечаем факт, что для нового узла списка в куче были выделены две ячейки, и head указывает на этот узел. Адрес первого узла мы запишем, как addr(1), это позволит нам отличать адреса друг от друга.</p>
45
<p>Поэтому мы просто отмечаем факт, что для нового узла списка в куче были выделены две ячейки, и head указывает на этот узел. Адрес первого узла мы запишем, как addr(1), это позволит нам отличать адреса друг от друга.</p>
82
<p>После вставки второго узла в начало списка, картина примет такой вид:</p>
46
<p>После вставки второго узла в начало списка, картина примет такой вид:</p>
83
<p>Теперь addr(1), который находился в head, переместился в узел 2, а в head попал адрес второго узла addr(2).</p>
47
<p>Теперь addr(1), который находился в head, переместился в узел 2, а в head попал адрес второго узла addr(2).</p>
84
<p>В обоих случаях (вставка в пустой и непустой список), сначала мы создаем узел, куда, в качестве указателя на следующий элемент, записываем текущее значение head.</p>
48
<p>В обоих случаях (вставка в пустой и непустой список), сначала мы создаем узел, куда, в качестве указателя на следующий элемент, записываем текущее значение head.</p>
85
<p>Взглянем на код:</p>
49
<p>Взглянем на код:</p>
86
-
<p><strong>Javascript</strong></p>
87
-
<p><strong>Python</strong></p>
88
-
<p><strong>PHP</strong></p>
89
-
<p><strong>Java</strong></p>
90
<p>Метод add принимает параметр value (значение). Он создает новый узел, помещает туда значение и вставляет узел в начало списка.</p>
50
<p>Метод add принимает параметр value (значение). Он создает новый узел, помещает туда значение и вставляет узел в начало списка.</p>
91
<p>Метод состоит из единственной строки:</p>
51
<p>Метод состоит из единственной строки:</p>
92
-
<p><strong>Javascript</strong></p>
93
-
<p><strong>Python</strong></p>
94
-
<p><strong>PHP</strong></p>
95
-
<p><strong>Java</strong></p>
96
<p>Здесь мы совместили две операции, которые можно было бы записать в две строки:</p>
52
<p>Здесь мы совместили две операции, которые можно было бы записать в две строки:</p>
97
-
<p><strong>Javascript</strong></p>
98
-
<p><strong>Python</strong></p>
99
-
<p><strong>PHP</strong></p>
100
-
<p><strong>Java</strong></p>
101
<p>Если первый вариант кода кажется вам сложным, вы можете остановиться на втором. Обычно программисты, по мере чтения кода, привыкают к часто встречающимся конструкциям. Знакомые конструкции уже не кажутся сложными.</p>
53
<p>Если первый вариант кода кажется вам сложным, вы можете остановиться на втором. Обычно программисты, по мере чтения кода, привыкают к часто встречающимся конструкциям. Знакомые конструкции уже не кажутся сложными.</p>
102
<p>Насколько быстро работает метод add? Создание объекта может занимать большое время, но у нас простой конструктор с двумя присваиваниями, поэтому работать он будет быстро.</p>
54
<p>Насколько быстро работает метод add? Создание объекта может занимать большое время, но у нас простой конструктор с двумя присваиваниями, поэтому работать он будет быстро.</p>
103
<p>Время добавления узла в начало всегда одно и то же и не зависит от размера списка, поэтому в данном случае речь идет об алгоритмической сложности O(1).</p>
55
<p>Время добавления узла в начало всегда одно и то же и не зависит от размера списка, поэтому в данном случае речь идет об алгоритмической сложности O(1).</p>
104
<h2>Вставка элемента в середину или конец списка</h2>
56
<h2>Вставка элемента в середину или конец списка</h2>
105
<p>Вставка в середину или конец - сложная операция. В отличие от массива, мы не можем сразу получить второй или пятый узел списка. Мы должны перебрать все узлы с начала, чтобы понять, куда вставить новое значение:</p>
57
<p>Вставка в середину или конец - сложная операция. В отличие от массива, мы не можем сразу получить второй или пятый узел списка. Мы должны перебрать все узлы с начала, чтобы понять, куда вставить новое значение:</p>
106
-
<p><strong>Javascript</strong></p>
107
-
<p><strong>Python</strong></p>
108
-
<p><strong>PHP</strong></p>
109
-
<p><strong>Java</strong></p>
110
<p>Вызов insert(index, value) означает, что узел со значением value будет вставлен в середину списка в позицию index. Если index равен 0, то значение вставится перед первым элементом - так же, как при вызове add:</p>
58
<p>Вызов insert(index, value) означает, что узел со значением value будет вставлен в середину списка в позицию index. Если index равен 0, то значение вставится перед первым элементом - так же, как при вызове add:</p>
111
-
<p><strong>Javascript</strong></p>
112
-
<p><strong>Python</strong></p>
113
-
<p><strong>PHP</strong></p>
114
-
<p><strong>Java</strong></p>
115
<p>Если список пустой, index не может быть больше нуля, потому что мы можем вставить значение только в начало списка. Как должна поступать в этом случае функция решает ее автор. Мы можем генерировать ошибку или попытаться выполнить какое-то разумное действие.</p>
59
<p>Если список пустой, index не может быть больше нуля, потому что мы можем вставить значение только в начало списка. Как должна поступать в этом случае функция решает ее автор. Мы можем генерировать ошибку или попытаться выполнить какое-то разумное действие.</p>
116
<p>Пойдем вторым путем - если список пустой, при больших значениях index будем вставлять элемент в самое начало:</p>
60
<p>Пойдем вторым путем - если список пустой, при больших значениях index будем вставлять элемент в самое начало:</p>
117
-
<p><strong>Javascript</strong></p>
118
-
<p><strong>Python</strong></p>
119
-
<p><strong>PHP</strong></p>
120
-
<p><strong>Java</strong></p>
121
<p>А в случае, если index оказывается за концом списка, будем вставлять элемент в самый конец. Для этого будем проверять два условия: либо мы нашли элемент с номером index, и он не в конце, либо мы достигли последнего элемента:</p>
61
<p>А в случае, если index оказывается за концом списка, будем вставлять элемент в самый конец. Для этого будем проверять два условия: либо мы нашли элемент с номером index, и он не в конце, либо мы достигли последнего элемента:</p>
122
-
<p><strong>Javascript</strong></p>
123
-
<p><strong>Python</strong></p>
124
-
<p><strong>PHP</strong></p>
125
-
<p><strong>Java</strong></p>
126
<p>Нарисуем, что должно происходить при вставке. Пусть в начале у нас есть список из элементов A и C. Мы хотим вставить элемент B после A, но перед C:</p>
62
<p>Нарисуем, что должно происходить при вставке. Пусть в начале у нас есть список из элементов A и C. Мы хотим вставить элемент B после A, но перед C:</p>
127
<p>Когда цикл заканчивается, переменная current указывается на узел A:</p>
63
<p>Когда цикл заканчивается, переменная current указывается на узел A:</p>
128
<p>После вставки мы получим такую картину:</p>
64
<p>После вставки мы получим такую картину:</p>
129
<p>Перед вставкой в current.next хранится ссылка на C, но теперь она должна переехать в узел B, а в current.next мы запишем ссылку на новый узел B:</p>
65
<p>Перед вставкой в current.next хранится ссылка на C, но теперь она должна переехать в узел B, а в current.next мы запишем ссылку на новый узел B:</p>
130
-
<p><strong>Javascript</strong></p>
131
-
<p><strong>Python</strong></p>
132
-
<p><strong>PHP</strong></p>
133
-
<p><strong>Java</strong></p>
134
<h2>Поиск элемента</h2>
66
<h2>Поиск элемента</h2>
135
<p>При работе с массивом важное значение имеет индекс элементов. При работе со списком индекс практически не используется, хотя формально мы можем найти третий или семнадцатый узел списка.</p>
67
<p>При работе с массивом важное значение имеет индекс элементов. При работе со списком индекс практически не используется, хотя формально мы можем найти третий или семнадцатый узел списка.</p>
136
<p>Если при поиске элемента в массиве, функции часто возвращают индекс элемента, то для списка - логическое значение true или false.</p>
68
<p>Если при поиске элемента в массиве, функции часто возвращают индекс элемента, то для списка - логическое значение true или false.</p>
137
<p>Вместо ответа на вопрос "если такой элемент в массиве есть, где он находится", функция поиска в списке отвечает на вопрос "есть ли такой элемент в списке":</p>
69
<p>Вместо ответа на вопрос "если такой элемент в массиве есть, где он находится", функция поиска в списке отвечает на вопрос "есть ли такой элемент в списке":</p>
138
-
<p><strong>Javascript</strong></p>
139
-
<p><strong>Python</strong></p>
140
-
<p><strong>PHP</strong></p>
141
-
<p><strong>Java</strong></p>
142
<p>Здесь у нас простой цикл. Мы пробегаем по всем узлам, пока не натыкаемся на null. Если мы встретили null и не нашли значения, значит, в списке его нет - в конце цикла мы возвращаем false.</p>
70
<p>Здесь у нас простой цикл. Мы пробегаем по всем узлам, пока не натыкаемся на null. Если мы встретили null и не нашли значения, значит, в списке его нет - в конце цикла мы возвращаем false.</p>
143
<p>Если значение находится в одном из узлов, мы прерываем цикл и сразу возвращаем true:</p>
71
<p>Если значение находится в одном из узлов, мы прерываем цикл и сразу возвращаем true:</p>
144
-
<p><strong>Javascript</strong></p>
145
-
<p><strong>Python</strong></p>
146
-
<p><strong>PHP</strong></p>
147
-
<p><strong>Java</strong></p>
148
<p>В конце цикла важно переходить к следующему узлу, иначе цикл станет бесконечным:</p>
72
<p>В конце цикла важно переходить к следующему узлу, иначе цикл станет бесконечным:</p>
149
-
<p><strong>Javascript</strong></p>
150
-
<p><strong>Python</strong></p>
151
-
<p><strong>PHP</strong></p>
152
-
<p><strong>Java</strong></p>
153
<h2>Определение длины списка</h2>
73
<h2>Определение длины списка</h2>
154
<p>В отличие от массива, длина списка нам неизвестна, ее нужно вычислить. Мы заводим счетчик и пробегаем по всем узлам списка, увеличивая его на каждой итерации. Длина пустого списка считается равной нулю.</p>
74
<p>В отличие от массива, длина списка нам неизвестна, ее нужно вычислить. Мы заводим счетчик и пробегаем по всем узлам списка, увеличивая его на каждой итерации. Длина пустого списка считается равной нулю.</p>
155
<p>В целом, код получается простой:</p>
75
<p>В целом, код получается простой:</p>
156
-
<p><strong>Javascript</strong></p>
157
-
<p><strong>Python</strong></p>
158
-
<p><strong>PHP</strong></p>
159
-
<p><strong>Java</strong></p>
160
<p>Переменная result - это наш счетчик. Переменная current на каждой итерации указывает на текущий узел списка. Когда она становится равной null, список пройден до конца. Перейдя к следующему узлу, мы увеличиваем счетчик, поэтому в конце цикла его значение равно количеству узлов.</p>
76
<p>Переменная result - это наш счетчик. Переменная current на каждой итерации указывает на текущий узел списка. Когда она становится равной null, список пройден до конца. Перейдя к следующему узлу, мы увеличиваем счетчик, поэтому в конце цикла его значение равно количеству узлов.</p>
161
<h2>Удаление элемента из начала списка</h2>
77
<h2>Удаление элемента из начала списка</h2>
162
<p>Удаление элемента из начала списка такое же простое, как и вставка. Мы будем возвращать значение из удаленного узла в качестве результата.</p>
78
<p>Удаление элемента из начала списка такое же простое, как и вставка. Мы будем возвращать значение из удаленного узла в качестве результата.</p>
163
<p>Если список пустой, мы не будем ничего удалять, и в качестве результата вернем undefined:</p>
79
<p>Если список пустой, мы не будем ничего удалять, и в качестве результата вернем undefined:</p>
164
-
<p><strong>Javascript</strong></p>
165
-
<p><strong>Python</strong></p>
166
-
<p><strong>PHP</strong></p>
167
-
<p><strong>Java</strong></p>
168
<p>При удалении в this.head попадает ссылка на следующий узел из первого узла. Когда в списке остается один узел, там находится null. После удаления последнего узла this.head становится равным null, что для нас означает пустой список.</p>
80
<p>При удалении в this.head попадает ссылка на следующий узел из первого узла. Когда в списке остается один узел, там находится null. После удаления последнего узла this.head становится равным null, что для нас означает пустой список.</p>
169
<p>Нам не приходится как-то по-особому описывать этот случай в коде - наш код работает для списков любой длины.</p>
81
<p>Нам не приходится как-то по-особому описывать этот случай в коде - наш код работает для списков любой длины.</p>
170
<p>Рисунки помогут разобраться, как удаляются узлы из списка:</p>
82
<p>Рисунки помогут разобраться, как удаляются узлы из списка:</p>
171
<h2>Удаление элемента из середины или конца списка</h2>
83
<h2>Удаление элемента из середины или конца списка</h2>
172
<p>Теперь, когда мы умеем вставлять элементы в начало и середину списка, и удалять их из начала списка, удаление узлов из середины не должно представлять для нас проблемы.</p>
84
<p>Теперь, когда мы умеем вставлять элементы в начало и середину списка, и удалять их из начала списка, удаление узлов из середины не должно представлять для нас проблемы.</p>
173
<p>Мы просто скомбинируем написанный нами ранее код:</p>
85
<p>Мы просто скомбинируем написанный нами ранее код:</p>
174
-
<p><strong>Javascript</strong></p>
175
-
<p><strong>Python</strong></p>
176
-
<p><strong>PHP</strong></p>
177
-
<p><strong>Java</strong></p>
178
<p>Единственное нововведение по сравнению с предыдущим кодом заключается в том, что при удалении узла из середины, нам нужно просматривать список на два элемента вперед. Если мы хотим удалить последний узел в списке, нам придется вносить изменения в предпоследний - менять там значение ссылки next.</p>
86
<p>Единственное нововведение по сравнению с предыдущим кодом заключается в том, что при удалении узла из середины, нам нужно просматривать список на два элемента вперед. Если мы хотим удалить последний узел в списке, нам придется вносить изменения в предпоследний - менять там значение ссылки next.</p>
179
<p>Поэтому нам приходится, как особый случай, обрабатывать список из одного элемента. Впрочем, эту проверку мы можем совместить с проверкой на удаление из начала:</p>
87
<p>Поэтому нам приходится, как особый случай, обрабатывать список из одного элемента. Впрочем, эту проверку мы можем совместить с проверкой на удаление из начала:</p>
180
-
<p><strong>Javascript</strong></p>
181
-
<p><strong>Python</strong></p>
182
-
<p><strong>PHP</strong></p>
183
-
<p><strong>Java</strong></p>
184
<p>Меняется и условие в цикле. Если раньше мы проверяли, что current.next не равен null, то теперь мы проверяем current.next.next. Иными словами, вместо ответа на вопрос "есть ли следующий узел в списке", мы отвечаем на вопрос "есть ли узел за следующим узлом".</p>
88
<p>Меняется и условие в цикле. Если раньше мы проверяли, что current.next не равен null, то теперь мы проверяем current.next.next. Иными словами, вместо ответа на вопрос "есть ли следующий узел в списке", мы отвечаем на вопрос "есть ли узел за следующим узлом".</p>
185
<p>В остальном код остается прежним.</p>
89
<p>В остальном код остается прежним.</p>
186
<h2>Сравнение массива и связного списка</h2>
90
<h2>Сравнение массива и связного списка</h2>
187
<p>Осталось разобраться, в каких случаях использовать массив, а в каких связный список. Об этом нам расскажет таблица, где мы сравним производительность операций:</p>
91
<p>Осталось разобраться, в каких случаях использовать массив, а в каких связный список. Об этом нам расскажет таблица, где мы сравним производительность операций:</p>
188
<p>Поиск и в массиве и в списке не очень быстрый, всего O(n).</p>
92
<p>Поиск и в массиве и в списке не очень быстрый, всего O(n).</p>
189
<p>Образно, массив похож на ежедневник, а связный список - на дневник. В ежедневнике нам важно быстро найти дату, а в дневнике - следующую пустую страницу. Если мы захотим найти все записи на заданную тему, нам придется перечитывать что дневник, что ежедневник.</p>
93
<p>Образно, массив похож на ежедневник, а связный список - на дневник. В ежедневнике нам важно быстро найти дату, а в дневнике - следующую пустую страницу. Если мы захотим найти все записи на заданную тему, нам придется перечитывать что дневник, что ежедневник.</p>
190
<h2>Выводы</h2>
94
<h2>Выводы</h2>
191
<p>В этом уроке мы изучили односвязный список. Повторим важные тезисы из урока:</p>
95
<p>В этом уроке мы изучили односвязный список. Повторим важные тезисы из урока:</p>
192
<ul><li>Структура данных - это способ хранения данных в памяти компьютера. Одни и те же операции могут быть быстрыми для одних структур и медленными для других</li>
96
<ul><li>Структура данных - это способ хранения данных в памяти компьютера. Одни и те же операции могут быть быстрыми для одних структур и медленными для других</li>
193
<li>Сложные структуры данных - объекты и массивы - хранятся в куче.</li>
97
<li>Сложные структуры данных - объекты и массивы - хранятся в куче.</li>
194
<li>Массив не очень хорош, если нам приходится добавлять или удалять элементы. Связный список хорош, если необходимо добавлять новые данные в начало</li>
98
<li>Массив не очень хорош, если нам приходится добавлять или удалять элементы. Связный список хорош, если необходимо добавлять новые данные в начало</li>
195
<li>Определение длины и поиск элемента по индексу быстрее в массиве. Массив и связный список похожи на ежедневник и дневник</li>
99
<li>Определение длины и поиск элемента по индексу быстрее в массиве. Массив и связный список похожи на ежедневник и дневник</li>
196
</ul>
100
</ul>