Skip to content

Commit 4eaebb6

Browse files
agorinenkoAnton Gorinenko
andauthored
Метод двух указателей (#6)
Co-authored-by: Anton Gorinenko <[email protected]>
1 parent 40789bd commit 4eaebb6

File tree

7 files changed

+104
-9
lines changed

7 files changed

+104
-9
lines changed

.idea/data_structures_and_algorithms.iml

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

img/two_pointers_1.png

15.1 KB
Loading

img/two_pointers_2.png

37.6 KB
Loading

tutorial/doubly_linked_list.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
Каждый узел в двусвязном списке содержит не только значение, но и ссылку на следующий и предыдущий элемент. Программно
1313
его можно представить так:
1414

15-
```
15+
```python
1616
@dataclass
1717
class DoublyListNode:
1818
"""
@@ -96,7 +96,7 @@ class DoublyListNode:
9696

9797
## Реализация двусвязного списка на языке python
9898

99-
```
99+
```python
100100
"""
101101
DoublyLinkedList
102102
"""

tutorial/singly_linked_list.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
Каждый узел в односвязном списке содержит не только значение, но и ссылку на следующий элемент. Программно его можно
1414
представить так:
1515

16-
```
16+
```python
1717
class SinglyListNode:
1818
"""
1919
Узел односвязного списка
@@ -84,8 +84,8 @@ class SinglyListNode:
8484

8585
Вставка элемента в "голову" - **O(1)**
8686

87-
Вставка элемента в "хвост" - **O(1)**. При условии, что список хранит указатель ``TAIL`` на последний элемент, иначе *
88-
*O(1)**.
87+
Вставка элемента в "хвост" - **O(1)**. При условии, что список хранит указатель ``TAIL`` на последний элемент, иначе
88+
**O(n)**.
8989

9090
## Удаление элемента из односвязного списка
9191

tutorial/two_pointers.md

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,97 @@
11
# Метод двух указателей (Two-pointer Technique)
2-
https://leetcode.com/explore/learn/card/array-and-string/205/array-two-pointer-technique/1156/
3-
https://leetcode.com/explore/learn/card/linked-list/214/two-pointer-technique/1211/
2+
3+
Обычно в задачах итерации или поиску по списку мы имеем один указатель, который итерируется с первого до последнего
4+
элемента коллекции. Однако иногда полезно иметь два указателя, которые можно использовать так:
5+
6+
![Метод двух указателей. Вариант 1.](../img/two_pointers_1.png)
7+
8+
1. В начальный момент времени указатели установлены на разные элемента списка. Обычно первый указывает на его начало, а
9+
второй на конец. Далее происходит итерация указателей к середине списка, где встретившись, они завершают свою работу.
10+
Этот прием часто используют в отсортированном массиве или списке. Примером, где возможно использование данного
11+
алгоритма может служить задача перестановки элементов списка задом наперед без дополнительного выделения памяти.
12+
13+
```python
14+
def reverse_arr(arr: List[int]) -> list:
15+
i = 0
16+
j = len(arr) - 1
17+
while i < j:
18+
arr[i], arr[j] = arr[j], arr[i]
19+
i += 1
20+
j -= 1
21+
22+
return arr
23+
```
24+
25+
Этот сценарий хорошо работаем в массиве или в двусвязном списке, но мы не можем его использовать в односвязном списке,
26+
т.к. проход по нему происходит в одном направлении.
27+
28+
![Метод двух указателей. Вариант 2.](../img/two_pointers_2.png)
29+
30+
2. Второй сценарий использования двух указателей, это когда в начальный момент времени они указывают на один элемент
31+
списка, проход по списку происходит в одном направлении, но указатели двигаются с разной скоростью - один быстрее
32+
другого. Данный алгоритм мы можем применить как в массиве или в двусвязном списке, так и в односвязном списке. Обычно
33+
можно принять, что за один шаг "медленного" указателя "быстрый" делает два шага. В тех задачах, где требуется
34+
определить стратегию движения указателей можно использовать данный алгоритм. Обычно это задачи с ограничением по
35+
памяти типа "замены на месте". Например, быстрый указатель служит для итерации по последовательности, а более
36+
медленный указывает на позицию для нового добавления элемента
37+
38+
```python
39+
def test_remove_element():
40+
nums = [0, 1, 2, 2, 3, 0, 4, 2]
41+
assert 5 == remove_element(nums, 2)
42+
print(nums[:5]) # [0, 1, 3, 0, 4]
43+
44+
45+
def remove_element(arr: List[int], val: int):
46+
"""
47+
Выполняет удаление значение из массива "на месте"
48+
:param arr: данный массив
49+
:param val: значение, которое следует удалить
50+
:return: длинна массива с актуальными значениями
51+
"""
52+
j = 0 # j is slow runner
53+
for i in range(len(arr)): # i is fast runner
54+
if arr[i] != val:
55+
arr[j] = arr[i]
56+
j += 1
57+
return j
58+
```
59+
60+
Как упоминалось выше, метод двух указателей можно использовать в связных списках. Однако следует учитывать некоторые
61+
особенности:
62+
63+
1. Перед вызовом ``node.next`` следует всегда проверять, что узел ``node`` не нулевой, иначе программа вызовет
64+
исключение.
65+
2. Следует правильно выбрать условие окончания прохода по списку.
66+
67+
```python
68+
def test_has_cycle():
69+
list_1 = create_list_from_array([3, 2, 0, -4], 1)
70+
71+
assert has_cycle(list_1)
72+
73+
74+
def has_cycle(head: Optional[ListNode]) -> bool:
75+
if not head:
76+
return False
77+
78+
first_pointer = head
79+
second_pointer = head
80+
81+
while True:
82+
# move slow pointer one step each time
83+
first_pointer = first_pointer.next
84+
# move fast pointer two steps each time
85+
second_pointer = second_pointer.next
86+
if not second_pointer:
87+
break
88+
second_pointer = second_pointer.next
89+
if not second_pointer:
90+
break
91+
92+
# change this condition to fit specific problem
93+
if id(first_pointer) == id(second_pointer):
94+
return True
95+
96+
return False
97+
```

0 commit comments

Comments
 (0)