Skip to content

Commit 3de031e

Browse files
authored
Merge pull request #4 from mohameds-dev/dev
Dev
2 parents 2485e24 + 7ab74c5 commit 3de031e

15 files changed

Lines changed: 615 additions & 36 deletions

CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,18 @@ add_executable(
3030
tests/doubly_linked_list/pop_front_tests.cpp
3131
tests/doubly_linked_list/push_back_tests.cpp
3232
tests/doubly_linked_list/push_front_tests.cpp
33+
tests/doubly_linked_list/constructor_tests.cpp
3334
tests/doubly_linked_list/clear_tests.cpp
35+
tests/doubly_linked_list/erase_tests.cpp
36+
tests/doubly_linked_list/move_to_begin_tests.cpp
37+
tests/doubly_linked_list/emplace_back_tests.cpp
3438
tests/ordered_map/size_tests.cpp
3539
tests/ordered_map/insertion_tests.cpp
3640
tests/ordered_map/lookup_tests.cpp
3741
tests/ordered_map/iterator_tests.cpp
3842
tests/ordered_map/constructor_tests.cpp
43+
tests/ordered_map/find_tests.cpp
44+
tests/ordered_map/move_to_front_tests.cpp
3945
)
4046
target_link_libraries(tests PRIVATE ordered_map Catch2::Catch2WithMain)
4147

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,9 @@ ordered_map/
194194
./build.sh
195195
```
196196

197-
## Future Improvements
197+
## Future Features
198+
199+
- [ ] Add `move_to_front` and `move_to_back` operations in ordered_map to re-order entries (without affecting or copying the entry value)
198200

199201
- [ ] Add const iterators
200202
- [ ] Add reverse iterators
@@ -206,8 +208,7 @@ ordered_map/
206208
- [ ] **DoublyLinkedList:** Add `erase` method to erase elements given their iterator
207209
- [ ] **DoublyLinkedList:** Add pre and post decrement operators
208210
- [ ] **DoublyLinkedList:** Test front(), back(), insertion and deletion functions for copying behavior
209-
- [ ] **OrderedMap:** Add support for initializing map with
211+
- [ ] **OrderedMap:** Add support for initializing map with
210212
- [ ] **OrderedMap:** Add support for custom hash functions
211213
- [ ] **OrderedMap:** Add `erase` method to erase elements given their iterator or key
212214
- [ ] Test for memory leaks
213-

include/doubly_linked_list.hpp

Lines changed: 154 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@ struct Node {
99
std::unique_ptr<Node> next;
1010
Node* prev;
1111

12-
Node(const T& a_value, Node* a_prev, std::unique_ptr<Node> a_next)
13-
: value(a_value), prev(a_prev), next(std::move(a_next)) {}
12+
template<typename U>
13+
Node(U&& a_value, Node* a_prev, std::unique_ptr<Node> a_next)
14+
: value(std::forward<U>(a_value)), prev(a_prev), next(std::move(a_next)) {}
15+
16+
template <typename... Args>
17+
Node(Node* p, std::unique_ptr<Node> n, Args&&... args)
18+
: value(std::forward<Args>(args)...), prev(p), next(std::move(n)) {}
19+
1420
};
1521

1622
template <typename T>
@@ -20,8 +26,98 @@ class DoublyLinkedList {
2026
Node<T>* tail;
2127
int _size;
2228

29+
void link_new_back_node(std::unique_ptr<Node<T>> new_node) {
30+
if (!head) {
31+
head = std::move(new_node);
32+
tail = head.get();
33+
} else {
34+
tail->next = std::move(new_node);
35+
tail->next->prev = tail;
36+
tail = tail->next.get();
37+
}
38+
++_size;
39+
}
40+
41+
template<typename U>
42+
void push_back_internal(U&& value) {
43+
auto new_node = std::make_unique<Node<T>>(std::forward<U>(value), tail, nullptr);
44+
link_new_back_node(std::move(new_node));
45+
}
46+
47+
template<typename U>
48+
void push_front_internal(U&& value) {
49+
auto new_node = std::make_unique<Node<T>>(std::forward<U>(value), nullptr, std::move(head));
50+
if (new_node->next) {
51+
new_node->next->prev = new_node.get();
52+
}
53+
else {
54+
tail = new_node.get();
55+
}
56+
head = std::move(new_node);
57+
_size++;
58+
}
59+
60+
T erase_middle_node(Node<T>* node) {
61+
T value = std::move(node->value);
62+
node->next->prev = node->prev;
63+
node->prev->next = std::move(node->next);
64+
_size--;
65+
return value;
66+
}
67+
68+
std::unique_ptr<Node<T>> extract_node_and_link_prev_with_next(Node<T>* node) {
69+
std::unique_ptr<Node<T>> extracted;
70+
71+
if (node->next) {
72+
node->next->prev = node->prev;
73+
} else {
74+
tail = node->prev;
75+
}
76+
77+
if (node->prev) {
78+
extracted = std::move(node->prev->next);
79+
node->prev->next = std::move(node->next);
80+
} else {
81+
extracted = std::move(head);
82+
head = std::move(node->next);
83+
}
84+
85+
return extracted;
86+
}
87+
88+
void emplace_node_before(std::unique_ptr<Node<T>> node, Node<T>* position) {
89+
if (position == head.get()) {
90+
node->next = std::move(head);
91+
if (node->next) node->next->prev = node.get();
92+
head = std::move(node);
93+
}
94+
else if (position == nullptr) {
95+
node->prev = tail;
96+
tail = node.get();
97+
node->prev->next = std::move(node);
98+
}
99+
else {
100+
node->prev = position->prev;
101+
node->next = std::move(position->prev->next);
102+
position->prev = node.get();
103+
node->prev->next = std::move(node);
104+
}
105+
}
106+
23107
public:
24108
DoublyLinkedList() : head(nullptr), tail(nullptr), _size(0) {}
109+
110+
DoublyLinkedList(std::initializer_list<T> init_list) : DoublyLinkedList() {
111+
for (const auto& value : init_list) push_back(value);
112+
}
113+
114+
template <typename ParamIterator>
115+
DoublyLinkedList(ParamIterator begin, ParamIterator end) : DoublyLinkedList() {
116+
for (auto it = begin; it != end; it++) {
117+
push_back(*it);
118+
}
119+
}
120+
25121
~DoublyLinkedList() {
26122
clear();
27123
}
@@ -45,23 +141,22 @@ class DoublyLinkedList {
45141
return tail->value;
46142
}
47143

144+
145+
146+
void push_back(T&& value) {
147+
push_back_internal(std::move(value));
148+
}
149+
48150
void push_back(const T& value) {
49-
if (!head) {
50-
head = std::make_unique<Node<T>>(value, nullptr, nullptr);
51-
tail = head.get();
52-
} else {
53-
tail->next = std::make_unique<Node<T>>(value, tail, nullptr);
54-
tail = tail->next.get();
55-
}
56-
_size++;
151+
push_back_internal(value);
57152
}
58153

59154
T pop_back() {
60155
if (!head) {
61156
throw std::out_of_range("List is empty");
62157
}
63158

64-
T popped_value = tail->value;
159+
T popped_value = std::move(tail->value);
65160

66161
if (tail == head.get()) {
67162
head.reset();
@@ -76,24 +171,20 @@ class DoublyLinkedList {
76171
return popped_value;
77172
}
78173

174+
void push_front(T&& value) {
175+
push_front_internal(std::move(value));
176+
}
177+
79178
void push_front(const T& value) {
80-
if (!head) {
81-
head = std::make_unique<Node<T>>(value, nullptr, nullptr);
82-
tail = head.get();
83-
} else {
84-
auto new_node = std::make_unique<Node<T>>(value, nullptr, std::move(head));
85-
new_node->next->prev = new_node.get();
86-
head = std::move(new_node);
87-
}
88-
_size++;
179+
push_front_internal(value);
89180
}
90181

91182
T pop_front() {
92183
if (!head) {
93184
throw std::out_of_range("List is empty");
94185
}
95186

96-
T popped_value = head->value;
187+
T popped_value = std::move(head->value);
97188
head = std::move(head->next);
98189

99190
if (!head)
@@ -104,7 +195,7 @@ class DoublyLinkedList {
104195
}
105196

106197
void clear() {
107-
while(size() > 0) {
198+
while(head) {
108199
pop_back();
109200
}
110201
}
@@ -115,13 +206,14 @@ class DoublyLinkedList {
115206

116207
class Iterator {
117208
Node<T>* current_node_ptr;
209+
DoublyLinkedList<T>* list_ptr;
118210

119-
Iterator(Node<T>* a_current) : current_node_ptr(a_current) {}
211+
Iterator(Node<T>* a_current, DoublyLinkedList<T>* a_list_ptr) : current_node_ptr(a_current), list_ptr(a_list_ptr) {}
120212

121213
friend class DoublyLinkedList<T>;
122214

123215
public:
124-
Iterator() : current_node_ptr(nullptr) {}
216+
Iterator() : current_node_ptr(nullptr), list_ptr(nullptr) {}
125217

126218
bool operator==(const Iterator& other) const {
127219
return current_node_ptr == other.current_node_ptr;
@@ -149,22 +241,56 @@ class DoublyLinkedList {
149241
}
150242

151243
Iterator& operator--() {
152-
if (current_node_ptr) {
244+
if (current_node_ptr == nullptr) {
245+
current_node_ptr = list_ptr->tail;
246+
}
247+
else if (current_node_ptr) {
153248
current_node_ptr = current_node_ptr->prev;
154249
}
155250
return *this;
156251
}
157252
};
158253

159254
Iterator begin() {
160-
return Iterator(head.get());
255+
return Iterator(head.get(), this);
161256
}
162257

163258
Iterator end() {
164-
return Iterator(nullptr);
259+
return Iterator(nullptr, this);
165260
}
166261

167262
Iterator back_iterator() {
168-
return Iterator(tail);
263+
return Iterator(tail, this);
264+
}
265+
266+
267+
Iterator erase(Iterator it) {
268+
if (empty()) throw std::out_of_range("List is empty");
269+
if (it == end()) throw std::out_of_range("Invalid iterator");
270+
271+
Iterator target_it = it++;
272+
273+
target_it == begin() ? pop_front() :
274+
target_it == back_iterator() ? pop_back() :
275+
erase_middle_node(target_it.current_node_ptr);
276+
277+
return it;
278+
}
279+
280+
void move_to_begin(Iterator it) {
281+
if (empty()) throw std::out_of_range("List is empty");
282+
if (it == end()) throw std::out_of_range("Invalid iterator");
283+
if (it == begin()) return;
284+
285+
auto extracted = extract_node_and_link_prev_with_next(it.current_node_ptr);
286+
emplace_node_before(std::move(extracted), head.get());
169287
}
288+
289+
template <typename... Args>
290+
void emplace_back(Args&&... args) {
291+
auto new_node = std::make_unique<Node<T>>(tail, nullptr, std::forward<Args>(args)...);
292+
link_new_back_node(std::move(new_node));
293+
}
294+
295+
170296
};

include/ordered_map.hpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,21 @@ class OrderedMap {
3333

3434
void insert(const KeyType& key, const ValueType& value) {
3535
if (_map.find(key) == _map.end()) {
36-
_list.push_back(std::make_pair(key, value));
36+
_list.emplace_back(key, value);
3737
_map[key] = _list.back_iterator();
3838
}
3939
else {
40-
*_map.find(key)->second = std::make_pair(key, value);
40+
(*_map[key]).second = value;
41+
}
42+
}
43+
44+
void insert(const KeyType& key, ValueType&& value) {
45+
if (_map.find(key) == _map.end()) {
46+
_list.emplace_back(key, std::move(value));
47+
_map[key] = _list.back_iterator();
48+
}
49+
else {
50+
(*_map[key]).second = std::move(value);
4151
}
4252
}
4353

@@ -113,6 +123,15 @@ class OrderedMap {
113123
}
114124
return Iterator(_list.back_iterator());
115125
}
116-
117126

127+
Iterator find(const KeyType& key) {
128+
return _map.find(key) == _map.end() ? end() : Iterator(_map[key]);
129+
}
130+
131+
void move_to_front(const KeyType& key) {
132+
if (_map.find(key) == _map.end()) {
133+
throw std::out_of_range("Key not found in ordered map");
134+
}
135+
_list.move_to_begin(_map[key]);
136+
}
118137
};

0 commit comments

Comments
 (0)