Skip to content

Commit 43911eb

Browse files
author
Anton Gorinenko
committed
Разделяй и властвуй
1 parent 843e62c commit 43911eb

File tree

8 files changed

+136
-3
lines changed

8 files changed

+136
-3
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747

4848
[Рекурсия](tutorial/recursion.md)
4949

50+
[Разделяй и властвуй](tutorial/divide_and_conquer.md)
51+
5052
[Динамическое программирование](tutorial/dynamic_programming.md) (в работе)
5153

5254
[Жадные алгоритмы](tutorial/greedy.md) (в работе)

img/merge_sort.png

30.7 KB
Loading

img/merge_sort_1.png

7.94 KB
Loading

img/merge_sort_2.png

9.55 KB
Loading

tutorial/divide_and_conquer.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Разделяй и властвуй
2+
3+
**"Разделяй и властвуй"** — схема разработки алгоритмов, заключающаяся в рекурсивном разбиении решаемой задачи на две
4+
или более подзадачи того же типа, но меньшего размера, и комбинировании их решений для получения ответа к исходной
5+
задаче; разбиения выполняются до тех пор, пока все подзадачи не окажутся элементарными. Рекурсия естественным образом
6+
подходит для решения задач данного класса. Основная идея схемы состоит в том, чтобы разбить задачу на две или более
7+
сходных, но более простых подзадач, решить их поочерёдно и объединить их решения. Это отличает "разделяй и властвуй" от
8+
других рекурсивных алгоритмов, которые последовательно решают более простые задачи, сводящиеся к базовому случаю.
9+
Примером последних может служить бинарный поиск, класс таких задач называют **"уменьшай и властвуй"**.
10+
11+
Алгоритм "разделяй и властвуй" состоит из трех шагов:
12+
13+
1. Разбиение изначальной проблемы на несколько более простых задач
14+
1. Решение каждой под задачи рекурсивным способом
15+
1. Объединение решений подзадач в одно общее решение
16+
17+
Ниже приведено наглядное представление алгоритма сортировки слиянием, которая реализует принципы "разделяй и властвуй".
18+
19+
![Сортировка слиянием](../img/merge_sort.png)
20+

tutorial/merge_sort.md

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,108 @@
1-
# Сортировка слиянием
1+
# Сортировка слиянием
2+
3+
Одним классическим примером алгоритма "разделяй и властвуй" является сортировка слиянием.
4+
5+
Существует два подхода реализации сортировки слиянием "сверху вниз" и "снизу вверх". Первый вариант можно просто
6+
реализовать с использованием рекурсии. Задача сортировки слиянием ставится и решается следующим образом:
7+
8+
1) Дан неотсортированный список, например список чисел. Требуется отсортировать список в порядке возрастания или
9+
убывания.
10+
2) На первом шаге разделим данный список на два
11+
3) На следующем шаге рекурсивно отсортируем два списка, полученные на первом шаге. В процессе рекурсии сортируемые
12+
списки будут становиться все меньше, пока не станут пустыми или не будут состоять из одного элемента. Это будет базой
13+
рекурсии.
14+
4) Наконец мы объединяем отсортированные списки, полученные на предыдущем шаге.
15+
16+
Алгоритм сортировки слиянием представлен ниже.
17+
18+
![Сортировка слиянием](../img/merge_sort.png)
19+
20+
Итак, после разбиения двух списков необходимо провести слияние. Сложность этого шага будет O(n), где n - общее
21+
количество элементов в двух списках. Слияние реализуется с использованием двух указателей и дополнительного списка
22+
23+
![Слияние](../img/merge_sort_1.png)
24+
25+
Реализация сортировки слиянием на python:
26+
27+
```python
28+
def merge_sort(nums):
29+
"""
30+
Сортировка
31+
"""
32+
# Базовый случай
33+
if len(nums) <= 1:
34+
return nums
35+
# Разделяем
36+
pivot = int(len(nums) / 2)
37+
# Рекурсивно решаем более простую задачу
38+
left_list = merge_sort(nums[0:pivot])
39+
right_list = merge_sort(nums[pivot:])
40+
# Объединяем
41+
return merge(left_list, right_list)
42+
43+
44+
def merge(left_list, right_list):
45+
"""
46+
Слияние
47+
"""
48+
left_cursor = right_cursor = 0
49+
ret = []
50+
while left_cursor < len(left_list) and right_cursor < len(right_list):
51+
if left_list[left_cursor] < right_list[right_cursor]:
52+
ret.append(left_list[left_cursor])
53+
left_cursor += 1
54+
else:
55+
ret.append(right_list[right_cursor])
56+
right_cursor += 1
57+
58+
ret.extend(left_list[left_cursor:])
59+
ret.extend(right_list[right_cursor:])
60+
61+
return ret
62+
```
63+
64+
В подходе "снизу вверх", мы делим список на подсписки по 1 элементу и затем рекурсивно объединяем их уже известным нам
65+
способом.
66+
67+
![Сортировка слиянием снизу вверх](../img/merge_sort_2.png)
68+
69+
Реализация сортировки слиянием методом снизу вверх выглядит следующим образом:
70+
71+
```python
72+
from collections import deque
73+
74+
75+
def merge_sort(nums):
76+
"""
77+
Sort
78+
"""
79+
# Divide and conquer
80+
res = deque([[i] for i in nums])
81+
# Merge
82+
while len(res) > 1:
83+
one = res.popleft()
84+
two = res.popleft()
85+
res.append(merge(one, two))
86+
87+
return res[0]
88+
89+
90+
def merge(left_list, right_list):
91+
"""
92+
Merge
93+
"""
94+
left_cursor = right_cursor = 0
95+
ret = []
96+
while left_cursor < len(left_list) and right_cursor < len(right_list):
97+
if left_list[left_cursor] < right_list[right_cursor]:
98+
ret.append(left_list[left_cursor])
99+
left_cursor += 1
100+
else:
101+
ret.append(right_list[right_cursor])
102+
right_cursor += 1
103+
104+
ret.extend(left_list[left_cursor:])
105+
ret.extend(right_list[right_cursor:])
106+
107+
return ret
108+
```

tutorial/tree.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
**сбалансированными**. Такие рандомизированные деревья поиска обеспечивают сбалансированность только в вероятностном
1414
смысле. Они обладают следующими свойствами:
1515

16-
1) имеют небольшую высоту
17-
1) имеют хорошую заполненность уровней
16+
1. имеют небольшую высоту
17+
1. имеют хорошую заполненность уровней
1818

1919
На рисунке ниже слева приведен пример сбалансированного дерева, которое получается для
2020
ключей ``[ 9, 2, 13, 7, 1, 11, 14]``. Справа изображено **несбалансированное дерево** для упорядоченных

tutorial/two_pointers.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ def remove_element(arr: List[int], val: int):
6464
исключение.
6565
2. Следует правильно выбрать условие окончания прохода по списку.
6666

67+
Рассмотрим задачу, где учитываются эти особенности.
68+
69+
Оригинал: [141. Linked List Cycle](https://leetcode.com/problems/linked-list-cycle/description/)
70+
6771
```python
6872
def test_has_cycle():
6973
list_1 = create_list_from_array([3, 2, 0, -4], 1)

0 commit comments

Comments
 (0)