├── 00.Структуры_данных.md
├── LICENSE
├── README.md
├── README.ru.md
└── art
├── AVLtreef.svg.png
├── Red-black_tree_example_with_NIL.svg.png
├── binom_ex_1.jpg
└── logo.png
/00.Структуры_данных.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ### Разница между массивом и связным списком
4 |
5 | Разница между массивом и связным списком заключается в том, что массив представляет собой статическую структуру
6 | данных, а связный список является динамической структурой данных.
7 |
8 | Массив и связный список - это две основные структуры данных, используемые для хранения и управления набором объектов.
9 | Они имеют следующие отличия:
10 |
11 | Память: Массив в основном занимает непрерывный блок памяти, где каждый элемент имеет свой индекс. Связный список
12 | использует "узлы", каждый из которых содержит указатель на следующий элемент списка и данные этого элемента. Это
13 | означает, что элементы списка могут располагаться в любой части памяти, а они связаны только указателями.
14 |
15 | Размер: Размер массива фиксирован, и его нельзя изменить после выделения памяти. В связном списке размер может быть
16 | изменен в любое время, добавлением или удалением элементов из списка.
17 |
18 | Вставка и удаление: В массиве процесс вставки или удаления элемента может быть затруднен, если это приведет к
19 | перераспределению памяти. В связном списке элементы могут быть вставлены или удалены просто путем изменения
20 | указателей.
21 |
22 | Удаление памяти: В массиве освобождение памяти может быть выполнено только после удаления всего массива. В связном
23 | списке память может быть удалена по мере удаления элементов, которые указывают на нее.
24 |
25 | Поиск элементов: В массиве поиск элемента является быстрее, так как элементы находятся в непрерывном блоке. В
26 | связном списке поиск может быть затруднен, так как элементы распределены в разных участках памяти, и требуется
27 | последовательная перестановка, начиная с начала списка.
28 |
29 | Таким образом, в зависимости от задачи, которую необходимо решить, выбирают подходящую структуру данных, либо массив,
30 | либо связный список.
31 |
32 |
33 | ### Применение различных структур данных python в различных алгоритмах
34 |
35 | Структура данных – это способ организации и хранения данных в программе. Они являются важной частью
36 | программирования и позволяют создавать более эффективные алгоритмы и улучшать быстродействие программы.
37 | Рассмотрим несколько примеров применения различных структур данных в алгоритмах:
38 |
39 | Структуры данных в Python - это способы организации и хранения данных в программе. В Python доступны следующие
40 | структуры данных:
41 |
42 | Списки (Lists) - это упорядоченный набор элементов. Списки можно изменять и использовать для хранения любых типов данных.
43 |
44 | Кортежи (Tuples) - это упорядоченный набор элементов, но в отличие от списков они не могут быть изменены после создания.
45 |
46 | Множества (Sets) - это коллекция уникальных элементов. Элементы множества должны быть хешируемыми.
47 |
48 | Словари (Dictionaries) - это набор пар ключ-значение. Ключи должны быть уникальными и хешируемыми.
49 |
50 | В Python также доступны специализированные структуры данных, такие как очереди, стеки и древовидные структуры. Чтобы
51 | использовать эти структуры данных, вы можете использовать библиотеки, такие как collections или heapq.
52 |
53 |
54 | #### Списки:
55 | Списки являются одной из самых используемых структур данных в Python. Они представляют собой упорядоченный
56 | набор элементов, которые могут быть изменены. Списки могут использоваться для хранения различных типов данных,
57 | включая числа, строки и другие списки. Например, в алгоритмах поиска и сортировки списки используются для хранения
58 | значений и их последующей обработки.
59 |
60 | Список (List) - это структура данных, которая позволяет хранить последовательность элементов. Основное применение
61 | списков - организация и хранение информации, упорядочивание и изменение элементов.
62 | Пример использования списка в алгоритме: сортировка пузырьком. В данном случае, мы можем использовать список для
63 | хранения неупорядоченного набора элементов, а затем, поочередно сравнивать каждый элемент с последующим и менять их
64 | местами, если это необходимо.
65 |
66 | Python использует динамическое управление памятью, что означает, что память присваивается и освобождается
67 | автоматически во время выполнения программы.
68 |
69 | В Python списки могут быть изменяемыми объектами, и поэтому память, выделенная под список, может увеличиваться и
70 | уменьшаться в зависимости от того, как его изменяют.
71 |
72 | Когда вы создаете список, Python выделяет память под каждый элемент списка. Если вам нужно создать большой список,
73 | вы можете снизить использование памяти, используя генераторы списков вместо циклов.
74 |
75 | Кроме того, если вы хотите избежать необходимости создания копии списка, вы можете использовать срезы ([start:end]),
76 | чтобы получить подмножество списка.
77 |
78 | Некоторые полезные советы для уменьшения использования памяти при работе со списками в Python:
79 |
80 | - Используйте генераторы списков вместо циклов, чтобы создавать списки
81 | - Используйте срезы ([start:end]) вместо копирования списков
82 | - Если вы работаете с большими объемами данных, можете использовать сторонние библиотеки, такие как NumPy или Pandas,
83 | которые оптимизированы для работы с массивами данных.
84 | - Если вы должны добавить много элементов в список, используйте метод extend вместо append. Метод append может
85 | привести к созданию большого количества временных списков, что потребует дополнительного использования памяти.
86 |
87 | Независимо от того, как вы используете списки, помните, что Python автоматически управляет памятью, поэтому вы
88 | можете не беспокоиться о том, что язык повредит системе из-за использования большого количества памяти. Однако
89 | оптимизация использования памяти может помочь повысить производительность вашей программы, особенно если вы
90 | работаете с большими объемами данных.
91 |
92 | Пример алгоритма python с использованием списка:
93 | ```python
94 | def find_min_element(nums):
95 | """
96 | Функция находит наименьший элемент в списке nums и возвращает его
97 | """
98 | min_element = nums[0] # присваиваем первый элемент в списке минимальному элементу
99 | for num in nums: # для каждого числа в списке
100 | if num < min_element: # если число меньше минимального элемента
101 | min_element = num # присваиваем минимальному элементу число
102 | return min_element # возвращаем найденный минимальный элемент
103 |
104 | my_list = [5, 8, 3, 2, 9, 1]
105 | print(find_min_element(my_list)) # вывод: 1
106 | ```
107 |
108 | #### Словари:
109 | Словари – это структура данных, которая представляет собой неупорядоченный набор пар «ключ-значение». Они
110 | используются для быстрого доступа к данным с помощью ключа. В алгоритмах, связанных с обработкой больших объемов
111 | данных, словари могут быть использованы для хранения и обработки данных.
112 |
113 | Каждый раз, когда вы добавляете новый элемент в словарь, вы резервируете часть памяти для хранения этого элемента.
114 | Также вы можете использовать memory_profiler для измерения использования памяти при работе с вашим словарем.
115 |
116 | Ниже приведены некоторые рекомендации, которые могут помочь вам сэкономить память при работе со словарем.
117 |
118 | - Используйте defaultdict, когда это уместно. defaultdict - это подкласс словаря Python, который позволяет задавать
119 | значение по умолчанию для ключей, которые еще не существуют в словаре. Если вы используете обычный словарь Python и
120 | пытаетесь получить значение для ключа, которого нет в словаре, вы получите KeyError.
121 | - Если ваш словарь использует целочисленные ключи, и номера ключей имеют последовательный порядок, вы можете
122 | использовать список вместо словаря.
123 | - Если ваш словарь содержит множество ключей, которые являются строками, вы можете использовать OrderedDict.
124 | - Если вы работаете с большими объемами данных, вы можете использовать модуль shelve для более эффективного хранения
125 | словарей на жестком диске.
126 |
127 | Пример алгоритма python с использованием словаря:
128 | ```python
129 | # Создаем словарь с некоторыми значениями
130 | my_dict = {'apple': 10, 'orange': 15, 'banana': 20}
131 |
132 | # Выводим все ключи из словаря
133 | print("Все ключи в словаре:")
134 | for key in my_dict:
135 | print(key)
136 |
137 | # Выводим все значения из словаря
138 | print("Все значения в словаре:")
139 | for value in my_dict.values():
140 | print(value)
141 |
142 | # Выводим все пары ключ-значение из словаря
143 | print("Все пары ключ-значение в словаре:")
144 | for key, value in my_dict.items():
145 | print(key, "->", value)
146 |
147 | # Удаляем элемент из словаря
148 | del my_dict['banana']
149 | print("Словарь после удаления элемента:")
150 | print(my_dict)
151 |
152 | # Добавляем новый элемент в словарь
153 | my_dict['pear'] = 25
154 | print("Словарь после добавления нового элемента:")
155 | print(my_dict)
156 |
157 | # Все ключи в словаре:
158 | # apple
159 | # orange
160 | # banana
161 | # Все значения в словаре:
162 | # 10
163 | # 15
164 | # 20
165 | # Все пары ключ-значение в словаре:
166 | # apple -> 10
167 | # orange -> 15
168 | # banana -> 20
169 | # Словарь после удаления элемента:
170 | # {'apple': 10, 'orange': 15}
171 | # Словарь после добавления нового элемента:
172 | # {'apple': 10, 'orange': 15, 'pear': 25}
173 | ```
174 |
175 | #### Кортежи:
176 | Кортежи – это неизменяемые упорядоченные наборы элементов. Кортежи используются для хранения данных,
177 | которые не должны изменяться в процессе выполнения программы. В алгоритмах, которые имеют дело с множеством данных,
178 | кортежи могут использоваться как структура для хранения пар значений.
179 |
180 | Кортежи (tuples) в Python хранятся в памяти как неизменяемые объекты. Это означает, что когда вы создаете кортеж, вы
181 | не можете изменить его содержимое в будущем.
182 |
183 | Кортежи занимают меньше памяти, чем списки, потому что не имеют дополнительных методов для изменения их содержимого.
184 | Кроме того, кортежи используют только одну ссылку на объект, поэтому они занимают меньше места в памяти.
185 |
186 | Например, если создать список и кортеж с одинаковыми элементами, для кортежа потребуется меньше памяти:
187 |
188 | ```python
189 | import sys
190 |
191 | my_list = [1, 2, 3, 'a', 'b', 'c']
192 | my_tuple = (1, 2, 3, 'a', 'b', 'c')
193 |
194 | print(sys.getsizeof(my_list)) # Вывод: 88
195 | print(sys.getsizeof(my_tuple)) # Вывод: 64
196 | ```
197 |
198 | Как видите, размер кортежа почти на треть меньше, чем размер списка. Это связано с тем, что список имеет
199 | дополнительные методы (append, extend, insert, remove), которые могут изменить его размер, в то время как кортеж не
200 | может быть изменен.
201 |
202 | Пример алгоритма python с использованием кортежа:
203 | ```python
204 | '''
205 | Этот алгоритм определяет кортеж из нескольких элементов, выводит все элементы кортежа, создает новый кортеж
206 | с помощью конкатенации, получает элементы кортежа по индексу, считает количество элементов в кортеже и удаляет кортеж.
207 | '''
208 | # Определяем кортеж из нескольких элементов
209 | tup1 = ('apple', 'banana', 'cherry', 'orange', 'peach')
210 |
211 | # Выводим все элементы кортежа
212 | for item in tup1:
213 | print(item)
214 |
215 | # Определяем новый кортеж посредством конкатенации
216 | tup2 = tup1 + ('grape', 'kiwi')
217 |
218 | # Выводим новый кортеж
219 | print(tup2)
220 |
221 | # Получаем элементы кортежа по индексу
222 | print(tup1[0])
223 | print(tup1[2])
224 |
225 | # Считаем количество элементов в кортеже
226 | print(len(tup1))
227 |
228 | # Удаляем кортеж
229 | del tup2
230 | ```
231 |
232 | #### Множества:
233 | Множества – это структуры данных, которые представляют собой неупорядоченные наборы элементов без
234 | повторений. Множества часто используются в алгоритмах проверки уникальности элементов или для нахождения пересечения
235 | и объединения множеств.
236 |
237 | В Python множества (set) имеют переменный размер, что означает, что они будут использовать сколько-то памяти в
238 | зависимости от количества элементов в них.
239 |
240 | Операция создания множества в Python выделяет некоторую память для хранения элементов множества. Эта память не
241 | освобождается до тех пор, пока множество не будет удалено (удаление происходит при помощи оператора del или после
242 | того, как переменная-ссылка на множество перестанет указывать на него).
243 |
244 | В общем случае, использование множества в Python не является особенно затратным по памяти. Если множество содержит
245 | несколько тысяч элементов или меньше, его размер не должен стать проблемой даже на устройствах с ограниченными
246 | ресурсами.
247 |
248 | Однако, если у вас есть большое количество элементов, то использование множества может привести к большому
249 | потреблению памяти. В этом случае может помочь использование специализированных структур данных, таких как битовые
250 | массивы (bit arrays), которые могут использоваться, если вы работаете с множеством значений из определенного
251 | диапазона, или Bloom filters – наборы хэш-функций, которые могут отвечать на вопрос о том, присутствует ли элемент в
252 | множестве или нет.
253 |
254 | В любом случае, если вы обнаруживаете, что использование множества приводит к проблемам с памятью, наилучшей
255 | стратегией может быть оптимизация алгоритма, который вы используете, или переход на более масштабируемые решения,
256 | использующие например базы данных или хранилища key-value.
257 |
258 |
259 | Пример алгоритма python с использованием множества:
260 | ```python
261 | '''В этом примере создаются два множества, set1 и set2. Оператор & используется для нахождения пересечения множеств,
262 | то есть для поиска элементов, которые содержатся в обоих множествах. Результат пересечения сохраняется в
263 | переменной intersection, а затем выводится на экран с помощью функции print().
264 | '''
265 |
266 | # Создание множеств
267 | set1 = set([1, 2, 3, 4])
268 | set2 = set([3, 4, 5, 6])
269 |
270 | # Использование оператора пересечения
271 | intersection = set1 & set2
272 |
273 | # Вывод результата на экран
274 | print("Пересечение множеств:", intersection)
275 | ```
276 |
277 | #### Очереди и стеки:
278 | Очереди и стеки – это структуры данных, которые используются для организации последовательности
279 | элементов. Очереди используются для добавления элементов в хвост очереди и извлечения элементов из головы очереди, а
280 | стеки используются для добавления и извлечения элементов только с одного конца. Очереди и стеки часто используются в
281 | алгоритмах поиска и обхода графов, например в алгоритме поиска в глубину.
282 |
283 | Стек (Stack) - это структура данных, которая позволяет хранить элементы в порядке "последний вошел - первый вышел".
284 | Стеки используются в алгоритмах, где необходимо сохранить последовательность выполнения операций и выполнить их в
285 | обратном порядке.
286 | Пример использования стека в алгоритме: обход дерева в глубину. В данном случае, мы используем стек, чтобы
287 | сохранить последовательность посещения узлов дерева и выполнить их в обратном порядке. Такой подход позволяет
288 | обойти все узлы дерева и выполнить необходимые операции.
289 |
290 | Очередь (Queue) - это структура данных, которая позволяет хранить элементы в порядке "первый вошел - первый вышел".
291 | Очереди используются в алгоритмах, где необходимо сохранить последовательность выполнения операций и выполнить их в
292 | порядке очереди.
293 | Пример использования очереди в алгоритме: алгоритм обхода в ширину в графе. В данном случае, мы используем очередь,
294 | чтобы сохранить последовательность посещения вершин графа и выполнить их в порядке очереди. Это позволяет обойти все
295 | вершины графа и выполнить необходимые операции.
296 |
297 | Массив (Array) - это структура данных, которая позволяет хранить несколько элементов одного типа. Массивы
298 | используются для организации информации, а также для улучшения быстродействия при доступе к элементам.
299 | Пример использования массива в алгоритме: двоичный поиск. Двоичный поиск - это алгоритм поиска элемента в
300 | упорядоченном массиве. Он работает быстрее, чем последовательный поиск, потому что использует принцип деления
301 | массива на половинки и сравнения элемента с значением середины. Таким образом, массив позволяет быстро осуществить
302 | доступ к элементам и выполнить необходимые операции.
303 |
304 | ### Плюсы и минусы использования базовых структур данных Python
305 | Плюсы использования базовых структур данных Python:
306 | - Удобство использования - базовые структуры данных в Python легко создаются и манипулируются. Например, чтобы создать
307 | список, достаточно указать элементы в квадратных скобках.
308 | - Высокая производительность - базовые структуры данных в Python имеют высокую скорость работы, что позволяет быстро
309 | обрабатывать большие объемы данных.
310 | - Разнообразие структур данных - Python поддерживает множество базовых структур данных, включая списки, кортежи,
311 | словари и множества, что позволяет выбрать наиболее подходящую структуру данных для конкретной задачи.
312 | - Широкое использование - базовые структуры данных Python используются в различных областях, от научных исследований
313 | до веб-разработки.
314 |
315 | Минусы использования базовых структур данных Python:
316 | - Низкая эффективность работы с большими объемами данных - при работе с очень большими объемами данных, базовые структуры данных Python могут стать медленными, и потребуется использовать специализированные библиотеки для обработки данных.
317 | - Ограниченная функциональность - базовые структуры данных Python имеют ограниченную функциональность по сравнению со специализированными структурами данных, такими как массивы или деревья.
318 | - Нет поддержки многопоточности - базовые структуры данных Python не могут использоваться эффективно для многопоточных приложений и требуют специализированных библиотек.
319 | - Нарушение инкапсуляции - базовые структуры данных Python не имеют инкапсулированных методов, что может привести к
320 | ошибкам при работе с ними.
321 |
322 | #### Что такое хэш-таблицы и как реализовать хэш-таблицы в Python
323 | Хэш-таблица - это структура данных, которая позволяет эффективно хранить и получать значения по ключу. Она
324 | использует хэш-функцию для преобразования ключа в индекс массива. Каждый индекс в массиве связан с некоторым
325 | значением. Хэш-таблица позволяет быстро находить значения по ключу, используя только одну операцию доступа к памяти.
326 | В Python хэш-таблица реализуется в виде словаря (dict). Внутри словаря данные хранятся в виде пар ключ-значение, где
327 | ключи являются уникальными хэш-значениями, а значения представляют собой соответствующие объекты.
328 |
329 | При добавлении нового элемента в словарь, ключ этого элемента вычисляется с помощью хэш-функции, которая преобразует
330 | произвольную строку или число в уникальное число фиксированной длины. Затем этот ключ используется для индексации
331 | соответствующего места в памяти, где хранится значение.
332 |
333 | Для решения коллизий, т.е. ситуаций, когда два разных ключа имеют одинаковое хэш-значение, Python использует метод
334 | цепочек. При этом для каждого слота в таблице выделяется связный список, в который добавляются элементы с
335 | одинаковыми ключами хэш-функции. Когда количество элементов в списке становится слишком большим, Python
336 | автоматически перестраивает таблицу, увеличивая ее размер.
337 |
338 | Таким образом, хэш-таблица в Python реализована как динамическая структура данных, позволяющая эффективно хранить и
339 | быстро находить элементы по ключу.
340 |
341 | ```python
342 | # В Python реализация хэш-таблицы доступна в виде словарей (dict). Для добавления элемента в словарь используется
343 | # следующий синтаксис:
344 |
345 | my_dict = {}
346 | my_dict[key] = value
347 |
348 |
349 | # Для получения значения по ключу нужно использовать следующий синтаксис:
350 | my_dict[key]
351 | ```
352 |
353 | Python также позволяет использовать пользовательские классы в качестве ключей в словарях, если эти классы реализуют
354 | методы __hash__() и __eq__().
355 |
356 | Подробный пример реализации хэш-таблицы в Python:
357 |
358 | ```python
359 | class HashTable: # Создаём класс HashTable
360 | def __init__(self): # Определяем метод __init__ - инициализация или сборка класса
361 | self.size = 11 # определяем размер хэш-таблицы self.size
362 | self.slots = [None] * self.size # массив для ключей self.slots Для начальной инициализации используем None.
363 | self.data = [None] * self.size # массив для значений self.data Для начальной инициализации используем None.
364 |
365 | def put(self, key, data): # Определяем метод put. В нём передаем ключ key и значение data.
366 | '''Определяем хэш-значение ключа hash_value с помощью метода self.hash_function.
367 | '''
368 | hash_value = self.hash_function(key, len(self.slots))
369 |
370 | ''' Если на данном хэш-значении в массиве self.slots пусто,
371 | то заполняем соответствующие места ключа и значения новыми значениями.'''
372 | if self.slots[hash_value] is None:
373 | self.slots[hash_value] = key
374 | self.data[hash_value] = data
375 | else:
376 | '''Если на данном хэш-значении уже есть ключ, то заменяем старое значение на новое.'''
377 | if self.slots[hash_value] == key:
378 | self.data[hash_value] = data # replace
379 | else:
380 | '''Если на данном хэш-значении пусто и
381 | это не первоначальная итерация, то находим следующее пустое место в хэш-таблице с помощью метода self.rehash,
382 | который возвращает новое хэш-значение на основе старого (линейное пробирование).'''
383 | next_slot = self.rehash(hash_value, len(self.slots))
384 | while self.slots[next_slot] is not None and self.slots[next_slot] != key:
385 | next_slot = self.rehash(next_slot, len(self.slots))
386 |
387 | ''' Если нашёлся свободный слот, то записываем ключ и значение на него.'''
388 | if self.slots[next_slot] is None:
389 | self.slots[next_slot] = key
390 | self.data[next_slot] = data
391 | else:
392 | self.data[next_slot] = data # replace
393 |
394 | def get(self, key):
395 | '''Определяем метод get. В нём передаем ключ key. Определяем начальное позицию start_slot в таблице с
396 | помощью метода self.hash_function.
397 |
398 | '''
399 | start_slot = self.hash_function(key, len(self.slots))
400 |
401 | # Создаем переменную для значения data, а также переменные для управления циклом: stop, found и position.
402 | data = None
403 | stop = False
404 | found = False
405 | position = start_slot
406 | while self.slots[position] is not None and not found and not stop:
407 | '''Стартуем цикл. Если на текущей позиции в таблице находится нужный ключ, то
408 | записываем соответствующее значение в переменную data и устанавливаем флаг found в значение True.'''
409 | if self.slots[position] == key:
410 | found = True
411 | data = self.data[position]
412 | else:
413 | '''Если на текущей позиции в таблице находится не нужный ключ, то переходим на следующую позицию с
414 | помощью метода self.rehash.'''
415 | position = self.rehash(position, len(self.slots))
416 | if position == start_slot:
417 | '''Если дошли до начальной позиции, то выходим из цикла. Возвращаем значение data, которое либо
418 | осталось None, либо содержит нужное значение.'''
419 | stop = True
420 | return data
421 |
422 | def hash_function(self, key, size):
423 | '''Определяем метод hash_function, который принимает ключ key и размер хэш-таблицы size. Возвращает остаток
424 | от деления ключа на размер таблицы.
425 | '''
426 | return key % size
427 |
428 | def rehash(self, old_hash, size):
429 | '''Определяем метод rehash, который принимает старый хэш old_hash и размер таблицы size. Возвращает новый хэш на основе
430 | текущего, увеличенного на единицу. Если новый хэш больше размера таблицы, то он сбрасывается на ноль.
431 | Применяется линейное пробирование.
432 | '''
433 | return (old_hash + 1) % size
434 | ```
435 |
436 | Пример реализации хэш-таблицы в Python:
437 | ```python
438 | '''
439 | Класс HashTable имеет методы insert, search и delete, которые соответственно позволяют добавлять элементы в
440 | хэш-таблицу, искать их и удалять. Хэш-функция просто берет остаток от деления ключа на размер таблицы. Если в один
441 | слот попадает несколько элементов, они хранятся в виде кортежа (ключ, значение). Если ключ уже есть в таблице при
442 | добавлении, то его значение заменяется новым. При поиске элемента, хэш-таблица вычисляет для него значение
443 | хэш-функции и итерируется по соответствующему слоту, пока не находится элемент с нужным ключом. При удалении
444 | элемента происходит аналогичный поиск, и если элемент найден, он удаляется из слота.
445 | '''
446 | class HashTable:
447 | def __init__(self, size=10):
448 | self.size = size
449 | self.table = [[] for _ in range(self.size)]
450 |
451 | def _hash_function(self, key):
452 | return key % self.size
453 |
454 | def insert(self, key, value):
455 | hash_value = self._hash_function(key)
456 | slot = self.table[hash_value]
457 | for i, (k, v) in enumerate(slot):
458 | if k == key:
459 | slot[i] = (key, value)
460 | break
461 | else:
462 | slot.append((key, value))
463 |
464 | def search(self, key):
465 | hash_value = self._hash_function(key)
466 | slot = self.table[hash_value]
467 | for k, v in slot:
468 | if k == key:
469 | return v
470 | raise KeyError(key)
471 |
472 | def delete(self, key):
473 | hash_value = self._hash_function(key)
474 | slot = self.table[hash_value]
475 | for i, (k, v) in enumerate(slot):
476 | if k == key:
477 | del slot[i]
478 | break
479 | else:
480 | raise KeyError(key)
481 | ```
482 |
483 | #### Приоритетные очереди и способы их реализации
484 | Чтобы понять приоритетные очереди в Python, нам нужно сначала понять, что такое очередь.
485 |
486 | Очередь — это структура данных, в которой элементы хранятся в порядке «первым поступил — первым обслужен» (FIFO).
487 | Это означает, что первый элемент, добавленный в очередь, будет первым элементом, который будет удален. Очереди могут
488 | быть реализованы с помощью списков или очередей в Python.
489 |
490 | Теперь приоритетная очередь — это разновидность очереди, в которой каждый элемент имеет связанный с ним приоритет.
491 | Элементы удаляются из очереди в порядке их приоритета, а не в порядке их добавления. Другими словами, элемент с
492 | наивысшим приоритетом удаляется первым.
493 |
494 | В Python мы можем реализовать приоритетную очередь, используя модуль heapq, который предоставляет функции для
495 | создания структур данных кучи и управления ими. Куча — это двоичная древовидная структура, в которой каждый узел
496 | имеет значение приоритета, а его дочерние элементы имеют значения приоритета, которые ниже или равны родительскому
497 | узлу.
498 |
499 | Модуль heapq предоставляет такие функции, как heappush для добавления элемента в очередь приоритетов, heappop для
500 | удаления элемента с наивысшим приоритетом и heapreplace для замены элемента с наивысшим приоритетом новым элементом.
501 |
502 | Прежде всего, в Python существуют несколько способов реализации приоритетных очередей:
503 |
504 | - Использование встроенного модуля heapq
505 | - Использование классической реализации на основе двоичной кучи (binary heap)
506 | - Использование модуля queue с аргументом PriorityQueue
507 | Для примера кода и дальнейшего описания выберем первый способ - использование модуля heapq.
508 | ```python
509 | '''В этом примере мы создаем список чисел, который затем преобразуем в кучу с помощью функции heapq.heapify(). Затем
510 | мы поочередно извлекаем элементы из кучи с помощью функции heapq.heappop(), которая всегда извлекает минимальный
511 | элемент.
512 | '''
513 | import heapq
514 |
515 | # Создаем некоторые данные
516 | data = [5, 3, 7, 1, 2, 8, 4]
517 |
518 | # Преобразуем список в кучу (heap)
519 | heapq.heapify(data)
520 |
521 | # Извлекаем элементы из кучи
522 | while data:
523 | print(heapq.heappop(data))
524 | ```
525 |
526 | ```python
527 | import heapq
528 |
529 | # создадим пустую кучу
530 | heap = []
531 |
532 | # добавить элементы в кучу со значениями приоритета
533 | heapq.heappush(heap, (1, 'first element'))
534 | heapq.heappush(heap, (3, 'third element'))
535 | heapq.heappush(heap, (2, 'second element'))
536 |
537 | # удалим элементы из кучи по приоритету
538 | print(heapq.heappop(heap)) # (1, 'first element')
539 | print(heapq.heappop(heap)) # (2, 'second element')
540 | print(heapq.heappop(heap)) # (3, 'third element')
541 | ```
542 |
543 | ### Знание расширенных структур данных
544 | #### B-дерево
545 | B-дерево - это структура данных, которая используется для хранения и упорядочения больших объемов данных. Оно
546 | является сбалансированным деревом, то есть каждая ветвь содержит примерно одинаковое число элементов. Особенностью
547 | B-дерева является наличие нескольких ключей на узле, что позволяет улучшить эффективность поиска данных.
548 | Б-дерево - это сбалансированное дерево поиска, которое предназначено для уменьшения количества обращений к диску при
549 | поиске данных на жестком диске. Оно состоит из узлов разного размера, которые хранят ключи и ссылки на другие узлы.
550 |
551 |
552 | Пример реализации B-дерева на языке Python:
553 | ```python
554 | '''Это минимальная реализация классов BNode и BTree на Python. Она позволяет вставлять новые элементы в дерево и
555 | выводить его содержимое на экран. В данной реализации используется параметр t, который определяет минимальное
556 | количество ключей на узле.
557 | '''
558 | class BNode:
559 | def __init__(self, leaf=False):
560 | self.leaf = leaf
561 | self.keys = []
562 | self.child = []
563 |
564 |
565 | class BTree:
566 | def __init__(self, t):
567 | self.root = BNode(True)
568 | self.t = t
569 |
570 | def insert(self, k):
571 | root = self.root
572 | if len(root.keys) == (2 * self.t) - 1:
573 | new_root = BNode()
574 | self.root = new_root
575 | new_root.child.insert(0, root)
576 | self._split(new_root, 0)
577 | self._insert_non_full(new_root, k)
578 | else:
579 | self._insert_non_full(root, k)
580 |
581 | def _insert_non_full(self, node, k):
582 | i = len(node.keys) - 1
583 | if node.leaf:
584 | node.keys.append(0)
585 | while i >= 0 and k < node.keys[i]:
586 | node.keys[i+1] = node.keys[i]
587 | i -= 1
588 | node.keys[i+1] = k
589 | else:
590 | while i >= 0 and k < node.keys[i]:
591 | i -= 1
592 | if len(node.child[i+1].keys) == (2*self.t)-1:
593 | self._split(node, i+1)
594 | if k > node.keys[i+1]:
595 | i += 1
596 | self._insert_non_full(node.child[i+1], k)
597 |
598 | def _split(self, node, i):
599 | t = self.t
600 | y = node.child[i]
601 | z = BNode(y.leaf)
602 | node.child.insert(i+1, z)
603 | node.keys.insert(i, y.keys[t-1])
604 | z.keys = y.keys[t:(2*t)-1]
605 | y.keys = y.keys[0:t-1]
606 | z.child = y.child[t:(2*t)]
607 | y.child = y.child[0:t-1]
608 |
609 | def print_tree(self, node=None, level=0):
610 | if node is None:
611 | node = self.root
612 |
613 | print('Level', level, '->', len(node.keys), end=":")
614 | for i in node.keys:
615 | print(i, end=' ')
616 | print()
617 |
618 | if not node.leaf:
619 | for i in node.child:
620 | self.print_tree(i, level + 1)
621 | ```
622 |
623 |
624 |
625 | Вот пример реализации Б-дерева и его использования на Python:
626 | ```python
627 | '''В этом коде мы определяем класс «BTreeNode», который представляет собой узел Б-дерева. Узел содержит список
628 | ключей и ссылки на другие узлы. Класс «BTree» представляет собой само дерево. Метод «insert» вставляет новый ключ в
629 | дерево, метод «_split_child» разделяет узел и метод «_insert_nonfull» вставляет новый ключ в неполный узел. '''
630 | class BTreeNode:
631 | def __init__(self, leaf=False):
632 | self.leaf = leaf
633 | self.keys = []
634 | self.child = []
635 |
636 | class BTree:
637 | def __init__(self, degree):
638 | self.root = BTreeNode(True)
639 | self.degree = degree
640 |
641 | def insert(self, k):
642 | r = self.root
643 | if len(r.keys) == (2*self.degree)-1:
644 | s = BTreeNode()
645 | self.root = s
646 | s.child.insert(0, r)
647 | self._split_child(s, 0)
648 | self._insert_nonfull(s, k)
649 | else:
650 | self._insert_nonfull(r, k)
651 |
652 | def _insert_nonfull(self, x, k):
653 | i = len(x.keys)-1
654 | if x.leaf:
655 | x.keys.append(0)
656 | while i >= 0 and k < x.keys[i]:
657 | x.keys[i+1] = x.keys[i]
658 | i -= 1
659 | x.keys[i+1] = k
660 | else:
661 | while i >= 0 and k < x.keys[i]:
662 | i -= 1
663 | i += 1
664 | if len(x.child[i].keys) == (2*self.degree)-1:
665 | self._split_child(x, i)
666 | if k > x.keys[i]:
667 | i += 1
668 | self._insert_nonfull(x.child[i], k)
669 |
670 | def _split_child(self, x, i):
671 | t = self.degree
672 | y = x.child[i]
673 | z = BTreeNode(leaf=y.leaf)
674 |
675 | x.child.insert(i+1, z)
676 | x.keys.insert(i, y.keys[t-1])
677 |
678 | z.keys = y.keys[t:(2*t)-1]
679 | y.keys = y.keys[0:t-1]
680 |
681 | if not y.leaf:
682 | z.child = y.child[t:(2*t)]
683 | y.child = y.child[0:t-1]
684 |
685 | def search(self, k, x=None):
686 | if isinstance(x, BTreeNode):
687 | i = 0
688 | while i < len(x.keys) and k > x.keys[i]:
689 | i += 1
690 | if i < len(x.keys) and k == x.keys[i]:
691 | return x, i
692 | elif x.leaf:
693 | return None
694 | else:
695 | return self.search(k, x.child[i])
696 | else:
697 | return self.search(k, self.root)
698 |
699 | def __str__(self):
700 | r = self.root
701 | return self._to_string(r)
702 |
703 | def _to_string(self, x, lvl=0):
704 | ret = " " * lvl
705 | if x is None:
706 | return ret + "None\n"
707 | else:
708 | if x.leaf:
709 | ret += "Leaf -> "
710 | else:
711 | ret += "Node -> "
712 | ret += str(x.keys) + "\n"
713 | for i in range(len(x.child)):
714 | ret += self._to_string(x.child[i], lvl + 1)
715 | return ret
716 |
717 | '''В этом примере мы создаем Б-дерево с минимальной степенью 3 и вставляем несколько ключей в дерево. Затем мы
718 | выполняем поиск ключей в дереве с помощью метода «search». Если ключ находится в дереве, метод «search» возвращает
719 | узел и индекс ключа в этом узле. Если ключ не найден, метод возвращает значение None.
720 | '''
721 | t = BTree(3)
722 | t.insert(8)
723 | t.insert(18)
724 | t.insert(2)
725 | t.insert(1)
726 | t.insert(20)
727 | t.insert(50)
728 | t.insert(23)
729 |
730 | print(t.search(2))
731 | print(t.search(23))
732 | print(t.search(300))
733 | ```
734 |
735 | #### Биномиальная куча
736 | Биномиальная куча (binomial heap) - это структура данных, которая представляет собой лес биномиальных деревьев. Она
737 | позволяет эффективно добавлять, удалять и обновлять элементы, а также искать минимальный элемент.
738 |
739 | Биномиальный лес – это семейство биномиальных деревьев.
740 |
741 | Биномиальное дерево высоты h = 0 состоит из одной единственной вершины, биномиальное дерево Bk высоты h = k
742 | образуется присоединением биномиального дерева высоты k-1 к корню другого биномиального дерева высоты k-1. Ниже
743 | показаны биномиальные деревья B0, B1, B2, B3 и B4.
744 |
745 |
746 |
747 | Ниже приведена простая реализация биномиальной кучи на языке Python.
748 | ```python
749 | '''В этой реализации биномиальной кучи используется вложенный класс Node, который представляет вершину биномиального
750 | дерева. Куча хранится как односвязный список биномиальных деревьев в порядке убывания их размеров. Операция
751 | объединения двух биномиальных деревьев происходит с помощью рекурсивного объединения их корневых списков. Операция
752 | удаления минимального элемента происходит поиском минимального узла в списке и его последующим удалением.
753 | '''
754 | class BinomialHeap:
755 | class Node:
756 | def __init__(self, key, value):
757 | self.key = key
758 | self.value = value
759 | self.degree = 0
760 | self.parent = None
761 | self.child = None
762 | self.sibling = None
763 |
764 | def __init__(self):
765 | self.head = None
766 |
767 | def push(self, key, value):
768 | new_node = self.Node(key, value)
769 | self.head = self._merge_lists(self.head, new_node)
770 |
771 | def pop(self):
772 | if not self.head:
773 | return None
774 |
775 | min_node = self.head
776 | prev_node = None
777 | curr_node = min_node
778 |
779 | while curr_node.sibling:
780 | if curr_node.sibling.key < min_node.key:
781 | min_node = curr_node.sibling
782 | prev_node = curr_node
783 | curr_node = curr_node.sibling
784 |
785 | if prev_node:
786 | prev_node.sibling = min_node.sibling
787 | elif min_node == self.head:
788 | self.head = min_node.sibling
789 |
790 | child_head = min_node.child
791 | if child_head:
792 | child_head.parent = None
793 | curr_node = child_head
794 | while curr_node:
795 | next_node = curr_node.sibling
796 | curr_node.sibling = curr_node.parent = None
797 | self.head = self._merge_lists(self.head, curr_node)
798 | curr_node = next_node
799 |
800 | return min_node.value
801 |
802 | def peek(self):
803 | return self.head.value if self.head else None
804 |
805 | def _merge_lists(self, head1, head2):
806 | if not head1:
807 | return head2
808 | if not head2:
809 | return head1
810 |
811 | if head1.key < head2.key:
812 | head1.sibling = self._merge_lists(head1.sibling, head2)
813 | return head1
814 | else:
815 | head2.sibling = self._merge_lists(head2.sibling, head1)
816 | return head2
817 | ```
818 |
819 | #### Фибоначчиева куча
820 | Фибоначчиева куча (англ. Fibonacci heap) — структура данных, представляющая собой набор деревьев, упорядоченных в
821 | соответствии со свойством неубывающей пирамиды. Фибоначчиевы кучи были введены Майклом Фредманом и Робертом
822 | Тарьяном в 1984 году.
823 |
824 | Структура является реализацией абстрактного типа данных «Очередь с приоритетом», и замечательна тем, что операции,
825 | в которых не требуется удаление, имеют амортизированное время работы. Кроме стандартных операций INSERT, MIN,
826 | DECREASE-KEY, фибоначчиева куча позволяет за время равное время выполнять операцию UNION слияния двух куч.
827 |
828 | ```python
829 | '''Вы можете использовать функцию fibonacci_heap() для создания новой кучи Фибоначчи. Затем вы можете выполнить
830 | операции вставки и удаления элементов из кучи. Например, для вставки элемента используйте метод insert(val), а для
831 | извлечения минимального элемента - метод extract_min(). '''
832 | def fibonacci_heap():
833 | class Node:
834 | def __init__(self, val):
835 | self.val = val
836 | self.child = None
837 | self.left = None
838 | self.right = None
839 | self.marked = False
840 | self.degree = 0
841 | self.parent = None
842 |
843 | def __str__(self):
844 | return str(self.val)
845 |
846 | class FibonacciHeap:
847 | def __init__(self):
848 | self.min = None
849 | self.trees = []
850 | self.num_nodes = 0
851 |
852 | def insert(self, val):
853 | node = Node(val)
854 | if self.min == None:
855 | self.min = node
856 | else:
857 | self._add_to_root_list(node)
858 | if node.val < self.min.val:
859 | self.min = node
860 | self.num_nodes += 1
861 |
862 | def _add_to_root_list(self, node):
863 | node.left = None
864 | node.right = None
865 | self.trees.append(node)
866 |
867 | def extract_min(self):
868 | z = self.min
869 | if z != None:
870 | for c in z.child:
871 | self._add_to_root_list(c)
872 | c.parent = None
873 | self.trees.remove(z)
874 | self.num_nodes -= 1
875 | if z == z.right:
876 | self.min = None
877 | else:
878 | self.min = z.right
879 | self._consolidate()
880 | return z
881 |
882 | def _consolidate(self):
883 | A = [None] * self.num_nodes
884 | for i in range(len(self.trees)):
885 | x = self.trees[i]
886 | d = x.degree
887 | while A[d] != None:
888 | y = A[d]
889 | if x.val > y.val:
890 | temp = x
891 | x = y
892 | y = temp
893 | self._fib_heap_link(y, x)
894 | A[d] = None
895 | d += 1
896 | A[d] = x
897 | self.min = None
898 | for i in range(len(A)):
899 | if A[i] != None:
900 | if self.min == None:
901 | self.trees = []
902 | self.min = A[i]
903 | else:
904 | self._add_to_root_list(A[i])
905 | if A[i].val < self.min.val:
906 | self.min = A[i]
907 |
908 | def _fib_heap_link(self, y, x):
909 | self.trees.remove(y)
910 | y.left = y
911 | y.right = y
912 | y.parent = x
913 | x.degree += 1
914 | if x.child == None:
915 | x.child = y
916 | else:
917 | self._add_to_child_list(x.child, y)
918 | y.marked = False
919 |
920 | def _add_to_child_list(self, parent, child):
921 | child.left = parent.left
922 | child.right = parent
923 | parent.left.right = child
924 | parent.left = child
925 |
926 | return FibonacciHeap()
927 |
928 | '''Например, чтобы создать новую кучу Фибоначчи и вставить в нее элементы 5, 3 и 8'''
929 | heap = fibonacci_heap()
930 |
931 | heap.insert(5)
932 | heap.insert(3)
933 | heap.insert(8)
934 |
935 | '''А затем, чтобы извлечь минимальный элемент из кучи, выполните следующую команду'''
936 | min = heap.extract_min()
937 | print(min)
938 | '''Результатом этой команды будет строка "3", так как 3 является минимальным элементом в куче.'''
939 | ```
940 |
941 | #### АВЛ-дерево
942 | АВЛ-дерево — сбалансированное по высоте двоичное дерево поиска: для каждой его вершины высота её двух поддеревьев
943 | различается не более чем на 1, имеет высокую эффективность при выполнении операций поиска,
944 | добавления и удаления элементов.
945 |
946 | АВЛ — аббревиатура, образованная первыми буквами создателей (советских учёных) Адельсон-Вельского Георгия
947 | Максимовича и Ландиса Евгения Михайловича.
948 |
949 |
950 |
951 | Для создания AVL-дерева в Python мы используем классы. Каждый узел дерева определяется в виде объекта с тремя
952 | свойствами: значение (value), левым (left) и правым (right) потомком.
953 |
954 | Код создания класса для AVL-дерева может выглядеть следующим образом:
955 | ```python
956 | class Node:
957 | def __init__(self, value):
958 | self.value = value
959 | self.left = None
960 | self.right = None
961 | self.height = 1
962 |
963 |
964 | class AVL_Tree:
965 | def __init__(self):
966 | self.root = None
967 |
968 | # Метод для вставки нового элемента в дерево
969 | def insert(self, value):
970 | self.root = self._insert(self.root, value)
971 |
972 | def _insert(self, node, value):
973 | if not node:
974 | return Node(value)
975 | elif value < node.value:
976 | node.left = self._insert(node.left, value)
977 | else:
978 | node.right = self._insert(node.right, value)
979 |
980 | node.height = 1 + max(self._height(node.left), self._height(node.right))
981 |
982 | balance = self._balance(node)
983 |
984 | if balance > 1 and value < node.left.value:
985 | return self._rotate_right(node)
986 |
987 | if balance < -1 and value > node.right.value:
988 | return self._rotate_left(node)
989 |
990 | if balance > 1 and value > node.left.value:
991 | node.left = self._rotate_left(node.left)
992 | return self._rotate_right(node)
993 |
994 | if balance < -1 and value < node.right.value:
995 | node.right = self._rotate_right(node.right)
996 | return self._rotate_left(node)
997 |
998 | return node
999 |
1000 | # Метод для удаления элемента из дерева
1001 | def delete(self, value):
1002 | self.root = self._delete(self.root, value)
1003 |
1004 | def _delete(self, node, value):
1005 | if not node:
1006 | return node
1007 |
1008 | elif value < node.value:
1009 | node.left = self._delete(node.left, value)
1010 |
1011 | elif value > node.value:
1012 | node.right = self._delete(node.right, value)
1013 |
1014 | else:
1015 | if node.left is None:
1016 | temp = node.right
1017 | node = None
1018 | return temp
1019 |
1020 | elif node.right is None:
1021 | temp = node.left
1022 | node = None
1023 | return temp
1024 |
1025 | temp = self._min_value_node(node.right)
1026 | node.value = temp.value
1027 | node.right = self._delete(node.right, temp.value)
1028 |
1029 | if node is None:
1030 | return node
1031 |
1032 | node.height = 1 + max(self._height(node.left), self._height(node.right))
1033 |
1034 | balance = self._balance(node)
1035 |
1036 | if balance > 1 and self._balance(node.left) >= 0:
1037 | return self._rotate_right(node)
1038 |
1039 | if balance < -1 and self._balance(node.right) <= 0:
1040 | return self._rotate_left(node)
1041 |
1042 | if balance > 1 and self._balance(node.left) < 0:
1043 | node.left = self._rotate_left(node.left)
1044 | return self._rotate_right(node)
1045 |
1046 | if balance < -1 and self._balance(node.right) > 0:
1047 | node.right = self._rotate_right(node.right)
1048 | return self._rotate_left(node)
1049 |
1050 | return node
1051 |
1052 | # Метод для проверки сбалансированности дерева
1053 | def _balance(self, node):
1054 | if not node:
1055 | return 0
1056 |
1057 | return self._height(node.left) - self._height(node.right)
1058 |
1059 | # Метод для нахождения высоты дерева
1060 | def _height(self, node):
1061 | if not node:
1062 | return 0
1063 |
1064 | return node.height
1065 |
1066 | # Метод для выполнения левого поворота
1067 | def _rotate_left(self, node):
1068 | y = node.right
1069 | z = y.left
1070 |
1071 | y.left = node
1072 | node.right = z
1073 |
1074 | node.height = 1 + max(self._height(node.left), self._height(node.right))
1075 | y.height = 1 + max(self._height(y.left), self._height(y.right))
1076 |
1077 | return y
1078 |
1079 | # Метод для выполнения правого поворота
1080 | def _rotate_right(self, node):
1081 | y = node.left
1082 | z = y.right
1083 |
1084 | y.right = node
1085 | node.left = z
1086 |
1087 | node.height = 1 + max(self._height(node.left), self._height(node.right))
1088 | y.height = 1 + max(self._height(y.left), self._height(y.right))
1089 |
1090 | return y
1091 |
1092 | # Метод для нахождения узла с минимальным значением
1093 | def _min_value_node(self, node):
1094 | current = node
1095 |
1096 | while current.left is not None:
1097 | current = current.left
1098 |
1099 | return current
1100 |
1101 | ```
1102 |
1103 | #### Красно-чёрное дерево
1104 | Красно-чёрное дерево (англ. red-black tree, RB tree) — один из видов самобалансирующихся двоичных деревьев поиска,
1105 | гарантирующих логарифмический рост высоты дерева от числа узлов и позволяющее быстро выполнять основные операции
1106 | дерева поиска: добавление, удаление и поиск узла. Сбалансированность достигается за счёт введения дополнительного
1107 | атрибута узла дерева — «цвета». Этот атрибут может принимать одно из двух возможных значений — «чёрный» или
1108 | «красный».
1109 |
1110 | Изобретателем красно-чёрного дерева считают немца Рудольфа Байера. Название «красно-чёрное дерево» структура данных
1111 | получила в статье Л. Гимбаса и Р. Седжвика (1978). По словам Гимбаса, они использовали ручки двух цветов. По
1112 | словам Седжвика, красный цвет лучше всех смотрелся на лазерном принтере.
1113 |
1114 | Красно-чёрное дерево используется для организации сравнимых данных, таких как фрагменты текста или числа. Листовые
1115 | узлы красно-чёрных деревьев не содержат данных, благодаря чему не требуют выделения памяти — достаточно записать в
1116 | узле-предке в качестве указателя на потомка нулевой указатель. Однако в некоторых реализациях для упрощения
1117 | алгоритма могут использоваться явные листовые узлы.
1118 |
1119 |
1120 |
1121 | Красно-чёрное дерево является структурой данных, представляющей собой бинарное дерево поиска, также известное как
1122 | 2-3-4 дерево или симметричное мультипликативное дерево, используемой для хранения и
1123 | упорядочивания элементов, и который обладает следующими свойствами:
1124 |
1125 | - Каждый узел может быть красным или черным.
1126 | - Корень дерева всегда черный.
1127 | - Каждый лист дерева (NULL) также является черным.
1128 | - Если узел красный, то его дочерние узлы должны быть черными (обратное правило может быть нарушено).
1129 | - Для каждого узла все пути от него до листьев содержат одинаковое количество черных узлов.
1130 |
1131 | Код на Python для красно-черного дерева:
1132 |
1133 | ```python
1134 | class Node(object):
1135 | """Узел дерева"""
1136 | def __init__(self, key, color="red"):
1137 | self.key = key
1138 | self.left = None
1139 | self.right = None
1140 | self.parent = None
1141 | self.color = color
1142 |
1143 | class RedBlackTree(object):
1144 | """Красно-черное дерево"""
1145 | def __init__(self):
1146 | self.nil = Node(None, "black")
1147 | self.nil.left = self.nil.right = self.nil.parent = self.nil
1148 | self.root = self.nil
1149 |
1150 | def insert(self, key):
1151 | """Добавление элемента"""
1152 | new_node = Node(key)
1153 | y = self.nil
1154 | x = self.root
1155 | # Поиск места вставки нового узла
1156 | while x != self.nil:
1157 | y = x
1158 | if new_node.key < x.key:
1159 | x = x.left
1160 | else:
1161 | x = x.right
1162 | new_node.parent = y
1163 | if y == self.nil:
1164 | self.root = new_node
1165 | elif new_node.key < y.key:
1166 | y.left = new_node
1167 | else:
1168 | y.right = new_node
1169 | new_node.left = new_node.right = self.nil
1170 | new_node.color = "red"
1171 | self.insert_fixup(new_node)
1172 |
1173 | def insert_fixup(self, z):
1174 | """Восстановление свойств дерева"""
1175 | while z.parent.color == "red":
1176 | if z.parent == z.parent.parent.left:
1177 | y = z.parent.parent.right
1178 | if y.color == "red":
1179 | z.parent.color = y.color = "black"
1180 | z.parent.parent.color = "red"
1181 | z = z.parent.parent
1182 | else:
1183 | if z == z.parent.right:
1184 | z = z.parent
1185 | self.left_rotate(z)
1186 | z.parent.color = "black"
1187 | z.parent.parent.color = "red"
1188 | self.right_rotate(z.parent.parent)
1189 | else:
1190 | y = z.parent.parent.left
1191 | if y.color == "red":
1192 | z.parent.color = y.color = "black"
1193 | z.parent.parent.color = "red"
1194 | z = z.parent.parent
1195 | else:
1196 | if z == z.parent.left:
1197 | z = z.parent
1198 | self.right_rotate(z)
1199 | z.parent.color = "black"
1200 | z.parent.parent.color = "red"
1201 | self.left_rotate(z.parent.parent)
1202 | self.root.color = "black"
1203 |
1204 | def left_rotate(self, x):
1205 | """Левый поворот"""
1206 | y = x.right
1207 | x.right = y.left
1208 | if y.left != self.nil:
1209 | y.left.parent = x
1210 | y.parent = x.parent
1211 | if x.parent == self.nil:
1212 | self.root = y
1213 | elif x == x.parent.left:
1214 | x.parent.left = y
1215 | else:
1216 | x.parent.right = y
1217 | y.left = x
1218 | x.parent = y
1219 |
1220 | def right_rotate(self, x):
1221 | """Правый поворот"""
1222 | y = x.left
1223 | x.left = y.right
1224 | if y.right != self.nil:
1225 | y.right.parent = x
1226 | y.parent = x.parent
1227 | if x.parent == self.nil:
1228 | self.root = y
1229 | elif x == x.parent.right:
1230 | x.parent.right = y
1231 | else:
1232 | x.parent.left = y
1233 | y.right = x
1234 | x.parent = y
1235 |
1236 | def delete(self, key):
1237 | """Удаление элемента"""
1238 | z = self.search(key)
1239 | if z != self.nil:
1240 | y = z
1241 | y_original_color = y.color
1242 | if z.left == self.nil:
1243 | x = z.right
1244 | self.transplant(z, z.right)
1245 | elif z.right == self.nil:
1246 | x = z.left
1247 | self.transplant(z, z.left)
1248 | else:
1249 | y = self.minimum(z.right)
1250 | y_original_color = y.color
1251 | x = y.right
1252 | if y.parent == z:
1253 | x.parent = y
1254 | else:
1255 | self.transplant(y, y.right)
1256 | y.right = z.right
1257 | y.right.parent = y
1258 | self.transplant(z, y)
1259 | y.left = z.left
1260 | y.left.parent = y
1261 | y.color = z.color
1262 | if y_original_color == "black":
1263 | self.delete_fixup(x)
1264 |
1265 | def delete_fixup(self, x):
1266 | """Восстановление свойств дерева"""
1267 | while x != self.root and x.color == "black":
1268 | if x == x.parent.left:
1269 | w = x.parent.right
1270 | if w.color == "red":
1271 | w.color = "black"
1272 | x.parent.color = "red"
1273 | self.left_rotate(x.parent)
1274 | w = x.parent.right
1275 | if w.left.color == "black" and w.right.color == "black":
1276 | w.color = "red"
1277 | x = x.parent
1278 | else:
1279 | if w.right.color == "black":
1280 | w.left.color = "black"
1281 | w.color = "red"
1282 | self.right_rotate(w)
1283 | w = x.parent.right
1284 | w.color = x.parent.color
1285 | x.parent.color = "black"
1286 | w.right.color = "black"
1287 | self.left_rotate(x.parent)
1288 | x = self.root
1289 | else:
1290 | w = x.parent.left
1291 | if w.color == "red":
1292 | w.color = "black"
1293 | x.parent.color = "red"
1294 | self.right_rotate(x.parent)
1295 | w = x.parent.left
1296 | if w.right.color == "black" and w.left.color == "black":
1297 | w.color = "red"
1298 | x = x.parent
1299 | else:
1300 | if w.left.color == "black":
1301 | w.right.color = "black"
1302 | w.color = "red"
1303 | self.left_rotate(w)
1304 | w = x.parent.left
1305 | w.color = x.parent.color
1306 | x.parent.color = "black"
1307 | w.left.color = "black"
1308 | self.right_rotate(x.parent)
1309 | x = self.root
1310 | x.color = "black"
1311 |
1312 | def transplant(self, u, v):
1313 | """Замена узла"""
1314 | if u.parent == self.nil:
1315 | self.root = v
1316 | elif u == u.parent.left:
1317 | u.parent.left = v
1318 | else:
1319 | u.parent.right = v
1320 | v.parent = u.parent
1321 |
1322 | def search(self, key):
1323 | """Поиск элемента"""
1324 | x = self.root
1325 | while x != self.nil and x.key != key:
1326 | if key < x.key:
1327 | x = x.left
1328 | else:
1329 | x = x.right
1330 | return x
1331 |
1332 | def minimum(self, x=None):
1333 | """Поиск минимального элемента"""
1334 | if x is None:
1335 | x = self.root
1336 | while x.left != self.nil:
1337 | x = x.left
1338 | return x
1339 |
1340 | def maximum(self, x=None):
1341 | """Поиск максимального элемента"""
1342 | if x is None:
1343 | x = self.root
1344 | while x.right != self.nil:
1345 | x = x.right
1346 | return x
1347 |
1348 | def inorder_walk(self, node=None):
1349 | """Обход узлов в порядке возрастания ключей"""
1350 | if node is None:
1351 | node = self.root
1352 | if node != self.nil:
1353 | for x in self.inorder_walk(node.left):
1354 | yield x
1355 | yield node.key
1356 | for x in self.inorder_walk(node.right):
1357 | yield x
1358 |
1359 | def __iter__(self):
1360 | return self.inorder_walk()
1361 | ```
1362 |
1363 | #### Косое дерево
1364 | Наихудшая временная сложность таких операций, как поиск, удаление и вставка, для двоичного дерева поиска (Binary
1365 | Search Tree) составляет O(n). Наихудший случай случай возникает, когда дерево несбалансировано. Мы можем улучшить
1366 | наихудший результат временной сложности до O(log n) с помощью красно-черных и АВЛ-деревьев.
1367 |
1368 | Можем ли мы добиться на практике лучшего результата, чем тот, что нам дают красно-черные или АВЛ-деревья?
1369 |
1370 | Подобно красно-черным и АВЛ-деревьям, Splay-дерево (или косое дерево) также является самобалансирующимся бинарным
1371 | деревом поиска. Основная идея splay-дерева состоит в том, чтобы помещать элемент, к которому недавно осуществлялся
1372 | доступ, в корень дерева, что делает этот элемент, доступным за время порядка O(1) при повторном доступе. Вся суть
1373 | заключается в том, чтобы использовать концепцию локальности ссылок (в среднестатистическом приложении 80% обращений
1374 | приходятся на 20% элементов). Представьте себе ситуацию, когда у нас есть миллионы или даже миллиарды ключей, и лишь
1375 | к некоторым из них обращаются регулярно, что весьма вероятно для многих типичных приложениях.
1376 |
1377 | Все операции со splay-деревом выполняются в среднем за время порядка O(log n), где n - количество элементов в дереве.
1378 | Любая отдельная операция в худшем случае может занять время порядка Тэта(n).
1379 |
1380 |
1381 | Операция поиска в splay-дереве представляет собой стандартный алгоритм поиска в бинарном дереве, после которого
1382 | дерево выворачивается (искомый узел перемещается в корень — операция splay). Если поиск завершился успехом, то
1383 | найденный узел поднимается наверх и становится новым корнем. В противном случае корнем становится последний узел, к
1384 | которому был осуществлен доступ до достижения NULL.
1385 |
1386 | Расширяющееся (англ. splay tree) или косое дерево является двоичным деревом поиска, в котором поддерживается
1387 | свойство сбалансированности. Это дерево принадлежит классу «саморегулирующихся деревьев», которые поддерживают
1388 | необходимый баланс ветвления дерева, чтобы обеспечить выполнение операций поиска, добавления и удаления за
1389 | логарифмическое время от числа хранимых элементов. Это реализуется без использования каких-либо дополнительных полей
1390 | в узлах дерева (как, например, в Красно-чёрных деревьях или АВЛ-деревьях, где в вершинах хранится, соответственно,
1391 | цвет вершины и глубина поддерева). Вместо этого «расширяющие операции» (splay operation), частью которых являются
1392 | вращения, выполняются при каждом обращении к дереву.
1393 |
1394 |
1395 | Расширяющееся дерево придумали Роберт Тарьян и Даниель Слейтор в 1983 году.
1396 |
1397 | ```python
1398 | class Node:
1399 | def __init__(self, value):
1400 | self.value = value
1401 | self.left_child = None
1402 | self.right_child = None
1403 |
1404 | class SkewedBinaryTree:
1405 | def __init__(self, values):
1406 | self.root = None
1407 | self.build_tree(values)
1408 |
1409 | def build_tree(self, values):
1410 | for value in values:
1411 | node = Node(value)
1412 | if self.root is None:
1413 | self.root = node
1414 | else:
1415 | self.insert_node(node, self.root)
1416 |
1417 | def insert_node(self, node, current_node):
1418 | if node.value > current_node.value:
1419 | if current_node.right_child is None:
1420 | current_node.right_child = node
1421 | else:
1422 | self.insert_node(node, current_node.right_child)
1423 | else:
1424 | if current_node.left_child is None:
1425 | current_node.left_child = node
1426 | else:
1427 | self.insert_node(node, current_node.left_child)
1428 | ```
1429 |
1430 | #### Список с пропусками
1431 | Список с пропусками (англ. Skip List) — вероятностная структура данных, основанная на нескольких параллельных
1432 | отсортированных связных списках с эффективностью, сравнимой с двоичным деревом (порядка O(log n) среднее время для
1433 | большинства операций).
1434 |
1435 | В основе списка с пропусками лежит расширение отсортированного связного списка дополнительными связями, добавленными
1436 | в случайных путях с геометрическим/негативным биномиальным распределением, таким образом, чтобы поиск по списку
1437 | мог быстро пропускать части этого списка. Вставка, поиск и удаление выполняются за логарифмическое случайное время.
1438 |
1439 | Для создания списка с пропусками в Python можно использовать формирование списка с помощью лист-понимания (list
1440 | comprehension) и функции range(). Например, если требуется создать список с 10 элементами, где каждый второй элемент
1441 | является пропуском, можно написать следующий код:
1442 |
1443 | ```python
1444 | my_list = [i if i % 2 == 0 else None for i in range(10)]
1445 | # Используется функция range() для создания последовательности чисел от 0 до 9.
1446 | # Если число четное, то оно добавляется в список. Если нечетное, то добавляется пропуск (None).
1447 | # Результатом выполнения кода является созданный список с пропусками.
1448 |
1449 | # Таким образом, результатом выполнения этого кода будет список с элементами:
1450 | # [0, None, 2, None, 4, None, 6, None, 8, None].
1451 |
1452 | ```
1453 |
1454 | В этом коде используется лист-понимания для создания списка. Функция range(10) возвращает последовательность
1455 | чисел от 0 до 9. Оператор if i % 2 == 0 используется для проверки того, является ли текущий элемент четным числом
1456 | или нет. Если элемент четный, то он добавляется в список, иначе добавляется пропуск (None).
1457 |
1458 | #### Trie, или нагруженное дерево
1459 | Trie, или нагруженное дерево — структура данных реализующая интерфейс ассоциативного массива, то есть позволяющая
1460 | хранить пары «ключ-значение». Сразу следует оговорится, что в большинстве случаев ключами выступают строки, однако в
1461 | качестве ключей можно использовать любые типы данных, представимые как последовательность байт (то есть вообще любые).
1462 |
1463 |
1464 | Нагруженное дерево отличается от обычных n-арных деревьев тем, что в его узлах не хранятся ключи. Вместо них в узлах
1465 | хранятся односимвольные метки, а ключем, который соответствует некоему узлу является путь от корня дерева до этого
1466 | узла, а точнее строка составленная из меток узлов, повстречавшихся на этом пути. В таком случае корень дерева,
1467 | очевидно, соответствует пустому ключу.
1468 |
1469 | TRIE (также известный как префиксное дерево или бор) - это древовидная структура данных для хранения и поиска
1470 | множества строк. Он позволяет выполнять операции поиска, вставки и удаления строк в среднем за время,
1471 | пропорциональное длине строки. Каждый узел дерева содержит один символ строки вместе с указателем на следующий узел
1472 | в этой строке.
1473 |
1474 | Вот пример реализации TRIE в Python:
1475 | ```python
1476 | # Класс Node представляет узел дерева, каждый из которых может иметь несколько дочерних узлов. Атрибут word_end
1477 | # определяет, является ли данный узел концом строки.
1478 |
1479 |
1480 | class Node:
1481 | def __init__(self):
1482 | self.children = {} # словарь дочерних узлов
1483 | self.word_end = False # флаг конца слова
1484 |
1485 | # Класс Trie использует класс Node для создания TRIE. Метод insert вставляет новое слово в TRIE дерево, а методы
1486 | # search и startsWith производят поиск соответствующих слов или префиксов в дереве.
1487 | class Trie:
1488 | def __init__(self):
1489 | self.root = Node() # создаем корень дерева
1490 |
1491 | def insert(self, word: str) -> None:
1492 | node = self.root
1493 | for char in word:
1494 | # если узел для текущего символа не существует, то создаем его
1495 | if char not in node.children:
1496 | node.children[char] = Node()
1497 | node = node.children[char]
1498 | node.word_end = True # помечаем конец слова
1499 |
1500 | def search(self, word: str) -> bool:
1501 | node = self.root
1502 | for char in word:
1503 | if char not in node.children:
1504 | return False
1505 | node = node.children[char]
1506 | return node.word_end # возвращаем True, если слово найдено
1507 |
1508 | def startsWith(self, prefix: str) -> bool:
1509 | node = self.root
1510 | for char in prefix:
1511 | if char not in node.children:
1512 | return False
1513 | node = node.children[char]
1514 | return True # возвращаем True, если существует хотя бы одно слово с таким префиксом
1515 |
1516 |
1517 | # Пример использования:
1518 |
1519 | trie = Trie()
1520 | words = ["apple", "banana", "cat", "dog"]
1521 |
1522 | # вставляем слова в дерево
1523 | for word in words:
1524 | trie.insert(word)
1525 |
1526 | # проверяем, существуют ли слова в дереве
1527 | print(trie.search("banana")) # True
1528 | print(trie.search("elephant")) # False
1529 |
1530 | # проверяем, существуют ли слова с заданным префиксом в дереве
1531 | print(trie.startsWith("ap")) # True
1532 | print(trie.startsWith("d")) # True
1533 | print(trie.startsWith("f")) # False
1534 |
1535 | ```
1536 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2023 BEPb
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
in progress...
42 | 43 | - [ ] **Data structures** 44 | - [x] Level 0 - Doesn’t know the difference between Array and LinkedList 45 | - [x] Level 1 - Able to explain and use Arrays, LinkedLists, Dictionaries etc in practical programming tasks 46 | - [x] Level 2 - Knows space and time tradeoffs of the basic data structures, Arrays vs LinkedLists, Able to explain 47 | how hashtables can be implemented and can handle collisions, Priority queues and ways to implement them etc. 48 | - [x] Level 3 - Knowledge of advanced data structures like B-trees, binomial and fibonacci heaps, AVL/Red Black trees, 49 | Splay Trees, Skip Lists, tries etc. 50 | 51 | - [ ] **Algorithms** 52 | - [x] Level 0 - Unable to find the average of numbers in an array (It’s hard to believe but I’ve interviewed such candidates) 53 | - [x] Level 1 - Basic sorting, searching and data structure traversal and retrieval algorithms 54 | - [x] Level 2 - Tree, Graph, simple greedy and divide and conquer algorithms, is able to understand the 55 | relevance of the levels of this matrix. 56 | - [ ] Level 3 - Able to recognize and code dynamic programming solutions, good knowledge of graph algorithms, 57 | good knowledge of numerical computation algorithms, able to identify NP problems etc. 58 | 59 | - [ ] **Systems programming** 60 | - [x] Level 0 - Doesn’t know what a compiler, linker or interpreter is 61 | - [x] Level 1 - Basic understanding of compilers, linker and interpreters. Understands what assembly code is and 62 | how things work at the hardware level. Some knowledge of virtual memory and paging. 63 | - [ ] Level 2 - Understands kernel mode vs. user mode, multi-threading, synchronization primitives and how they’re 64 | implemented, able to read assembly code. Understands how networks work, understanding of network protocols and 65 | socket level programming. 66 | - [ ] Level 3 - Understands the entire programming stack, hardware (CPU + Memory + Cache + 67 | Interrupts + microcode), binary code, assembly, static and dynamic linking, compilation, interpretation, JIT 68 | compilation, garbage collection, heap, stack, memory addressing… 69 | 70 |333 | 334 |
Computer Science |
339 | 340 | | 341 | | 342 | | 343 | | |
---|---|---|---|---|---|
349 | | (Level 0) | 350 |(Level 1) | 351 |(Level 2) | 352 |(Level 3) | 353 |Comments | 354 |
data structures | 358 |Doesn’t know the difference between Array and LinkedList | 359 |Able to explain and use Arrays, LinkedLists, Dictionaries etc in practical programming tasks | 360 |Knows space and time tradeoffs of the basic data structures, Arrays vs LinkedLists, Able to explain 361 | how hashtables can be implemented and can handle collisions, Priority queues and ways to implement them etc. 362 | | 363 |Knowledge of advanced data structures like B-trees, binomial and fibonacci heaps, AVL/Red Black trees, 364 | Splay Trees, Skip Lists, tries etc. | 365 |366 | |
algorithms | 370 |Unable to find the average of numbers in an array (It’s hard to believe but I’ve interviewed such candidates) | 371 |Basic sorting, searching and data structure traversal and retrieval algorithms | 372 |Tree, Graph, simple greedy and divide and conquer 373 | algorithms, is able to understand the relevance of the levels of this matrix. | 374 |Able to recognize and code 375 | dynamic programming solutions, good knowledge of graph algorithms, good knowledge of numerical computation 376 | algorithms, able to identify NP problems etc. | 377 |378 | |
systems programming | 382 |Doesn’t know 383 | what a compiler, linker or interpreter is 384 | | 385 |Basic understanding of compilers, linker and interpreters. 386 | Understands what assembly code is and how things work at the hardware level. Some knowledge of virtual memory 387 | and 388 | paging. 389 | | 390 |Understands kernel mode vs. user mode, multi-threading, synchronization primitives and how they’re 391 | implemented, able to read assembly code. Understands how networks work, understanding of network protocols and 392 | socket level programming. 393 | | 394 |Understands the entire programming stack, hardware (CPU + Memory + Cache + 395 | Interrupts + microcode), binary code, assembly, static and dynamic linking, compilation, interpretation, JIT 396 | compilation, garbage collection, heap, stack, memory addressing… 397 | | 398 ||
Software 401 | Engineering |
402 | 403 | | 404 | | 405 | | 406 | | 407 | |
410 | | 2n (Level 0) | 411 |n2 412 | (Level 1) 413 | | 414 |n (Level 2) | 415 |log(n) (Level 3) | 416 |Comments | 417 |
source code version 420 | control 421 | | 422 |Folder backups by date | 423 |VSS and beginning CVS/SVN user | 424 |Proficient in using CVS and 425 | SVN features. Knows how to branch and merge, use patches setup repository properties etc. 426 | | 427 |Knowledge of 428 | distributed VCS systems. Has tried out Bzr/Mercurial/Darcs/Git 429 | | 430 |431 | |
build 434 | automation 435 | | 436 |Only knows how to build from IDE | 437 |Knows how to build the system from the command 438 | line 439 | | 440 |Can setup a script to build the basic system | 441 |Can setup a script to build the system and also 442 | documentation, installers, generate release notes and tag the code in source 443 | control 444 | | 445 |446 | |
automated testing | 449 |Thinks that all testing is the job of the 450 | tester 451 | | 452 |Has written automated unit tests and comes up with good unit test cases for the code that is being 453 | written 454 | | 455 |Has written code in TDD manner | 456 |Understands and is able to setup automated functional, 457 | load/performance and UI tests 458 | | 459 |460 | |
Programming |
466 | 467 | | 468 | | 469 | | 471 | | 472 | |
475 | | 2n (Level 0) | 476 |n2 (Level 1) | 477 |n (Level 2) | 478 |log(n) (Level 3) 479 | | 480 |Comments | 481 |
problem decomposition | 484 |Only straight line code with copy paste for 485 | reuse 486 | | 487 |Able to break up problem into multiple functions | 488 |Able to come up with reusable 489 | functions/objects that solve the overall problem 490 | | 491 |Use of appropriate data structures and algorithms and comes 492 | up with generic/object-oriented code that encapsulate aspects of the problem that are subject to change. 493 | | 494 |495 | |
systems decomposition | 498 |Not able to think above the level of a single 499 | file/class 500 | | 501 |Able to break up problem space and design solution as long as it is within the same 502 | platform/technology 503 | | 504 |Able to design systems that span multiple technologies/platforms. | 505 |Able to 506 | visualize and design complex systems with multiple product lines and integrations with external systems. Also 507 | should 508 | be able to design operations support systems like monitoring, reporting, fail overs etc. 509 | | 510 |511 | |
communication | 514 |Cannot express thoughts/ideas to peers. Poor spelling and grammar. 515 | | 516 |Peers can understand what is being said. Good spelling and grammar. | 517 |Is able to effectively 518 | communicate with peers 519 | | 520 |Able to understand and communicate thoughts/design/ideas/specs in a unambiguous 521 | manner and adjusts communication as per the context 522 | | 523 |This is an often under rated but very critical criteria 524 | for judging a programmer. With the increase in outsourcing of programming tasks to places where English is not 525 | the 526 | native tongue this issue has become more prominent. I know of several projects that failed because the 527 | programmers 528 | could not understand what the intent of the communication was. 529 | | 530 |
code organization within a 533 | file 534 | | 535 |no evidence of organization within a file | 536 |Methods are grouped logically or by 537 | accessibility 538 | | 539 |Code is grouped into regions and well commented with references to other source 540 | files 541 | | 542 |File has license header, summary, well commented, consistent white space usage. The file should look 543 | beautiful. 544 | | 545 |546 | |
code organization across files | 549 |No thought given to organizing code 550 | across files 551 | | 552 |Related files are grouped into a folder | 553 |Each physical file has a unique purpose, for e. 554 | g. one class definition, one feature implementation etc. 555 | | 556 |Code organization at a physical level closely 557 | matches design and looking at file names and folder distribution provides insights into 558 | design 559 | | 560 |561 | |
source tree organization | 564 |Everything in one folder | 565 |Basic separation 566 | of code into logical folders. 567 | | 568 |No circular dependencies, binaries, libs, docs, builds, third-party code all 569 | organized into appropriate folders 570 | | 571 |Physical layout of source tree matches logical hierarchy and organization. 572 | The directory names and organization provide insights into the design of the system. 573 | | 574 |The difference between 575 | this and the previous item is in the scale of organization, source tree organization relates to the entire set 576 | of 577 | artifacts that define the system. 578 | | 579 |
code readability | 582 |Mono-syllable names | 583 |Good names 584 | for files, variables classes, methods etc. 585 | | 586 |No long functions, comments explaining unusual code, bug fixes, 587 | code assumptions 588 | | 589 |Code assumptions are verified using asserts, code flows naturally – no deep nesting of 590 | conditionals or methods 591 | | 592 |593 | |
defensive coding | 596 |Doesn’t understand the 597 | concept 598 | | 599 |Checks all arguments and asserts critical assumptions in code | 600 |Makes sure to check return 601 | values and check for exceptions around code that can fail. 602 | | 603 |Has his own library to help with defensive coding, 604 | writes unit tests that simulate faults 605 | | 606 |607 | |
error handling | 610 |Only codes the happy 611 | case 612 | | 613 |Basic error handling around code that can throw exceptions/generate errors | 614 |Ensures that 615 | error/exceptions leave program in good state, resources, connections and memory is all cleaned up 616 | properly 617 | | 618 |Codes to detect possible exception before, maintain consistent exception handling strategy in all 619 | layers of code, come up with guidelines on exception handling for entire system. 620 | | 621 |622 | |
IDE | 625 |Mostly uses IDE for text editing | 626 |Knows their way around the interface, 627 | able to effectively use the IDE using menus. 628 | | 629 |Knows keyboard shortcuts for most used operations. | 630 |Has 631 | written custom macros 632 | | 633 |634 | |
API | 637 |Needs to look up the documentation 638 | frequently 639 | | 640 |Has the most frequently used APIs in memory | 641 |Vast and In-depth knowledge of the 642 | API 643 | | 644 |Has written libraries that sit on top of the API to simplify frequently used tasks and to fill in gaps 645 | in the API 646 | | 647 |E.g. of API can be Java library, .net framework or the custom API for the 648 | application 649 | | 650 |
frameworks | 653 |Has not used any framework outside of the core platform | 654 |Has 655 | heard about but not used the popular frameworks available for the platform. 656 | | 657 |Has used more than one framework 658 | in a professional capacity and is well-versed with the idioms of the frameworks. 659 | | 660 |Author of 661 | framework 662 | | 663 |664 | |
requirements | 667 |Takes the given requirements and codes to spec | 668 |Come 669 | up with questions regarding missed cases in the spec 670 | | 671 |Understand complete picture and come up with entire 672 | areas that need to be speced 673 | | 674 |Able to suggest better alternatives and flows to given requirements based on 675 | experience 676 | | 677 |678 | |
scripting | 681 |No knowledge of scripting tools | 682 |Batch files/shell 683 | scripts 684 | | 685 |Perl/Python/Ruby/VBScript/Powershell | 686 |Has written and published reusable 687 | code 688 | | 689 |690 | |
database | 693 |Thinks that Excel is a database | 694 |Knows basic database 695 | concepts, normalization, ACID, transactions and can write simple selects 696 | | 697 |Able to design good and normalized 698 | database schemas keeping in mind the queries that’ll have to be run, proficient in use of views, stored 699 | procedures, 700 | triggers and user defined types. Knows difference between clustered and non-clustered indexes. Proficient in use 701 | of 702 | ORM tools. 703 | | 704 |Can do basic database administration, performance optimization, index optimization, write 705 | advanced select queries, able to replace cursor usage with relational sql, understands how data is stored 706 | internally, 707 | understands how indexes are stored internally, understands how databases can be mirrored, replicated etc. 708 | Understands how the two phase commit works. 709 | | 710 |711 | |
Experience |
716 | 717 | | 718 | | 720 | | 721 | | 722 | |
725 | | 2n (Level 0) | 726 |n2 (Level 1) | 727 |n (Level 2) | 728 |log(n) 729 | (Level 3) 730 | | 731 |Comments | 732 |
languages with professional experience | 735 |Imperative or Object 736 | Oriented 737 | | 738 |Imperative, Object-Oriented and declarative (SQL), added bonus if they understand static vs dynamic 739 | typing, weak vs strong typing and static inferred types 740 | | 741 |Functional, added bonus if they understand lazy 742 | evaluation, currying, continuations 743 | | 744 |Concurrent (Erlang, Oz) and Logic (Prolog) 745 | | 746 |747 | |
platforms with professional 750 | experience 751 | | 752 |1 | 753 |2-3 | 754 |4-5 | 755 |6+ | 756 |757 | |
years of professional 760 | experience 761 | | 762 |1 | 763 |2-5 | 764 |6-9 | 765 |10+ | 766 |767 | |
domain knowledge | 770 |No 771 | knowledge of the domain 772 | | 773 |Has worked on at least one product in the domain. | 774 |Has worked on multiple 775 | products in the same domain. 776 | | 777 |Domain expert. Has designed and implemented several products/solutions in the 778 | domain. Well versed with standard terms, protocols used in the domain. 779 | | 780 |781 | |
Knowledge |
786 | 787 | | 788 | | 789 | | 790 | | 791 | |
tool 794 | knowledge 795 | | 796 |Limited to primary IDE (VS.Net, Eclipse etc.) | 797 |Knows about some alternatives to popular and 798 | standard tools. 799 | | 800 |Good knowledge of editors, debuggers, IDEs, open source alternatives etc. etc. For e.g. 801 | someone who knows most of the tools from Scott Hanselman’s power tools list. Has used ORM tools. 802 | | 803 |Has 804 | actually written tools and scripts, added bonus if they’ve been published. 805 | | 806 |807 | |
languages 810 | exposed to 811 | | 812 |Imperative or Object Oriented | 813 |Imperative, Object-Oriented and declarative (SQL), added 814 | bonus if they understand static vs dynamic typing, weak vs strong typing and static inferred 815 | types 816 | | 817 |Functional, added bonus if they understand lazy evaluation, currying, continuations | 818 |Concurrent 819 | (Erlang, Oz) and Logic (Prolog) 820 | | 821 |822 | |
codebase knowledge | 825 |Has never looked at the 826 | codebase 827 | | 828 |Basic knowledge of the code layout and how to build the system | 829 |Good working knowledge of 830 | code base, has implemented several bug fixes and maybe some small features. 831 | | 832 |Has implemented multiple big 833 | features in the codebase and can easily visualize the changes required for most features or bug fixes. 834 | | 835 |836 | |
knowledge of upcoming technologies | 839 |Has not heard of the upcoming 840 | technologies 841 | | 842 |Has heard of upcoming technologies in the field | 843 |Has downloaded the alpha 844 | preview/CTP/beta and read some articles/manuals 845 | | 846 |Has played with the previews and has actually built 847 | something with it and as a bonus shared that with everyone else 848 | | 849 |850 | |
platform 853 | internals 854 | | 855 |Zero knowledge of platform internals | 856 |Has basic knowledge of how the platform works 857 | internally 858 | | 859 |Deep knowledge of platform internals and can visualize how the platform takes the program and 860 | converts it into executable code. 861 | | 862 |Has written tools to enhance or provide information on platform internals. 863 | For e.g. disassemblers, decompilers, debuggers etc. 864 | | 865 |866 | |
books | 869 |Unleashed series, 21 870 | days series, 24 hour series, dummies series… 871 | | 872 |Code Complete, Don’t Make me Think, Mastering Regular 873 | Expressions 874 | | 875 |Design Patterns, Peopleware, Programming Pearls, Algorithm Design Manual, Pragmatic Programmer, 876 | Mythical Man month 877 | | 878 |Structure and Interpretation of Computer Programs, Concepts Techniques, Models of 879 | Computer Programming, Art of Computer Programming, Database systems , by C. J Date, Thinking Forth, Little 880 | Schemer 881 | | 882 |883 | |
blogs | 886 |Has heard of them but never got the time. | 887 |Reads 888 | tech/programming/software engineering blogs and listens to podcasts regularly. 889 | | 890 |Maintains a link blog with 891 | some collection of useful articles and tools that he/she has collected 892 | | 893 |Maintains a blog in which personal 894 | insights and thoughts on programming are shared 895 | | 896 |897 | |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |