├── .gitignore
├── img
├── stack.jpg
├── go-struct.png
└── go-struct-2x.png
├── main.go
├── queue-linked.go
├── stack-linked.go
├── queue.go
├── stack.go
├── set.go
├── linked-list.go
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
--------------------------------------------------------------------------------
/img/stack.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goavengers/go-datastructure/HEAD/img/stack.jpg
--------------------------------------------------------------------------------
/img/go-struct.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goavengers/go-datastructure/HEAD/img/go-struct.png
--------------------------------------------------------------------------------
/img/go-struct-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goavengers/go-datastructure/HEAD/img/go-struct-2x.png
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | // UseStack()
5 | // UseQueue()
6 | // UseSet()
7 | // UseLinkedList()
8 | // UseLinkedStack()
9 | // UseLinkedQueue()
10 | }
11 |
--------------------------------------------------------------------------------
/queue-linked.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func UseLinkedQueue() {
6 | queue := &LinkedQueue{}
7 | queue.Enqueue(1)
8 | queue.Enqueue(2)
9 | queue.Enqueue(3)
10 |
11 | queue.ToString()
12 | queue.Dequeue()
13 | fmt.Println(queue.Peek())
14 | queue.ToString()
15 | }
16 |
17 | type LinkedQueue struct {
18 | linkedList LinkedList
19 | }
20 |
21 | func (lq *LinkedQueue) Enqueue(value interface{}) {
22 | lq.linkedList.Add(value)
23 | }
24 |
25 | func (lq *LinkedQueue) Dequeue() interface{} {
26 | if ok, removed := lq.linkedList.RemoveHead(); ok {
27 | return removed.value
28 | }
29 |
30 | return nil
31 | }
32 |
33 | func (lq *LinkedQueue) Peek() interface{} {
34 | if lq.linkedList.head == nil {
35 | return nil
36 | }
37 |
38 | return lq.linkedList.head.value
39 | }
40 |
41 | func (lq *LinkedQueue) ToString() {
42 | lq.linkedList.PrintNodes()
43 | }
44 |
--------------------------------------------------------------------------------
/stack-linked.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func UseLinkedStack() {
6 | stack := NewLinkedStack()
7 | stack.Push(1)
8 | stack.Push(2)
9 |
10 | fmt.Println(stack.Peek())
11 | fmt.Println(stack.Pop())
12 | fmt.Println(stack.Peek())
13 |
14 | stack.Push("String")
15 | fmt.Println(stack.Size())
16 | fmt.Println(stack.Peek())
17 | fmt.Println(stack.Pop())
18 | fmt.Println(stack.Peek())
19 | }
20 |
21 | type LinkedStack struct {
22 | linkedList LinkedList
23 | }
24 |
25 | func NewLinkedStack() LinkedStack {
26 | return LinkedStack{linkedList: LinkedList{
27 | count: 0,
28 | head: nil,
29 | tail: nil,
30 | }}
31 | }
32 |
33 | func (ls *LinkedStack) Peek() interface{} {
34 | if ls.IsEmpty() {
35 | return nil
36 | }
37 |
38 | return ls.linkedList.head.value
39 | }
40 |
41 | func (ls *LinkedStack) Push(value interface{}) {
42 | ls.linkedList.PreAdd(value)
43 | }
44 |
45 | func (ls *LinkedStack) Pop() interface{} {
46 | if ok, removed := ls.linkedList.RemoveHead(); ok {
47 | return removed.value
48 | }
49 |
50 | return nil
51 | }
52 |
53 | func (ls *LinkedStack) IsEmpty() bool {
54 | return ls.linkedList.head == nil
55 | }
56 |
57 | func (ls *LinkedStack) Size() int {
58 | return ls.linkedList.Size()
59 | }
60 |
--------------------------------------------------------------------------------
/queue.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "sync"
6 | )
7 |
8 | func UseQueue() {
9 | queue := &Queue{}
10 | queue.Enqueue(1)
11 | queue.Enqueue(2)
12 | queue.Enqueue(3)
13 |
14 | fmt.Println(queue.Collections())
15 | queue.Dequeue()
16 | fmt.Println(queue.Peek())
17 | fmt.Println(queue.Collections())
18 | }
19 |
20 | type Queue struct {
21 | mt sync.RWMutex
22 | collection []interface{}
23 | }
24 |
25 | // Добавляет элемент в очередь.
26 | // Новые элементы очереди можно добавлять как в начало списка, так и в конец.
27 | // Важно только, чтобы элементы доставались с противоположного края.
28 | // Сложность: O(1).
29 | func (q *Queue) Enqueue(value interface{}) {
30 | q.mt.Lock()
31 | defer q.mt.Unlock()
32 | q.collection = append(q.collection, value)
33 | }
34 |
35 | // Удаляет первый помещенный элемент из очереди и возвращает его.
36 | // Если очередь пустая, возвращает nil
37 | // Поскольку мы вставляем элементы в конец списка, убирать мы их будем с начала.
38 | // Сложность: O(1).
39 | func (q *Queue) Dequeue() interface{} {
40 | q.mt.Lock()
41 | defer q.mt.Unlock()
42 |
43 | if q.isEmpty() {
44 | return nil
45 | }
46 |
47 | val := q.collection[0]
48 | q.collection = q.collection[1:]
49 |
50 | return val
51 | }
52 |
53 | // Возвращает элемент, который вернет следующий вызов метода Dequeue.
54 | // Очередь остается без изменений. Если очередь пустая, возврщается nil
55 | // Сложность: O(1)
56 | func (q *Queue) Peek() interface{} {
57 | q.mt.RLock()
58 | defer q.mt.RUnlock()
59 |
60 | if q.isEmpty() {
61 | return nil
62 | }
63 |
64 | return q.collection[0]
65 | }
66 |
67 | // Возвращает количество элементов в очереди или 0, если очередь пустая.
68 | // Сложность: O(1).
69 | func (q *Queue) Count() int {
70 | return len(q.collection)
71 | }
72 |
73 | func (q *Queue) isEmpty() bool {
74 | return q.Count() == 0
75 | }
76 |
77 | func (q *Queue) Collections() []interface{} {
78 | return q.collection
79 | }
80 |
--------------------------------------------------------------------------------
/stack.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "sync"
6 | )
7 |
8 | func UseStack() {
9 | stack := NewStack()
10 | stack.Push(1)
11 | stack.Push(2)
12 |
13 | fmt.Println(stack.Peek())
14 | fmt.Println(stack.Pop())
15 | fmt.Println(stack.Peek())
16 |
17 | stack.Push("String")
18 | fmt.Println(stack.Size())
19 | fmt.Println(stack.Peek())
20 | fmt.Println(stack.Pop())
21 | fmt.Println(stack.Peek())
22 | }
23 |
24 | type Stack struct {
25 | mt sync.RWMutex
26 | collection map[int]interface{}
27 | count int
28 | }
29 |
30 | func NewStack() *Stack {
31 | return &Stack{
32 | collection: map[int]interface{}{},
33 | count: 0,
34 | }
35 | }
36 |
37 | // Adds a value onto the end of the stack
38 | // Добавляет элемент на вершину стека.
39 | // Сложность: O(1).
40 | func (s *Stack) Push(value interface{}) {
41 | s.mt.Lock()
42 | defer s.mt.Unlock()
43 | s.collection[s.count] = value
44 | s.Inc()
45 | }
46 |
47 | // Removes and returns the value at the end of the stack
48 | // Удаляет элемент с вершины стека и возвращает его. Если стек пустой, возвращает nil
49 | // Т.к.`Push` добавляет элементы в конец списка, поэтому забирать их будет также с конца.
50 | // Сложность: O(1).
51 | func (s *Stack) Pop() interface{} {
52 | s.mt.Lock()
53 | defer s.mt.Unlock()
54 | if s.Size() == 0 {
55 | return nil
56 | }
57 |
58 | s.Dec()
59 | result := s.collection[s.Size()]
60 | delete(s.collection, s.Size())
61 | return result
62 | }
63 |
64 | // Returns the value at the end of the stack
65 | // Возвращает верхний элемент стека, но не удаляет его.
66 | // Сложность: O(1).
67 | func (s *Stack) Peek() interface{} {
68 | s.mt.RLock()
69 | defer s.mt.RUnlock()
70 |
71 | if s.Size() == 0 {
72 | return nil
73 | }
74 |
75 | return s.collection[s.Size()-1]
76 | }
77 |
78 | // Get count elements in stack
79 | // Возвращает количество элементов в стеке.
80 | // Зачем нам знать, сколько элементов находится в стеке, если мы все равно не имеем к ним доступа?
81 | // С помощью этого поля мы можем проверить, есть ли элементы на стеке или он пуст.
82 | // Сложность: O(1).
83 | func (s *Stack) Size() int {
84 | return s.count
85 | }
86 |
87 | func (s *Stack) Inc() {
88 | s.count++
89 | }
90 |
91 | func (s *Stack) Dec() {
92 | s.count--
93 | }
94 |
--------------------------------------------------------------------------------
/set.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func UseSet() {
6 | setA := &Set{}
7 | setB := &Set{}
8 |
9 | setA.Add(10)
10 | setA.Add(20)
11 |
12 | setB.Add(10)
13 | setB.Add(20)
14 | setB.Add(50)
15 | setB.Add(60)
16 | setB.Add(70)
17 |
18 | fmt.Println(setA.Subset(setB))
19 | fmt.Println(setA.Intersection(setB).Collections())
20 | fmt.Println(setB.Difference(setA).Collections())
21 |
22 | fmt.Println(setA.Collections())
23 | setA.Remove(10)
24 | setA.Add(20)
25 | fmt.Println(setA.Collections())
26 |
27 | fmt.Println(setA.Contains(10))
28 |
29 | setA.Add(10)
30 | setA.Add(60)
31 | fmt.Println(setA.Collections())
32 | fmt.Println(setB.Collections())
33 | fmt.Println(setB.SymmetricDifference(setA))
34 | }
35 |
36 | type Set struct {
37 | collection []interface{}
38 | }
39 |
40 | func (s *Set) Collections() []interface{} {
41 | return s.collection
42 | }
43 |
44 | // Поведение: Добавляет элементы в множество.
45 | // Если элемент уже присутствует в множестве, возвращается false, иначе true.
46 | // Сложность: O(n)
47 | func (s *Set) Add(value interface{}) bool {
48 | if _, exist := s.Contains(value); exist == false {
49 | s.collection = append(s.collection, value)
50 | return true
51 | }
52 |
53 | return false
54 | }
55 |
56 | // Поведение: Возвращает index, true, если множество содержит указанный элемент.
57 | // В противном случае возвращает index, false.
58 | // Сложность: O(n)
59 | func (s *Set) Contains(value interface{}) (int, bool) {
60 | for k, v := range s.collection {
61 | if v == value {
62 | return k, true
63 | }
64 | }
65 |
66 | return 0, false
67 | }
68 |
69 | // Поведение: Удаляет указанный элемент из множества и возвращает true.
70 | // В случае, если элемент нет и он не удален, возвращает false.
71 | // Сложность: O(n)
72 | func (s *Set) Remove(value interface{}) bool {
73 | if i, exist := s.Contains(value); exist == true {
74 | length := s.Size()
75 |
76 | s.collection[i] = s.collection[length-1]
77 | s.collection = s.collection[:length-1]
78 |
79 | return true
80 | }
81 |
82 | return false
83 | }
84 |
85 | func (s *Set) Size() int {
86 | return len(s.collection)
87 | }
88 |
89 | // Поведение: Возвращает множество, полученное операцией объединения его с указанным.
90 | // Сложность: O(m·n), где m и n — количество элементов переданного и текущего множеств соответственно.
91 | func (s *Set) Union(set *Set) *Set {
92 | union := &Set{}
93 |
94 | for _, v := range s.Collections() {
95 | union.Add(v)
96 | }
97 |
98 | for _, v := range set.Collections() {
99 | union.Add(v)
100 | }
101 |
102 | return union
103 | }
104 |
105 | // Поведение: Возвращает множество, полученное операцией пересечения его с указанным.
106 | // Сложность: O(m·n), где m и n — количество элементов переданного и текущего множеств соответственно.
107 | func (s *Set) Intersection(set *Set) *Set {
108 | intersection := &Set{}
109 |
110 | for _, v := range s.Collections() {
111 | if _, exist := set.Contains(v); exist == true {
112 | intersection.Add(v)
113 | }
114 | }
115 |
116 | return intersection
117 | }
118 |
119 | // Поведение: Возвращает множество, являющееся разностью текущего с указанным.
120 | // Сложность: O(m·n), где m и n — количество элементов переданного и текущего множеств соответственно.
121 | func (s *Set) Difference(set *Set) *Set {
122 | difference := &Set{}
123 |
124 | for _, v := range s.Collections() {
125 | if _, exist := set.Contains(v); exist == false {
126 | difference.Add(v)
127 | }
128 | }
129 |
130 | return difference
131 | }
132 |
133 | // Поведение: Возвращает true, если второе множество является подмножеством первого, в противном случае возвращает false.
134 | // Сложность: O(m·n), где m и n — количество элементов переданного и текущего множеств соответственно.
135 | func (s *Set) Subset(set *Set) bool {
136 | if s.Size() > set.Size() {
137 | return false
138 | }
139 |
140 | return s.Difference(set).Size() == 0
141 | }
142 |
143 | // Поведение: Возвращает множество, являющееся симметрической разностью текущего с указанным.
144 | // Сложность: O(m·n), где m и n — количество элементов переданного и текущего множеств соответственно.
145 | func (s *Set) SymmetricDifference(set *Set) *Set {
146 | a := s.Difference(set)
147 | b := set.Difference(s)
148 |
149 | return a.Union(b)
150 | }
151 |
--------------------------------------------------------------------------------
/linked-list.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | func UseLinkedList() {
8 | linkedList := &LinkedList{}
9 | linkedList.Add(1)
10 | linkedList.Add(2)
11 | linkedList.Add(3)
12 | linkedList.Add(4)
13 | linkedList.PrintNodes()
14 |
15 | if index, removed := linkedList.Remove(3); removed {
16 | fmt.Println(fmt.Sprintf("Removed index %d", index))
17 | linkedList.PrintNodes()
18 | }
19 |
20 | if index, exist := linkedList.Contains(2); exist {
21 | fmt.Println(fmt.Sprintf("Exist index %d", index))
22 | }
23 |
24 | linkedList.RemoveHead()
25 | linkedList.PrintNodes()
26 | linkedList.RemoveTail()
27 | linkedList.PrintNodes()
28 | linkedList.Add(3)
29 | linkedList.Add(4)
30 | linkedList.PreAdd(1)
31 | linkedList.PrintNodes()
32 | fmt.Println(linkedList.Size())
33 | }
34 |
35 | type LinkedList struct {
36 | count int
37 | head *Node
38 | tail *Node
39 | }
40 |
41 | type Node struct {
42 | value interface{}
43 | next *Node
44 | }
45 |
46 | func (l *LinkedList) Add(value interface{}) {
47 | newNode := &Node{
48 | value: value,
49 | next: nil,
50 | }
51 |
52 | if l.head == nil {
53 | l.head = newNode
54 | l.tail = newNode
55 | } else {
56 | l.tail.next = newNode
57 | l.tail = newNode
58 | }
59 |
60 | l.count++
61 | }
62 |
63 | func (l *LinkedList) Remove(element interface{}) (int, bool) {
64 | current := l.head
65 | var previous *Node
66 |
67 | index := 0
68 | for current != nil {
69 | // find element
70 | if current.value == element {
71 |
72 | // is not first node
73 | if previous != nil {
74 |
75 | // before: Head -> 1 -> 2 -> 3 --> null
76 | // after: Head -> 1 -> 2 -------> null
77 | previous.next = current.next
78 |
79 | // set last node if current already is last
80 | if current.next == nil {
81 | l.tail = previous
82 | }
83 | } else {
84 | // before: Head -> 3 -> 5
85 | // after: Head ------> 5
86 |
87 | // Head -> 3 -> null
88 | // Head ------> null
89 |
90 | l.head = current.next
91 | // check is empty list
92 | if l.head == nil {
93 | l.tail = nil
94 | }
95 | }
96 |
97 | l.count--
98 | return index, true
99 | }
100 |
101 | index++
102 | previous = current
103 | current = current.next
104 | }
105 |
106 | return index, false
107 | }
108 |
109 | func (l *LinkedList) Contains(element interface{}) (int, bool) {
110 | current := l.head
111 |
112 | index := 0
113 | for current != nil {
114 | if current.value == element {
115 | return index, true
116 | }
117 |
118 | current = current.next
119 | index++
120 | }
121 |
122 | return index, false
123 | }
124 |
125 | func (l *LinkedList) Size() int {
126 | return l.count
127 | }
128 |
129 | func (l *LinkedList) PreAdd(value interface{}) {
130 | newNode := &Node{
131 | value: value,
132 | next: l.head,
133 | }
134 |
135 | l.head = newNode
136 |
137 | if l.tail == nil {
138 | l.tail = newNode
139 | }
140 |
141 | l.count++
142 | }
143 |
144 | func (l *LinkedList) RemoveHead() (bool, *Node) {
145 | if l.head == nil {
146 | return false, nil
147 | }
148 |
149 | tmpHead := l.head
150 |
151 | if l.head.next != nil {
152 | l.head = l.head.next
153 | } else {
154 | l.head = nil
155 | l.head = nil
156 | }
157 |
158 | l.count--
159 | return true, tmpHead
160 | }
161 |
162 | func (l *LinkedList) RemoveTail() (bool, *Node) {
163 | tmpTail := l.tail
164 |
165 | if l.head.next == nil {
166 | // There is only one node in linked list.
167 |
168 | l.head = nil
169 | l.tail = nil
170 | l.count--
171 | return true, tmpTail
172 | }
173 |
174 | // If there are many nodes in linked list...
175 | // Rewind to the last node and delete "next" link for the node before the last one.
176 |
177 | currentNode := l.head
178 | for currentNode.next != nil {
179 | if currentNode.next.next == nil {
180 | currentNode.next = nil
181 | } else {
182 | currentNode = currentNode.next
183 | }
184 | }
185 |
186 | l.count--
187 | l.tail = currentNode
188 | return true, tmpTail
189 | }
190 |
191 | // help method
192 | func (l *LinkedList) PrintNodes() {
193 | currentNode := l.head
194 | chain := ""
195 |
196 | for currentNode.next != nil {
197 | chain += fmt.Sprintf("| %v : next | --> ", currentNode.value)
198 | currentNode = currentNode.next
199 | }
200 |
201 | // last node
202 | chain += fmt.Sprintf("| %v : next |", currentNode.value)
203 | fmt.Println(chain)
204 | }
205 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |
Структуры данных: 10 типов структур данных, которые необходимо знать каждому
4 |
Вместе мы разберемся!
5 |
6 |
7 | ## Содержание
8 |
9 | 1. [Зачем нужны структуры данных?](#why_are_structures_needed)
10 | 2. [Наиболее распространенные структуры данных](#most_popular_structures)
11 | 3. [Стек (Stack)](#stack)
12 | 1. [Реализация](#stack_implementation)
13 | 2. [Сложность](#stack_complexity)
14 | 3. [Вопросы о стеке, часто задаваемые на собеседованиях](#stack_interview)
15 | 4. [Очереди (Queue)](#queue)
16 | 1. [Реализация](#queue_implementation)
17 | 2. [Сложность](#queue_complexity)
18 | 3. [Вопросы о очередях, часто задаваемые на собеседованиях](#queue_interview)
19 | 5. [Множества (Sets)](#sets)
20 | 1. [Реализация](#sets_implementation)
21 | 2. [Сложность](#sets_complexity)
22 | 3. [Вопросы о множествах, часто задаваемые на собеседованиях](#sets_interview)
23 | 6. [Связанные списки (Linked Lists)](#linked_lists)
24 | 1. [Реализация](#linked_lists_implementation)
25 | 2. [Сложность](#linked_lists_complexity)
26 | 3. [Реализация стека и очереди на основе связанных списков](#linked_lists_stack_queue)
27 | 4. [Двусвязанные списки (Double Linked List)](#linked_lists_double)
28 | 5. [Вопросы о связанных списках, часто задаваемые на собеседованиях](#linked_lists_interview)
29 | 7. Массивы
30 | 1. [Реализация](#array_implementation)
31 | 2. [Сложность](#array_complexity)
32 | 3. [Вопросы о множествах, часто задаваемые на собеседованиях](#array_interview)
33 | 8. Деревья
34 | 1. [Реализация](#tree_implementation)
35 | 2. [Сложность](#tree_complexity)
36 | 3. [Вопросы о множествах, часто задаваемые на собеседованиях](#tree_interview)
37 | 9. Боры
38 | 1. [Реализация](#trie_implementation)
39 | 2. [Сложность](#trie_complexity)
40 | 3. [Вопросы о множествах, часто задаваемые на собеседованиях](#trie_interview)
41 | 10. Графы
42 | 1. [Реализация](#graph_implementation)
43 | 2. [Сложность](#graph_complexity)
44 | 3. [Вопросы о множествах, часто задаваемые на собеседованиях](#graph_interview)
45 | 11. Хеш-таблицы
46 | 1. [Реализация](#hash_table_implementation)
47 | 2. [Сложность](#hash_table_complexity)
48 | 3. [Вопросы о множествах, часто задаваемые на собеседованиях](#hash_table_interview)
49 |
50 | Структуры данных играют важную роль в процессе разработки ПО, а еще по ним часто задают вопросы на собеседованиях для разработчиков. Хорошая новость в том, что по сути они представляют собой всего лишь специальные форматы для организации и хранения данных.
51 |
52 | ### :confused: Зачем нужны структуры данных?
53 |
54 | Поскольку структуры данных используются для хранения информации в упорядоченном виде, а данные — самый важный феномен в информатике, истинная ценность структур данных очевидна.
55 |
56 | Не важно, какую именно задачу вы решаете, так или иначе вам придется иметь дело с данными, будь то зарплата сотрудника, биржевые котировки, список продуктов для похода в магазин или обычный телефонный справочник.
57 |
58 | В зависимости от конкретного сценария, данные нужно хранить в подходящем формате. У нас в распоряжении — ряд структур данных, обеспечивающих нас такими различными форматами.
59 |
60 | ### :yum: Наиболее распространенные структуры данных
61 |
62 | Сначала давайте перечислим наиболее распространенные структуры данных, а затем разберем каждую по очереди:
63 |
64 | - Массивы
65 | - Динамические массивы
66 | - Множества
67 | - Стеки
68 | - Очереди
69 | - Связные списки
70 | - Деревья
71 | - Графы
72 | - Боры (в сущности, это тоже деревья, но их целесообразно рассмотреть отдельно).
73 | - Хеш-таблицы
74 |
75 | ### :sleeping: Реализации
76 |
77 | Все полные исходники реализаций структур данных вы всегда сможете найти в одноименных фалах расширения `.go`.
78 | Все структуры предоставляют функцию `Use!Structure!`, где `!Structure!` — это название структуры или псевдоним, однозначно определяющий с какой структурой мы сейчас работаем, например для стека `UseStack`, для очереди `UseQueue`, для структуры стек, основанной на связанных списках псевдоним `UseLinkedStack`.
79 |
80 | Полный список таких методов всегда предоставляется в файле `main.go`
81 |
82 | ### Очередь и стек (Queue & Stack)
83 |
84 | Начнем с этих двух, т.к. они немного схожи.
85 | По факту сами они не являются конкретными структурами данных, а объектами реализующий две операции: положить в очередь (push) и взять из очереди (pull).
86 | Для очереди порядок выдаются объекты в том же порядке — __FIFO__ (First In First Out), для стека в обратном — __LIFO__ (Last In First Out). Ну, есть ещё очередь с приоритетами — это когда первым выдаёт объект с наивысшим приоритетом.
87 |
88 | ---
89 |
90 | ### :point_right: Стек
91 |
92 | Это коллекция, элементы которой получают по принципу «последний вошел, первый вышел» (Last-In-First-Out или __LIFO__).
93 | Это значит, что мы будем иметь доступ только к последнему добавленному элементу.
94 |
95 | В отличие от списков, мы не можем получить доступ к произвольному элементу стека.
96 | Мы можем только добавлять или удалять элементы с помощью специальных методов.
97 | У стека нет также метода Contains, как у списков.
98 | Кроме того, у стека нет итератора.
99 | Для того, чтобы понимать, почему на стек накладываются такие ограничения, давайте посмотрим на то, как он работает и как используется.
100 |
101 | Наиболее часто встречающаяся аналогия для объяснения стека — стопка тарелок.
102 | Вне зависимости от того, сколько тарелок в стопке, мы всегда можем снять верхнюю.
103 | Чистые тарелки точно так же кладутся на верх стопки, и мы всегда будем первой брать ту тарелку, которая была положена последней.
104 |
105 | 
106 |
107 | Если мы положим, например, красную тарелку, затем синюю, а затем зеленую, то сначала надо будет снять зеленую, потом синюю, и, наконец, красную.
108 | Главное, что надо запомнить — тарелки всегда ставятся и на верх стопки.
109 | Когда кто-то берет тарелку, он также снимает ее сверху.
110 | Получается, что тарелки разбираются в порядке, обратном тому, в котором ставились.
111 |
112 | Существует три основных операции, которые могут выполняться в стеках:
113 | - вставка элемента в стек (называемый «push»);
114 | - удаление элемента из стека (называемое «pop»);
115 | - отображение содержимого стека (иногда называемого «peak»).
116 |
117 | Теперь, когда мы понимаем, как работает стек, введем несколько терминов.
118 | Операция добавления элемента на стек называется «push», удаления — «pop».
119 | Последний добавленный элемент называется верхушкой стека, или «top», и его можно посмотреть с помощью операции «peek».
120 |
121 | **Реализация**
122 |
123 |
124 | Скрытый код
125 |
126 | ```go
127 | type Stack struct {
128 | mt sync.RWMutex
129 | collection map[int]interface{}
130 | count int
131 | }
132 |
133 | func NewStack() *Stack {
134 | return &Stack{
135 | collection: map[int]interface{}{},
136 | count: 0,
137 | }
138 | }
139 |
140 | // Adds a value onto the end of the stack
141 | // Добавляет элемент на вершину стека.
142 | // Сложность: O(1).
143 | func (s *Stack) Push(value interface{}) {
144 | s.mt.Lock()
145 | defer s.mt.Unlock()
146 | s.collection[s.count] = value
147 | s.Inc()
148 | }
149 |
150 | // Removes and returns the value at the end of the stack
151 | // Удаляет элемент с вершины стека и возвращает его. Если стек пустой, возвращает nil
152 | // Т.к.`Push` добавляет элементы в конец списка, поэтому забирать их будет также с конца.
153 | // Сложность: O(1).
154 | func (s *Stack) Pop() interface{} {
155 | s.mt.Lock()
156 | defer s.mt.Unlock()
157 | if s.Size() == 0 {
158 | return nil
159 | }
160 |
161 | s.Dec()
162 | result := s.collection[s.Size()]
163 | delete(s.collection, s.Size())
164 | return result
165 | }
166 |
167 | // Returns the value at the end of the stack
168 | // Возвращает верхний элемент стека, но не удаляет его.
169 | // Сложность: O(1).
170 | func (s *Stack) Peek() interface{} {
171 | s.mt.RLock()
172 | defer s.mt.RUnlock()
173 |
174 | if s.Size() == 0 {
175 | return nil
176 | }
177 |
178 | return s.collection[s.Size()-1]
179 | }
180 |
181 | // Get count elements in stack
182 | // Возвращает количество элементов в стеке.
183 | // Зачем нам знать, сколько элементов находится в стеке, если мы все равно не имеем к ним доступа?
184 | // С помощью этого поля мы можем проверить, есть ли элементы на стеке или он пуст.
185 | // Сложность: O(1).
186 | func (s *Stack) Size() int {
187 | return s.count
188 | }
189 |
190 | func (s *Stack) Inc() {
191 | s.count++
192 | }
193 |
194 | func (s *Stack) Dec() {
195 | s.count--
196 | }
197 | ```
198 |
199 |
200 | **Примнение**
201 |
202 |
203 | Обра́тная по́льская запись (англ. Reverse Polish notation, RPN)
204 |
205 | ```go
206 | // todo coming soon
207 | ```
208 |
209 |
210 | **Сложность:**
211 |
212 | Алгоритм | В среднем | Худший случай
213 | --- | --- | ---
214 | Space | O(n) | O(n) |
215 | Search | O(n) | O(n) |
216 | Insert | O(1) | O(1) |
217 | Delete | O(1) | O(1) |
218 |
219 | **Вопросы о стеке, часто задаваемые на собеседованиях:**
220 |
221 | - Вычислить постфиксное выражение при помощи стека
222 | - Отсортировать значения в стеке
223 | - Проверить сбалансированные скобки в выражении
224 |
225 | ---
226 |
227 | ### :point_right: Очередь
228 |
229 | Вы можете думать об этой структуре, как об очереди людей в продуктовом магазине. Стоящий первым будет обслужен первым. Также как очередь.
230 | Если рассматривать очередь с точки доступа к данным, то она является __FIFO__ (First In First Out).
231 | Это означает, что после добавления нового элемента все элементы, которые были добавлены до этого, должны быть удалены до того, как новый элемент будет удален.
232 |
233 | В очереди есть только две основные операции и две вспомогательные:
234 | - enqueue;
235 | - dequeue;
236 | - peek;
237 | - count.
238 |
239 | Enqueue означает вставить элемент в конец очереди, а dequeue означает удаление переднего элемента.
240 |
241 | **Реализация**
242 |
243 | Скрытый код
244 |
245 | ```go
246 | type Queue struct {
247 | mt sync.RWMutex
248 | collection []interface{}
249 | }
250 |
251 | // Добавляет элемент в очередь.
252 | // Новые элементы очереди можно добавлять как в начало списка, так и в конец.
253 | // Важно только, чтобы элементы доставались с противоположного края.
254 | // Сложность: O(1).
255 | func (q *Queue) Enqueue(value interface{}) {
256 | q.mt.Lock()
257 | defer q.mt.Unlock()
258 | q.collection = append(q.collection, value)
259 | }
260 |
261 | // Удаляет первый помещенный элемент из очереди и возвращает его.
262 | // Если очередь пустая, возвращает nil
263 | // Поскольку мы вставляем элементы в конец списка, убирать мы их будем с начала.
264 | // Сложность: O(1).
265 | func (q *Queue) Dequeue() interface{} {
266 | q.mt.Lock()
267 | defer q.mt.Unlock()
268 |
269 | if q.isEmpty() {
270 | return nil
271 | }
272 |
273 | val := q.collection[0]
274 | q.collection = q.collection[1:]
275 |
276 | return val
277 | }
278 |
279 | // Возвращает элемент, который вернет следующий вызов метода Dequeue.
280 | // Очередь остается без изменений. Если очередь пустая, возврщается nil
281 | // Сложность: O(1)
282 | func (q *Queue) Peek() interface{} {
283 | q.mt.RLock()
284 | defer q.mt.RUnlock()
285 |
286 | if q.isEmpty() {
287 | return nil
288 | }
289 |
290 | return q.collection[0]
291 | }
292 |
293 | // Возвращает количество элементов в очереди или 0, если очередь пустая.
294 | // Сложность: O(1).
295 | func (q *Queue) Count() int {
296 | return len(q.collection)
297 | }
298 |
299 | func (q *Queue) isEmpty() bool {
300 | return q.Count() == 0
301 | }
302 |
303 | func (q *Queue) Collections() []interface{} {
304 | return q.collection
305 | }
306 | ```
307 |
308 |
309 | **Сложность:**
310 |
311 | Алгоритм | В среднем | Худший случай
312 | --- | --- | ---
313 | Space | O(n) | O(n) |
314 | Search | O(n) | O(n) |
315 | Insert | O(1) | O(1) |
316 | Delete | O(1) | O(1) |
317 |
318 | **Вопросы о стеке, часто задаваемые на собеседованиях:**
319 |
320 | - Реализуйте стек при помощи очереди
321 | - Обратите первые k элементов в очереди
322 | - Сгенерируйте двоичные числа от 1 до n при помощи очереди
323 |
324 | ---
325 |
326 | ### :point_right: Множества (Sets)
327 |
328 | Это коллекция, которая реализует основные математические операции над множествами:
329 |
330 | __Пересечения__ (intersection) - если заданы два множества, эта функция вернет другое множество, содержащее элементы, которые имеются и в первом и во втором множестве;
331 |
332 | ```go
333 | [1, 2, 3, 4] intersect [3, 4, 5, 6] = [3, 4]
334 | ```
335 |
336 | __Объединение__ (union) - объединяет все элементы из двух разных множеств и возвращает результат, как новый набор (без дубликатов);
337 |
338 | ```go
339 | [1, 2, 3, 4] union [3, 4, 5, 6] = [1, 2, 3, 4, 5, 6]
340 | ```
341 |
342 | __Разность__ (difference) - вернет список элементов, которые находятся в одном множестве, но НЕ повторяются в другом;
343 |
344 | ```go
345 | [1, 2, 3, 4] difference [3, 4, 5, 6] = [1, 2]
346 | ```
347 |
348 | __Симметрическая разность__ (symmetric difference) - это все элементы, которые содержатся только в одном из рассматриваемых множеств;
349 |
350 | ```go
351 | [1, 2, 3, 4] symmetric difference [3, 4, 5, 6] = [1, 2, 5, 6]
352 | ```
353 |
354 | Вы, возможно, заметили, что симметрическая разность — это «пересечение наоборот». Учитывая это, давайте попробуем найти ее, используя уже имеющиеся операции.
355 |
356 | Итак, мы хотим получить множество, которое содержит все элементы из двух множеств, за исключением тех, которые есть в обоих. Другими словами, мы хотим получить разность объединения двух множеств и их пересечения.
357 |
358 | Или, если рассматривать по шагам:
359 |
360 | ```go
361 | [1, 2, 3, 4] union [3, 4, 5, 6] = [1, 2, 3, 4, 5, 6]
362 |
363 | [1, 2, 3, 4] intersection [3, 4, 5, 6] = [3, 4]
364 |
365 | [1, 2, 3, 4, 5, 6] set difference [3, 4] = [1, 2, 5, 6]
366 | ```
367 |
368 | Что дает нам нужный результат:
369 |
370 | ```go
371 | [1, 2, 5, 6]
372 | ```
373 |
374 | __Подмножество__ (subset) - возвращает булево значение, показывающее, содержит ли одно множество все элементы другого множества.
375 |
376 | ```go
377 | [1, 2, 3] is subset [0, 1, 2, 3, 4, 5] = true
378 | ```
379 |
380 | В то время как:
381 |
382 | ```go
383 | [1, 2, 3] is subset [0, 1, 2] = false
384 | ```
385 |
386 | Эту проверку можно провести, используя имещиеся методы: difference и intersection, пустой результат при разности и множество с таким же количеством элементов при пересечении говорит о том, что все элементы первого множества содержатся во втором множестве;
387 |
388 | ```go
389 | [1, 2, 3] difference [0, 1, 2, 3, 4, 5] = [] = true
390 |
391 | [1, 2, 3] intersection [0, 1, 2, 3, 4, 5] = [1, 2, 3] = true
392 | ```
393 |
394 | В общем случае, конечно, множества (sets) может иметь метод __subset__ (который может быть реализован более эффективно). Однако стоит помнить, что это уже не какая-то новая возможность, а просто другое применение существующих.
395 |
396 | **Помните:** Множества хранят данные без определенного порядка и без повторяющихся значений.
397 |
398 | **Реализация**
399 |
400 |
401 | Скрытый код
402 |
403 | ```go
404 | type Set struct {
405 | collection []interface{}
406 | }
407 |
408 | func (s *Set) Collections() []interface{} {
409 | return s.collection
410 | }
411 |
412 | // Поведение: Добавляет элементы в множество.
413 | // Если элемент уже присутствует в множестве, возвращается false, иначе true.
414 | // Сложность: O(n)
415 | func (s *Set) Add(value interface{}) bool {
416 | if _, exist := s.Contains(value); exist == false {
417 | s.collection = append(s.collection, value)
418 | return true
419 | }
420 |
421 | return false
422 | }
423 |
424 | // Поведение: Возвращает index, true, если множество содержит указанный элемент.
425 | // В противном случае возвращает index, false.
426 | // Сложность: O(n)
427 | func (s *Set) Contains(value interface{}) (int, bool) {
428 | for k, v := range s.collection {
429 | if v == value {
430 | return k, true
431 | }
432 | }
433 |
434 | return 0, false
435 | }
436 |
437 | // Поведение: Удаляет указанный элемент из множества и возвращает true.
438 | // В случае, если элемент нет и он не удален, возвращает false.
439 | // Сложность: O(n)
440 | func (s *Set) Remove(value interface{}) bool {
441 | if i, exist := s.Contains(value); exist == true {
442 | length := s.Size()
443 |
444 | s.collection[i] = s.collection[length-1]
445 | s.collection = s.collection[:length-1]
446 |
447 | return true
448 | }
449 |
450 | return false
451 | }
452 |
453 | func (s *Set) Size() int {
454 | return len(s.collection)
455 | }
456 |
457 | // Поведение: Возвращает множество, полученное операцией объединения его с указанным.
458 | // Сложность: O(m·n), где m и n — количество элементов переданного и текущего множеств соответственно.
459 | func (s *Set) Union(set *Set) *Set {
460 | union := &Set{}
461 |
462 | for _, v := range s.Collections() {
463 | union.Add(v)
464 | }
465 |
466 | for _, v := range set.Collections() {
467 | union.Add(v)
468 | }
469 |
470 | return union
471 | }
472 |
473 | // Поведение: Возвращает множество, полученное операцией пересечения его с указанным.
474 | // Сложность: O(m·n), где m и n — количество элементов переданного и текущего множеств соответственно.
475 | func (s *Set) Intersection(set *Set) *Set {
476 | intersection := &Set{}
477 |
478 | for _, v := range s.Collections() {
479 | if _, exist := set.Contains(v); exist == true {
480 | intersection.Add(v)
481 | }
482 | }
483 |
484 | return intersection
485 | }
486 |
487 | // Поведение: Возвращает множество, являющееся разностью текущего с указанным.
488 | // Сложность: O(m·n), где m и n — количество элементов переданного и текущего множеств соответственно.
489 | func (s *Set) Difference(set *Set) *Set {
490 | difference := &Set{}
491 |
492 | for _, v := range s.Collections() {
493 | if _, exist := set.Contains(v); exist == false {
494 | difference.Add(v)
495 | }
496 | }
497 |
498 | return difference
499 | }
500 |
501 | // Поведение: Возвращает true, если второе множество является подмножеством первого, в противном случае возвращает false.
502 | // Сложность: O(m·n), где m и n — количество элементов переданного и текущего множеств соответственно.
503 | func (s *Set) Subset(set *Set) bool {
504 | if s.Size() > set.Size() {
505 | return false
506 | }
507 |
508 | return s.Difference(set).Size() == 0
509 | }
510 |
511 | // Поведение: Возвращает множество, являющееся симметрической разностью текущего с указанным.
512 | // Сложность: O(m·n), где m и n — количество элементов переданного и текущего множеств соответственно.
513 | func (s *Set) SymmetricDifference(set *Set) *Set {
514 | a := s.Difference(set)
515 | b := set.Difference(s)
516 |
517 | return a.Union(b)
518 | }
519 | ```
520 |
521 |
522 | ---
523 |
524 | ### :point_right: Связанные списки (Linked List)
525 |
526 | Основное назначение связного списка — предоставление механизма для хранения и доступа к произвольному количеству данных. Как следует из названия, это достигается связыванием данных вместе в список.
527 |
528 | Для начала определим несколько терминов, чтобы в дальнейшем не возникло недопонимания:
529 |
530 | - __указатель__ содержит адрес участка памяти, хранящего определённые данные (для указателей также допустимо нулевое значение);
531 | - __ссылка__, в отличие от указателя, всегда должна указывать на определённый адрес;
532 | - __структура данных__ — способ группировки информации, который может быть реализован на любом языке программирования.
533 |
534 | Простейший способ создать односвязный список — поочерёдно создать и связать узлы:
535 |
536 | ```go
537 | type Node struct {
538 | value interface{}
539 | next *Node
540 | }
541 |
542 | first := &Node{3}
543 | moddle := &Node{5}
544 | last := &Node{7}
545 |
546 | first.next = middle
547 |
548 | // +-----+------+ +-----+------+
549 | // | 3 | *---+--->| 5 | null +
550 | // +-----+------+ +-----+------+
551 |
552 | middle.next = last
553 |
554 | // +-----+------+ +-----+------+ +-----+------+
555 | // | 3 | *---+--->| 5 | *---+-->| 7 | null +
556 | // +-----+------+ +-----+------+ +-----+------+
557 |
558 | ```
559 |
560 | **Реализация**
561 |
562 |
563 | Скрытый код
564 |
565 | ```go
566 | type LinkedList struct {
567 | count int
568 | head *Node
569 | tail *Node
570 | }
571 |
572 | type Node struct {
573 | value interface{}
574 | next *Node
575 | }
576 |
577 | func (l *LinkedList) Add(value interface{}) {
578 | newNode := &Node{
579 | value: value,
580 | next: nil,
581 | }
582 |
583 | if l.head == nil {
584 | l.head = newNode
585 | l.tail = newNode
586 | } else {
587 | l.tail.next = newNode
588 | l.tail = newNode
589 | }
590 |
591 | l.count++
592 | }
593 |
594 | func (l *LinkedList) Remove(element interface{}) (int, bool) {
595 | current := l.head
596 | var previous *Node
597 |
598 | index := 0
599 | for current != nil {
600 | // find element
601 | if current.value == element {
602 |
603 | // is not first node
604 | if previous != nil {
605 |
606 | // before: Head -> 1 -> 2 -> 3 --> null
607 | // after: Head -> 1 -> 2 -------> null
608 | previous.next = current.next
609 |
610 | // set last node if current already is last
611 | if current.next == nil {
612 | l.tail = previous
613 | }
614 | } else {
615 | // before: Head -> 3 -> 5
616 | // after: Head ------> 5
617 |
618 | // Head -> 3 -> null
619 | // Head ------> null
620 |
621 | l.head = current.next
622 | // check is empty list
623 | if l.head == nil {
624 | l.tail = nil
625 | }
626 | }
627 |
628 | l.count--
629 | return index, true
630 | }
631 |
632 | index++
633 | previous = current
634 | current = current.next
635 | }
636 |
637 | return index, false
638 | }
639 |
640 | func (l *LinkedList) Contains(element interface{}) (int, bool) {
641 | current := l.head
642 |
643 | index := 0
644 | for current != nil {
645 | if current.value == element {
646 | return index, true
647 | }
648 |
649 | current = current.next
650 | index++
651 | }
652 |
653 | return index, false
654 | }
655 |
656 | func (l *LinkedList) Size() int {
657 | return l.count
658 | }
659 | ```
660 |
661 |
662 | **Реализация стека и очереди на основе связанных списков**
663 |
664 | С помощью связанных списков можно можно создавать такие структуры данных, как [Стеки (Stack)](#stack) и [Очереди (Queue)](#queue), которые мы уже реализовывали выше, давайте попробуем реализовать их с помощью связанных списков.
665 |
666 | Для этого, нам понадобится немного модифицировать существующий код связанных списков и добавить к ним несколько методов, которые сильно напоминают методы из двухсвязанных списков [(Double Linked List)](#linked_list_double) о которых мы поговорим чуть позже.
667 |
668 | ```go
669 | // данный метод добавляет элемент перед первым элементом в списке
670 | func (l *LinkedList) PreAdd(value interface{}) {
671 | newNode := &Node{
672 | value: value,
673 | next: l.head,
674 | }
675 |
676 | l.head = newNode
677 |
678 | if l.tail == nil {
679 | l.tail = newNode
680 | }
681 |
682 | l.count++
683 | }
684 |
685 | // следующие два метода удаляют с начала и конца соответственно названиям метода
686 | func (l *LinkedList) RemoveHead() (bool, *Node) {
687 | if l.head == nil {
688 | return false, nil
689 | }
690 |
691 | tmpHead := l.head
692 |
693 | if l.head.next != nil {
694 | l.head = l.head.next
695 | } else {
696 | l.head = nil
697 | l.head = nil
698 | }
699 |
700 | l.count--
701 | return true, tmpHead
702 | }
703 |
704 | func (l *LinkedList) RemoveTail() (bool, *Node) {
705 | tmpTail := l.tail
706 |
707 | if l.head.next == nil {
708 | // There is only one node in linked list.
709 |
710 | l.head = nil
711 | l.tail = nil
712 | l.count--
713 | return true, tmpTail
714 | }
715 |
716 | // If there are many nodes in linked list...
717 | // Rewind to the last node and delete "next" link for the node before the last one.
718 |
719 | currentNode := l.head
720 | for currentNode.next != nil {
721 | if currentNode.next.next == nil {
722 | currentNode.next = nil
723 | } else {
724 | currentNode = currentNode.next
725 | }
726 | }
727 |
728 | l.count--
729 | l.tail = currentNode
730 | return true, tmpTail
731 | }
732 | ```
733 |
734 | Теперь, когда мы добавили необходимые методы, мы можем реализовывать структуру данных Стек с помощью связанных списков:
735 |
736 |
737 | Реализация структуры Стек с помощью связанного списка
738 |
739 | ```go
740 | type LinkedStack struct {
741 | linkedList LinkedList
742 | }
743 |
744 | func NewLinkedStack() LinkedStack {
745 | return LinkedStack{linkedList: LinkedList {
746 | count: 0,
747 | head: nil,
748 | tail: nil,
749 | }}
750 | }
751 |
752 | func (ls *LinkedStack) Peek() interface{} {
753 | if ls.IsEmpty() {
754 | return nil
755 | }
756 |
757 | return ls.linkedList.head.value
758 | }
759 |
760 | func (ls *LinkedStack) Push(value interface{}) {
761 | ls.linkedList.PreAdd(value)
762 | }
763 |
764 | func (ls *LinkedStack) Pop() interface{} {
765 | if ok, removed := ls.linkedList.RemoveHead(); ok {
766 | return removed.value
767 | }
768 |
769 | return nil
770 | }
771 |
772 | func (ls *LinkedStack) IsEmpty() bool {
773 | return ls.linkedList.head == nil
774 | }
775 |
776 | func (ls *LinkedStack) Size() int {
777 | return ls.linkedList.Size()
778 | }
779 | ```
780 |
781 |
782 | Таким же образом реализуем структуру очередь:
783 |
784 |
785 | Реализация структуры Очередь с помощью связанного списка
786 |
787 | ```go
788 | type LinkedQueue struct {
789 | linkedList LinkedList
790 | }
791 |
792 | func (lq *LinkedQueue) Enqueue(value interface{}) {
793 | lq.linkedList.Add(value)
794 | }
795 |
796 | func (lq *LinkedQueue) Dequeue() interface{} {
797 | if ok, removed := lq.linkedList.RemoveHead(); ok {
798 | return removed.value
799 | }
800 |
801 | return nil
802 | }
803 |
804 | func (lq *LinkedQueue) Peek() interface{} {
805 | if lq.linkedList.head == nil {
806 | return nil
807 | }
808 |
809 | return lq.linkedList.head.value
810 | }
811 |
812 | func (lq *LinkedQueue) ToString() {
813 | lq.linkedList.PrintNodes()
814 | }
815 | ```
816 |
817 |
818 | Вот так вот легко реализовывать структуры данных поверх другой структуры данных.
819 | Сравните полученный код с "нативной" реализацией и вы увидите, что код стал короче и компактнее, а так же то, что код выглядит более унифицированным.
820 |
821 | **Сложность:**
822 |
823 | Алгоритм | В среднем | Худший случай
824 | --- | --- | ---
825 | Space | O(n) | O(n) |
826 | Search | O(n) | O(n) |
827 | Insert | O(1) | O(1) |
828 | Delete | O(1) | O(1) |
829 |
830 | **Вопросы о связанных списках, часто задаваемые на собеседованиях:**
831 |
832 | - Обратите связный список
833 | - Найдите петлю в связном списке
834 | - Возвратите N-ный узел с начала связного списка
835 | - Удалите из связного списка дублирующиеся значения
836 |
837 | ---
838 |
--------------------------------------------------------------------------------