DSA - Data Structures and Algorithms
Loading...
Searching...
No Matches
list.h
Go to the documentation of this file.
1
11
12#ifndef LIST_H
13#define LIST_H
14
15#include <cstddef>
16#include <cstdint>
17#include <initializer_list>
18#include <iostream>
19#include <iterator>
20#include <limits>
21#include <memory>
22#include <ranges>
23
24namespace dsa
25{
26
27 template<typename T>
28 class List;
29
30 template<typename T>
31 auto operator+(const List<T>& list1, const List<T>& list2) -> List<T>;
32
55 template<typename T>
56 class List
57 {
58 public:
59
64 {
65 public:
66
70 NodeBase() = default;
71
77 NodeBase(const NodeBase& other) = default;
78
85 auto operator=(const NodeBase& other) -> NodeBase & = default;
86
93 NodeBase(NodeBase&& other) = default;
94
102 auto operator=(NodeBase&& other) -> NodeBase & = default;
103
107 virtual ~NodeBase() = default;
108
109 private:
110
111 friend class List<T>;
112
116 std::unique_ptr<NodeBase> m_next{};
117
121 NodeBase* m_prev{};
122 };
123
127 class Node : public NodeBase
128 {
129 public:
130
137 : m_value{ value }
138 {
139 }
140
146 auto value() -> T&
147 {
148 return m_value;
149 }
150
156 [[nodiscard]] auto value() const -> T
157 {
158 return m_value;
159 }
160
161 private:
162
166 friend class List;
167
168 T m_value{};
169 };
170
181 template<bool IF_CONST>
183 {
184 public:
185
192 using iterator_type = std::conditional_t<IF_CONST, const T, T>;
193
200 using iterator_category = std::bidirectional_iterator_tag;
201
207 using difference_type = std::ptrdiff_t;
208
214 using value_type = T;
215
222
227
231 ListIterator() noexcept = default;
232
238 ListIterator(NodeBase* node) noexcept
239 : m_current_node{ node }
240 {
241 }
242
250 {
251 this->m_current_node = node;
252 return *this;
253 }
254
261 {
262 if (m_current_node)
263 {
264 m_current_node = m_current_node->m_next.get();
265 }
266
267 return *this;
268 }
269
276 {
277 const ListIterator iterator = *this;
278 ++(*this);
279 return iterator;
280 }
281
288 {
289 if (m_current_node)
290 {
291 m_current_node = m_current_node->m_prev;
292 }
293
294 return *this;
295 }
296
303 {
304 const ListIterator iterator = *this;
305 --(*this);
306 return iterator;
307 }
308
317 auto operator==(const ListIterator& other) const -> bool
318 {
319 return m_current_node == other.m_current_node;
320 }
321
330 auto operator!=(const ListIterator& other) const -> bool
331 {
332 return !operator==(other);
333 }
334
343 auto operator[](size_t index) -> ListIterator
344 {
345 NodeBase* temp{ m_current_node };
346
347 for (size_t i = 0; i < index; i++)
348 {
349 if (temp->m_next)
350 {
351 temp = temp->m_next.get();
352 }
353 else
354 {
355 return nullptr;
356 }
357 }
358
359 return temp;
360 }
361
367 auto operator*() const -> reference
368 {
369 if (Node* node = dynamic_cast<Node*>(m_current_node))
370 {
371 return node->value();
372 }
373 throw std::runtime_error("Invalid iterator dereference");
374 }
375
382 {
383 if (Node* node = dynamic_cast<Node*>(m_current_node))
384 {
385 return &node->value();
386 }
387 throw std::runtime_error("Invalid iterator pointer");
388 }
389
394 {
395 return ListIterator<true>(m_current_node);
396 }
397
398 private:
399
403 friend class List;
404
405 NodeBase* m_current_node{};
406 };
407
413 using value_type = T;
414
420 using pointer = T*;
421
427 using const_pointer = const T*;
428
434 using reference = T&;
435
441 using const_reference = const T&;
442
447
452
456 List();
457
464 List(size_t count);
465
473 List(size_t count, const T& value);
474
480 List(const std::initializer_list<T>& init_list);
481
487 List(const List<T>& other);
488
495 auto operator=(const List<T>& other) -> List&;
496
503 List(List<T>&& other) noexcept;
504
512 auto operator=(List<T>&& other) noexcept -> List&;
513
517 ~List();
518
525 void assign(size_t count, const_reference value);
526
532 void assign(const std::initializer_list<T>& init_list);
533
539 auto front() -> reference;
540
546 [[nodiscard]] auto front() const -> const_reference;
547
553 auto back() -> reference;
554
560 [[nodiscard]] auto back() const -> const_reference;
561
567 auto begin() -> iterator;
568
574 auto begin() const -> const_iterator;
575
581 [[nodiscard]] auto cbegin() const -> const_iterator;
582
588 auto end() -> iterator;
589
595 auto end() const -> const_iterator;
596
602 [[nodiscard]] auto cend() const -> const_iterator;
603
610 [[nodiscard]] auto empty() const -> bool;
611
617 [[nodiscard]] auto size() const -> size_t;
618
624 [[nodiscard]] auto max_size() const -> size_t;
625
629 void clear();
630
640 auto insert(const const_iterator& pos, const_reference value) -> iterator;
641
652 auto insert(const const_iterator& pos, size_t count, const_reference value) -> iterator;
653
663 auto insert(const const_iterator& pos, std::initializer_list<T> init_list) -> iterator;
664
674 auto erase(iterator pos) -> iterator;
675
686
697 auto erase(iterator first, iterator last) -> iterator;
698
710
716 void push_back(T value);
717
721 void pop_back();
722
728 void push_front(T value);
729
733 void pop_front();
734
740 void resize(size_t count);
741
748 void resize(size_t count, const_reference value);
749
755 void swap(List<T>& other) noexcept;
756
763 void merge(List<T>& other);
764
771 // transfers ownership of nodes, moving entire container is not necessary
772 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
773 void merge(List<T>&& other);
774
782 void splice(const const_iterator& pos, List<T>& other);
783
791 void splice(const_iterator pos, List<T>&& other);
792
801 void splice(const const_iterator& pos, List<T>& other, const const_iterator& iter);
802
811 void splice(const_iterator pos, List<T>&& other, const_iterator iter);
812
822 void splice(const const_iterator& pos, List<T>& other, const const_iterator& first, const const_iterator& last);
823
833 void splice(const_iterator pos, List<T>&& other, const_iterator first, const_iterator last);
834
840 void remove(const_reference value);
841
845 void reverse();
846
851 void unique();
852
860 auto operator+=(const List<T>& other) -> List<T>&
861 {
862 for (auto it = other.cbegin(); it != other.cend(); ++it)
863 {
864 push_back(*it);
865 }
866
867 return *this;
868 }
869
876 auto operator+=(const std::initializer_list<T> init_list) -> List<T>&
877 {
878 for (const auto& item : init_list)
879 {
880 push_back(item);
881 }
882
883 return *this;
884 }
885
894 [[nodiscard]] auto get(size_t index) const -> Node*;
895
905 auto set(size_t index, T value) -> bool;
906
907 private:
908
919 friend auto operator+<>(const List<T>& list1, const List<T>& list2)->List<T>;
920
925 void init_node()
926 {
927 if (m_head == nullptr)
928 {
929 m_head = std::make_unique<NodeBase>();
930 m_tail = m_head.get();
931 }
932 }
933
940 auto erase_element(iterator pos) -> iterator
941 {
942
943 if (pos == begin())
944 {
945 pop_front();
946 return iterator(begin());
947 }
948
949 if (pos == (--end()))
950 {
951 pop_back();
952 return iterator(end());
953 }
954
955 NodeBase* temp{ pos.m_current_node->m_prev };
956 NodeBase* to_remove{ temp->m_next.get() };
957
958 temp->m_next = std::move(to_remove->m_next);
959 temp->m_next->m_prev = temp;
960
961 m_size--;
962 return iterator(temp->m_next.get());
963 }
964
974 auto insert_element_before(iterator pos, const_reference value) -> iterator
975 {
976 if (pos == begin())
977 {
978 push_front(value);
979 return begin();
980 }
981
982 if (pos == end())
983 {
984 push_back(value);
985 return end().m_current_node->m_prev;
986 }
987
988 NodeBase* temp{ pos.m_current_node->m_prev };
989
990 auto newNode = std::make_unique<Node>(value);
991 newNode->m_next = std::move(temp->m_next);
992 newNode->m_prev = temp;
993
994 temp->m_next = std::move(newNode);
995 temp->m_next->m_next->m_prev = temp->m_next.get();
996
997 m_size++;
998 return iterator(temp->m_next.get());
999 }
1000
1009 auto if_valid_iterator(const_iterator pos) -> bool
1010 {
1011 bool valid_iterator{};
1012
1013 if (pos == cbegin() || pos == cend())
1014 {
1015 return true;
1016 }
1017
1018 for (auto it = cbegin(); it != cend(); ++it)
1019 {
1020 if (it == pos)
1021 {
1022 valid_iterator = true;
1023 break;
1024 }
1025 }
1026 return valid_iterator;
1027 }
1028
1037 auto distance(const_iterator first, const const_iterator& last) -> size_t;
1038
1048 // transfers ownership of nodes, moving entire container is not necessary
1049 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
1050 void transfer(const_iterator pos, List<T>&& other, const_iterator first, const const_iterator& last);
1051
1052 std::unique_ptr<NodeBase> m_head{};
1053 NodeBase* m_tail{};
1054 size_t m_size{};
1055 };
1056
1057 template<typename T>
1059 {
1060 init_node();
1061 }
1062
1063 template<typename T>
1064 List<T>::List(size_t count)
1065 : List(count, T{})
1066 {
1067 }
1068
1069 template<typename T>
1070 List<T>::List(size_t count, const T& value)
1071 {
1072 init_node();
1073
1074 for (size_t i = 0; i < count; i++)
1075 {
1076 push_front(value);
1077 }
1078 }
1079
1080 template<typename T>
1081 List<T>::List(const std::initializer_list<T>& init_list)
1082 {
1083 for (const auto& item : init_list)
1084 {
1085 push_back(item);
1086 }
1087 }
1088
1089 template<typename T>
1091 {
1092 for (size_t i = 0; i < other.size(); i++)
1093 {
1094 push_back(other.get(i)->value());
1095 }
1096 }
1097
1098 template<typename T>
1099 auto List<T>::operator=(const List<T>& other) -> List<T>&
1100 {
1101 if (&other != this)
1102 {
1103 while (m_head->m_next)
1104 {
1105 pop_front();
1106 }
1107
1108 for (size_t i = 0; i < other.size(); i++)
1109 {
1110 push_back(other.get(i)->value());
1111 }
1112 }
1113
1114 return *this;
1115 }
1116
1117 template<typename T>
1118 List<T>::List(List<T>&& other) noexcept
1119 {
1120 operator=(std::move(other));
1121 }
1122
1123 template<typename T>
1124 auto List<T>::operator=(List<T>&& other) noexcept -> List<T>&
1125 {
1126 if (&other != this)
1127 {
1128 clear();
1129
1130 m_head = std::move(other.m_head);
1131 m_tail = std::move(other.m_tail);
1132 m_size = other.m_size;
1133
1134 other.m_head = nullptr;
1135 other.m_tail = nullptr;
1136 other.m_size = 0;
1137 }
1138
1139 return *this;
1140 }
1141
1142 template<typename T>
1144 {
1145 clear();
1146 }
1147
1148 template<typename T>
1149 void List<T>::assign(size_t count, const_reference value)
1150 {
1151 clear();
1152
1153 for (size_t i = 0; i < count; i++)
1154 {
1155 push_back(value);
1156 }
1157 }
1158
1159 template<typename T>
1160 void List<T>::assign(const std::initializer_list<T>& init_list)
1161 {
1162 clear();
1163
1164 for (const auto& item : init_list)
1165 {
1166 push_back(item);
1167 }
1168 }
1169
1170 template<typename T>
1172 {
1173 return *begin();
1174 }
1175
1176 template<typename T>
1177 auto List<T>::front() const -> typename List<T>::const_reference
1178 {
1179 return *cbegin();
1180 }
1181
1182 template<typename T>
1184 {
1185 return *(--end());
1186 }
1187
1188 template<typename T>
1189 auto List<T>::back() const -> typename List<T>::const_reference
1190 {
1191 return *(--cend());
1192 }
1193
1194 template<typename T>
1196 {
1197 return iterator(m_head.get());
1198 }
1199
1200 template<typename T>
1201 auto List<T>::begin() const -> typename List<T>::const_iterator
1202 {
1203 return const_iterator(m_head.get());
1204 }
1205
1206 template<typename T>
1207 auto List<T>::cbegin() const -> typename List<T>::const_iterator
1208 {
1209 return begin();
1210 }
1211
1212 template<typename T>
1214 {
1215 return iterator(m_tail);
1216 }
1217
1218 template<typename T>
1219 auto List<T>::end() const -> typename List<T>::const_iterator
1220 {
1221 return const_iterator(m_tail);
1222 }
1223
1224 template<typename T>
1225 auto List<T>::cend() const -> typename List<T>::const_iterator
1226 {
1227 return end();
1228 }
1229
1230 template<typename T>
1231 auto List<T>::empty() const -> bool
1232 {
1233 return m_size == 0;
1234 }
1235
1236 template<typename T>
1237 auto List<T>::size() const -> size_t
1238 {
1239 return m_size;
1240 }
1241
1242 template<typename T>
1243 auto List<T>::max_size() const -> size_t
1244 {
1245 return std::numeric_limits<size_t>::max();
1246 }
1247
1248 template<typename T>
1250 {
1251 if (m_head && m_head->m_next)
1252 {
1253 while (m_head->m_next)
1254 {
1255 m_head = std::move(m_head->m_next);
1256 }
1257
1258 m_size = 0;
1259 m_tail->m_prev = nullptr;
1260 }
1261 }
1262
1263 template<typename T>
1265 {
1266 return insert(pos, 1, value);
1267 }
1268
1269 template<typename T>
1270 auto List<T>::insert(const const_iterator& pos, size_t count, const_reference value) -> typename List<T>::iterator
1271 {
1272 iterator iter{ pos.m_current_node };
1273
1274 if (!if_valid_iterator(pos))
1275 {
1276 return nullptr;
1277 }
1278
1279 for (size_t i = 0; i < count; i++)
1280 {
1281 iter = insert_element_before(iter, value);
1282 }
1283
1284 return iter;
1285 }
1286
1287 template<typename T>
1288 auto List<T>::insert(const const_iterator& pos, std::initializer_list<T> init_list) -> typename List<T>::iterator
1289 {
1290 iterator iter(pos.m_current_node);
1291
1292 if (!if_valid_iterator(pos))
1293 {
1294 return nullptr;
1295 }
1296
1297 for (const auto& val : init_list)
1298 {
1299 iter = insert_element_before(iter, val);
1300 ++iter;
1301 }
1302
1303 return iter;
1304 }
1305
1306 template<typename T>
1308 {
1309 if (!if_valid_iterator(pos))
1310 {
1311 return nullptr;
1312 }
1313
1314 return erase_element(pos);
1315 }
1316
1317 template<typename T>
1319 {
1320 if (!if_valid_iterator(first) || !if_valid_iterator(last))
1321 {
1322 return nullptr;
1323 }
1324
1325 const size_t dist = distance(first, last);
1326 if (dist == 0)
1327 {
1328 return last;
1329 }
1330
1331 iterator iter(first.m_current_node);
1332 for (size_t i = 0; i < dist; i++)
1333 {
1334 iter = erase_element(iter);
1335 }
1336
1337 return iter;
1338 }
1339
1340 template<typename T>
1342 {
1343 init_node();
1344
1345 auto newNode = std::make_unique<Node>(value);
1346 if (!m_head->m_next)
1347 {
1348 newNode->m_next = std::move(m_head);
1349 m_head = std::move(newNode);
1350 m_tail->m_prev = m_head.get();
1351 }
1352 else
1353 {
1354 m_head->m_prev = newNode.get();
1355 newNode->m_next = std::move(m_head);
1356 m_head = std::move(newNode);
1357 }
1358
1359 m_size++;
1360 }
1361
1362 template<typename T>
1364 {
1365 if (m_size == 0)
1366 {
1367 return;
1368 }
1369
1370 if (m_size == 1)
1371 {
1372 m_head = std::move(m_head->m_next);
1373 m_tail->m_prev = nullptr;
1374 }
1375 else
1376 {
1377 m_head = std::move(m_head->m_next);
1378 m_head->m_prev = nullptr;
1379 }
1380
1381 m_size--;
1382 }
1383
1384 template<typename T>
1386 {
1387 init_node();
1388
1389 auto newNode = std::make_unique<Node>(value);
1390
1391 if (!m_head->m_next) // only sentinel exists
1392 {
1393 newNode->m_next = std::move(m_head);
1394 m_head = std::move(newNode);
1395 m_tail->m_prev = m_head.get();
1396 }
1397 else
1398 {
1399 newNode->m_prev = m_tail->m_prev;
1400 newNode->m_next = std::move(m_tail->m_prev->m_next);
1401 m_tail->m_prev = newNode.get();
1402 m_tail->m_prev->m_prev->m_next = std::move(newNode);
1403 }
1404
1405 m_size++;
1406 }
1407
1408 template<typename T>
1410 {
1411 if (m_size == 0)
1412 {
1413 return;
1414 }
1415
1416 if (m_size == 1)
1417 {
1418 m_head = std::move(m_head->m_next);
1419 m_tail->m_prev = nullptr;
1420 }
1421 else
1422 {
1423 NodeBase* temp{ m_tail->m_prev->m_prev };
1424 m_tail->m_prev->m_prev->m_next = std::move(m_tail->m_prev->m_next);
1425 m_tail->m_prev = temp;
1426 }
1427
1428 m_size--;
1429 }
1430
1431 template<typename T>
1432 void List<T>::resize(size_t count)
1433 {
1434 resize(count, T{});
1435 }
1436
1437 template<typename T>
1438 void List<T>::resize(size_t count, const_reference value)
1439 {
1440 if (count == m_size)
1441 {
1442 return;
1443 }
1444
1445 if (m_size > count)
1446 {
1447 // container is reduced to its count elements
1448 while (m_size > count)
1449 {
1450 pop_back();
1451 }
1452 }
1453
1454 if (m_size < count)
1455 {
1456 // additional copies of value are appended
1457 while (m_size < count)
1458 {
1459 push_back(value);
1460 }
1461 }
1462 }
1463
1464 template<typename T>
1465 void List<T>::swap(List<T>& other) noexcept
1466 {
1467 std::swap(m_head, other.m_head);
1468 std::swap(m_tail, other.m_tail);
1469 std::swap(m_size, other.m_size);
1470 }
1471
1472 template<typename T>
1474 {
1475 merge(std::move(other));
1476 }
1477
1478 template<typename T>
1479 // transfers ownership of nodes, moving entire container is not necessary
1480 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
1482 {
1483 if (&other != this)
1484 {
1485 if (m_size != 0)
1486 {
1487 auto temp_head = std::make_unique<Node>(0);
1488 NodeBase* temp_tail = temp_head.get();
1489
1490 std::unique_ptr<NodeBase> to_move{};
1491 std::unique_ptr<NodeBase> to_return{};
1492
1493 std::unique_ptr<NodeBase> sentinel_this{ std::move(m_tail->m_prev->m_next) };
1494 const std::unique_ptr<NodeBase> sentinel_other{ std::move(other.m_tail->m_prev->m_next) };
1495
1496 while (m_head && other.m_head)
1497 {
1498 Node* node_this = dynamic_cast<Node*>(m_head.get());
1499 Node* node_other = dynamic_cast<Node*>(other.m_head.get());
1500
1501 if (node_this && node_other)
1502 {
1503 if (node_this->value() <= node_other->value())
1504 {
1505 to_move = std::move(m_head);
1506 to_move->m_prev = temp_tail;
1507
1508 to_return = std::move(to_move->m_next);
1509 temp_tail->m_next = std::move(to_move);
1510 m_head = std::move(to_return);
1511 }
1512 else
1513 {
1514 to_move = std::move(other.m_head);
1515 to_move->m_prev = temp_tail;
1516
1517 to_return = std::move(to_move->m_next);
1518 temp_tail->m_next = std::move(to_move);
1519 other.m_head = std::move(to_return);
1520 }
1521
1522 temp_tail = temp_tail->m_next.get();
1523 }
1524 }
1525
1526 NodeBase* last{};
1527 if (m_head == nullptr) // other.m_head attached at the end
1528 {
1529 last = sentinel_other->m_prev;
1530 other.m_head->m_prev = temp_tail;
1531 temp_tail->m_next = std::move(other.m_head);
1532 }
1533 else // this.m_head attached at the end
1534 {
1535 last = sentinel_this->m_prev;
1536 m_head->m_prev = temp_tail;
1537 temp_tail->m_next = std::move(m_head);
1538 }
1539 last->m_next = std::move(sentinel_this);
1540 last->m_next->m_prev = last;
1541
1542 m_head = std::move(temp_head->m_next);
1543 m_head->m_prev = nullptr;
1544
1545 m_size += other.m_size;
1546 other.m_size = 0;
1547 }
1548 else
1549 {
1550 swap(other);
1551 }
1552 }
1553 }
1554
1555 template<typename T>
1556 auto List<T>::distance(const_iterator first, const const_iterator& last) -> size_t
1557 {
1558 size_t dist{};
1559 while (first != last)
1560 {
1561 ++first;
1562 ++dist;
1563 }
1564
1565 return dist;
1566 }
1567
1568 template<typename T>
1569 // transfers ownership of nodes, moving entire container is not necessary
1570 // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
1571 void List<T>::transfer(const_iterator pos, List<T>&& other, const_iterator first, const const_iterator& last)
1572 {
1573 if (&other != this && other.m_size > 0)
1574 {
1575 const size_t dist = distance(first, last);
1576 if (dist == 0)
1577 {
1578 return;
1579 }
1580
1581 NodeBase* temp_prev{ pos.m_current_node->m_prev }; // to append to
1582 NodeBase* last_to_move{ last.m_current_node->m_prev };
1583
1584 // select fragment from other
1585
1586 std::unique_ptr<NodeBase> fragment{};
1587 std::unique_ptr<NodeBase> fragment_end{ std::move(last_to_move->m_next) };
1588 if (first == other.begin())
1589 {
1590 fragment = std::move(other.m_head);
1591 other.m_head = std::move(fragment_end);
1592 other.m_head->m_prev = nullptr;
1593 }
1594 else
1595 {
1596 fragment = std::move(first.m_current_node->m_prev->m_next);
1597 fragment->m_prev->m_next = std::move(fragment_end);
1598 fragment->m_prev->m_next->m_prev = first.m_current_node->m_prev;
1599 fragment->m_prev = nullptr;
1600 }
1601
1602 // insert fragment into this
1603
1604 if (pos == begin())
1605 {
1606 last_to_move->m_next = std::move(m_head);
1607 last_to_move->m_next->m_prev = last_to_move;
1608 m_head = std::move(fragment);
1609 }
1610 else
1611 {
1612 last_to_move->m_next = std::move(temp_prev->m_next);
1613 last_to_move->m_next->m_prev = last_to_move;
1614 temp_prev->m_next = std::move(fragment);
1615 temp_prev->m_next->m_prev = temp_prev;
1616 }
1617
1618 m_size += dist;
1619 other.m_size -= dist;
1620 }
1621 }
1622
1623 template<typename T>
1624 void List<T>::splice(const const_iterator& pos, List<T>& other)
1625 {
1626 transfer(pos, std::move(other), other.begin(), other.end());
1627 }
1628
1629 template<typename T>
1631 {
1632 transfer(pos, std::move(other), other.begin(), other.end());
1633 }
1634
1635 template<typename T>
1636 void List<T>::splice(const const_iterator& pos, List<T>& other, const const_iterator& iter)
1637 {
1638 transfer(pos, std::move(other), iter, iter.m_current_node->m_next.get());
1639 }
1640
1641 template<typename T>
1643 {
1644 transfer(pos, std::move(other), iter, iter.m_current_node->m_next.get());
1645 }
1646
1647 template<typename T>
1648 void List<T>::splice(const const_iterator& pos, List<T>& other,
1649 const const_iterator& first, const const_iterator& last)
1650 {
1651 transfer(pos, std::move(other), first, last);
1652 }
1653
1654 template<typename T>
1656 {
1657 transfer(pos, std::move(other), first, last);
1658 }
1659
1660 template<typename T>
1662 {
1663 NodeBase* temp{ m_head.get() };
1664 NodeBase* next{};
1665
1666 while (temp->m_next.get())
1667 {
1668 next = temp->m_next.get();
1669
1670 if (Node* node = dynamic_cast<Node*>(m_head.get()))
1671 {
1672 if (node->value() == value)
1673 {
1674 pop_front();
1675 temp = m_head.get();
1676 continue;
1677 }
1678 }
1679
1680 if (next && next != m_tail && next->m_next != nullptr)
1681 {
1682 Node* node = dynamic_cast<Node*>(next);
1683 if (node->value() == value)
1684 {
1686 continue;
1687 }
1688 }
1689
1690 temp = temp->m_next.get();
1691 }
1692 }
1693
1694 template<typename T>
1696 {
1697 if (m_head->m_next == nullptr)
1698 {
1699 return;
1700 }
1701
1702 NodeBase* new_back{ m_head.get() };
1703 std::unique_ptr<NodeBase> sentinel = std::move(m_tail->m_prev->m_next);
1704
1705 std::unique_ptr<NodeBase> temp = std::move(m_head);
1706 std::unique_ptr<NodeBase> prev{};
1707
1708 for (size_t i = 0; i < m_size; i++)
1709 {
1710 std::unique_ptr<NodeBase> next = std::move(temp->m_next);
1711 temp->m_next = std::move(prev);
1712 temp->m_prev = next.get();
1713
1714 prev = std::move(temp);
1715 temp = std::move(next);
1716 }
1717
1718 m_head = std::move(prev);
1719 m_tail->m_prev = new_back;
1720 m_tail->m_prev->m_next = std::move(sentinel);
1721 }
1722
1723 template<typename T>
1725 {
1726 NodeBase* temp{ m_head.get() };
1727 NodeBase* prev{};
1728 NodeBase* next{};
1729 while (temp)
1730 {
1731 prev = temp;
1732
1733 while (prev)
1734 {
1735 next = prev->m_next.get();
1736
1737 Node* node_next = dynamic_cast<Node*>(next);
1738 Node* node_temp = dynamic_cast<Node*>(temp);
1739 if (node_next && node_temp)
1740 {
1741 if (node_next->value() == node_temp->value())
1742 {
1743 NodeBase* to_remove = next;
1744
1745 if (to_remove->m_next)
1746 {
1747 to_remove->m_next->m_prev = prev;
1748 }
1749 else // temp was last node
1750 {
1751 m_tail = prev;
1752 }
1753
1754 prev->m_next = std::move(to_remove->m_next);
1755
1756 m_size--;
1757 continue;
1758 }
1759 }
1760
1761 prev = prev->m_next.get();
1762 temp = temp->m_next.get();
1763 }
1764 }
1765 }
1766
1767 template<typename T>
1768 auto List<T>::get(size_t index) const -> typename List<T>::Node*
1769 {
1770 if (index >= m_size)
1771 {
1772 return nullptr;
1773 }
1774
1775 NodeBase* temp{};
1776
1777 // select list end to look for selected index
1778 enum Mode : std::uint8_t { FRONT, BACK, AUTO };
1779 constexpr Mode mode = Mode::AUTO;
1780
1781
1782 if constexpr (mode == FRONT)
1783 {
1784 // count nodes from front
1785 temp = m_head.get();
1786 for (size_t i = 0; i < index; i++)
1787 {
1788 temp = temp->m_next.get();
1789 }
1790 }
1791 else if constexpr (mode == BACK)
1792 {
1793 // count nodes from back
1794 temp = m_tail->m_prev;
1795 for (size_t i = m_size - 1; i > index; i--)
1796 {
1797 temp = temp->m_prev;
1798 }
1799 }
1800 else // mode == AUTO
1801 {
1802 // optimize counting nodes from front or back
1803 if (index < m_size / 2)
1804 {
1805 temp = m_head.get();
1806 for (size_t i = 0; i < index; i++)
1807 {
1808 temp = temp->m_next.get();
1809 }
1810 }
1811 else
1812 {
1813 temp = m_tail->m_prev;
1814 for (size_t i = m_size - 1; i > index; i--)
1815 {
1816 temp = temp->m_prev;
1817 }
1818 }
1819 }
1820
1821 return dynamic_cast<Node*>(temp);
1822 }
1823
1824 template<typename T>
1825 auto List<T>::set(size_t index, T value) -> bool
1826 {
1827 Node* temp = get(index);
1828 if (temp)
1829 {
1830 temp->m_value = value;
1831 return true;
1832 }
1833
1834 return false;
1835 }
1836
1845 template<typename T>
1846 auto operator+(const List<T>& list1, const List<T>& list2) -> List<T>
1847 {
1848 List<T> temp(list1);
1849
1850 for (auto iter = list2.cbegin(); iter != list2.cend(); ++iter)
1851 {
1852 T value = *iter;
1853 temp.push_back(value);
1854 }
1855
1856 return temp;
1857 }
1858
1867 template<typename T>
1868 auto operator<<(std::ostream& out, const List<T>& list) -> std::ostream&
1869 {
1870 if (list.empty())
1871 {
1872 return out;
1873 }
1874
1875 for (auto iter = list.cbegin(); iter != list.cend(); ++iter)
1876 {
1877 T value = *iter;
1878 out << value << ' ';
1879 }
1880
1881 return out;
1882 }
1883
1893 template<typename T>
1894 auto operator==(const List<T>& list1, const List<T>& list2) -> bool
1895 {
1896 if (list1.size() != list2.size())
1897 {
1898 return false;
1899 }
1900
1901 auto list1_iter = list1.cbegin();
1902 auto list2_iter = list2.cbegin();
1903
1904 while (list1_iter != list1.cend())
1905 {
1906 if (*list1_iter != *list2_iter)
1907 {
1908 return false;
1909 }
1910
1911 list1_iter++;
1912 list2_iter++;
1913 }
1914
1915 return true;
1916 }
1917
1927 template<typename T>
1928 auto operator!=(const List<T>& list1, const List<T>& list2) -> bool
1929 {
1930 return !(operator==(list1, list2));
1931 }
1932
1943 template<typename T>
1944 auto operator<(const List<T>& list1, const List<T>& list2) -> bool
1945 {
1946 auto list1_iter = list1.cbegin();
1947 auto list2_iter = list2.cbegin();
1948
1949 while (list1_iter != list1.cend() && list2_iter != list2.cend())
1950 {
1951 if (*list1_iter > *list2_iter)
1952 {
1953 return false;
1954 }
1955 if (*list1_iter < *list2_iter)
1956 {
1957 return true;
1958 }
1959
1960 list1_iter++;
1961 list2_iter++;
1962 }
1963
1964 // first n elements are equal
1965 // check sizes
1966 return list1.size() < list2.size();
1967 }
1968
1979 template<typename T>
1980 auto operator>(const List<T>& list1, const List<T>& list2) -> bool
1981 {
1982 return operator<(list2, list1);
1983 }
1984
1995 template<typename T>
1996 auto operator<=(const List<T>& list1, const List<T>& list2) -> bool
1997 {
1998 return !(operator>(list1, list2));
1999 }
2000
2011 template<typename T>
2012 auto operator>=(const List<T>& list1, const List<T>& list2) -> bool
2013 {
2014 return !(operator<(list1, list2));
2015 }
2016}
2017
2018#endif // !LIST_H
constexpr auto operator==(const Array< T, N > &lhs, const Array< T, N > &rhs) noexcept(noexcept(*lhs.begin()== *rhs.begin())) -> bool
The relational operator compares two Array objects.
Definition array.h:591
auto operator<<(std::ostream &out, const Array< T, N > &array) -> std::ostream &
Overloads operator to print all elements of Array.
Definition array.h:572
Implements ListIterator.
Definition list.h:183
auto operator*() const -> reference
Overload operator* to dereference Node value / data.
Definition list.h:367
auto operator->() -> pointer
Overload operator-> to get pointer to Node value / data.
Definition list.h:381
auto operator++() -> ListIterator &
Overload pre-increment operator++ to point ListIterator at next Node.
Definition list.h:260
auto operator--() -> ListIterator &
Overload pre-decrement operator-- to point ListIterator at previous Node.
Definition list.h:287
auto operator[](size_t index) -> ListIterator
Overload operator[] for ListIterator iterator access.
Definition list.h:343
std::conditional_t< IF_CONST, const T, T > iterator_type
Alias for iterator type.
Definition list.h:192
ListIterator() noexcept=default
Construct a new ListIterator object.
auto operator--(int) -> ListIterator
Overload post-decrement operator-- to point ListIterator at previous Node.
Definition list.h:302
T value_type
Alias for data type used by iterator.
Definition list.h:214
iterator_type * pointer
Alias for pointer to data type used by iterator.
Definition list.h:221
auto operator++(int) -> ListIterator
Overload post-increment operator++ to point ListIterator at next Node.
Definition list.h:275
std::ptrdiff_t difference_type
Alias for pointer difference type.
Definition list.h:207
iterator_type & reference
Alias for reference to type used by iterator.
Definition list.h:226
auto operator!=(const ListIterator &other) const -> bool
Overload operator!= for ListIterator objects comparison.
Definition list.h:330
std::bidirectional_iterator_tag iterator_category
Alias for bidirectional iterator tag, define iterator direction.
Definition list.h:200
auto operator==(const ListIterator &other) const -> bool
Overload operator!= for ListIterator objects comparison.
Definition list.h:317
auto operator=(NodeBase *node) -> ListIterator &
Overload operator= to assign node to currently pointed ListIterator.
Definition list.h:249
Struct implements base pointer used by List.
Definition list.h:64
NodeBase()=default
Construct a new NodeBase object.
auto operator=(NodeBase &&other) -> NodeBase &=default
Construct NodeBase object using move assignment.
NodeBase(NodeBase &&other)=default
Construct NodeBase object using move constructor.
auto operator=(const NodeBase &other) -> NodeBase &=default
Construct a new NodeBase object using copy assignment.
virtual ~NodeBase()=default
Destroy the Node Base object.
NodeBase(const NodeBase &other)=default
Construct a new NodeBase object using copy constructor.
Implements Node template class with pointer to adjacent elements.
Definition list.h:128
Node(T value)
Construct a new Node object with initial value.
Definition list.h:136
auto value() const -> T
Function returns value stored in Node object.
Definition list.h:156
friend class List
Forward friend declaration of List.
Definition list.h:166
auto value() -> T &
Function returns value stored in Node object.
Definition list.h:146
Implements List using Node with pointer to adjacent elements as internal base.
Definition list.h:57
void unique()
Function removes consecutive duplicated elements.
Definition list.h:1724
auto cend() const -> const_iterator
Function returns pointer to List last Node.
Definition list.h:1225
~List()
Destroy the List object.
Definition list.h:1143
auto front() -> reference
Function returns reference to value stored in List first Node.
Definition list.h:1171
auto size() const -> size_t
Function returns List size.
Definition list.h:1237
T value_type
Alias for data type used in class.
Definition list.h:413
auto erase(iterator pos) -> iterator
Function erases Node object at specified pos.
Definition list.h:1307
void swap(List< T > &other) noexcept
Function swaps content of two List objects.
Definition list.h:1465
void remove(const_reference value)
Function removes all elements equal to value.
Definition list.h:1661
auto end() -> iterator
Function returns pointer to List last Node.
Definition list.h:1213
T * pointer
Alias for pointer to data type used in class.
Definition list.h:420
const T * const_pointer
Alias for const pointer to data type used in class.
Definition list.h:427
auto max_size() const -> size_t
Function returns maximum number of elements container can hold.
Definition list.h:1243
auto operator+=(const std::initializer_list< T > init_list) -> List< T > &
push_back elements of another List to base container
Definition list.h:876
auto back() -> reference
Function returns reference to value stored in List last Node.
Definition list.h:1183
void clear()
Function removes all elements of List.
Definition list.h:1249
auto insert(const const_iterator &pos, const_reference value) -> iterator
Function inserts new Node before specified pos.
Definition list.h:1264
List()
Construct a new List object.
Definition list.h:1058
ListIterator< true > const_iterator
Alias for const iterator to data type used in class.
Definition list.h:446
T & reference
Alias for reference to data type used in class.
Definition list.h:434
auto get(size_t index) const -> Node *
Function returns pointer to specific Node of List.
Definition list.h:1768
void push_front(T value)
Function adds new Node at the beginning of List.
Definition list.h:1341
void assign(size_t count, const_reference value)
Function assign values to the List.
Definition list.h:1149
void splice(const const_iterator &pos, List< T > &other)
Function moves elements from other List object.
Definition list.h:1624
const T & const_reference
Alias for const reference to data type used in class.
Definition list.h:441
auto set(size_t index, T value) -> bool
Function sets value of specifed Node of List.
Definition list.h:1825
friend auto operator+(const List< T > &list1, const List< T > &list2) -> List< T >
Construct new object based on two List objects.
Definition list.h:1846
void merge(List< T > &other)
Function combines two sorted Lists into one sorted List.
Definition list.h:1473
void push_back(T value)
Function adds new Node at the end of List.
Definition list.h:1385
void pop_back()
Function removes last Node of List.
Definition list.h:1409
auto operator=(const List< T > &other) -> List &
Constructs List using copy assignment.
Definition list.h:1099
void reverse()
Function reverts in place Nodes of List.
Definition list.h:1695
auto cbegin() const -> const_iterator
Function returns const pointer to List first Node.
Definition list.h:1207
void pop_front()
Function removes first Node of List.
Definition list.h:1363
ListIterator< false > iterator
Alias for iterator to data type used in class.
Definition list.h:451
auto begin() -> iterator
Function returns pointer to List first Node.
Definition list.h:1195
auto empty() const -> bool
Function checks if container has no elements.
Definition list.h:1231
void resize(size_t count)
Function resize List to specified number of elements.
Definition list.h:1432
auto operator>(const List< T > &list1, const List< T > &list2) -> bool
The relational operator compares two List objects.
Definition list.h:1980
auto operator>=(const List< T > &list1, const List< T > &list2) -> bool
The relational operator compares two List objects.
Definition list.h:2012
auto operator<=(const List< T > &list1, const List< T > &list2) -> bool
The relational operator compares two List objects.
Definition list.h:1996
auto operator+(const List< T > &list1, const List< T > &list2) -> List< T >
Construct new object based on two List objects.
Definition list.h:1846
auto operator!=(const List< T > &list1, const List< T > &list2) -> bool
The relational operator compares two List objects.
Definition list.h:1928
auto operator<(const List< T > &list1, const List< T > &list2) -> bool
The relational operator compares two List objects.
Definition list.h:1944