-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[REVIEW] Добавяне на reverse и sort(mergesort) функции в списъците #11
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
#ifndef DOUBLE_LINKED_LIST_HPP | ||
#define DOUBLE_LINKED_LIST_HPP | ||
|
||
#include <utility> | ||
#include <stdexcept> | ||
|
||
|
@@ -100,6 +101,71 @@ class DoubleLinkedList { | |
deleteFirst(front->data); | ||
} | ||
|
||
void split(I& list, I& left, I& right) { | ||
if(!list.valid()) { | ||
left = right = I(); | ||
return; | ||
} | ||
|
||
if(!list.next().valid()) { | ||
left = list; | ||
right = I(); | ||
return; | ||
} | ||
|
||
I slow = list; | ||
I fast = list; | ||
|
||
while(fast.valid() && fast.next().valid()) { | ||
fast = fast.next().next(); | ||
if(fast.valid()) | ||
slow = slow.next(); | ||
} | ||
|
||
left = list; | ||
right = slow.next(); | ||
slow.ptr->next->prev = nullptr; | ||
slow.ptr->next = nullptr; | ||
} | ||
|
||
I merge(I left, I right) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Тази функция може да е публична, да приема два списъка с препратка, след което да им „краде“ веригите като ги слива. Ако резултатът е текущият списък, първо трябва да се изтрие текущото му съдържание,ако има такова. Ако резултатът е нов списък, тогава функцията би следвало да е статична. |
||
if(!left.valid()) | ||
return right; | ||
|
||
if(!right.valid()) | ||
return left; | ||
|
||
if(left.get() <= right.get()) { | ||
I recResult = merge(left.next(), right); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Рекурсията е излишна. Идеята на функцията е да стане с константна допълнителна памет, а така става с линейна допълнителна памет заради стековите рамки. |
||
left.ptr->next = recResult.ptr; | ||
recResult.ptr->prev = left.ptr; | ||
return left; | ||
} else { | ||
I recResult = merge(left, right.next()); | ||
right.ptr->next = recResult.ptr; | ||
recResult.ptr->prev = right.ptr; | ||
return right; | ||
} | ||
} | ||
|
||
I mergeSort(I list) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Използва се като статична функция, макар че не е. Ако следваме идеята горните функции също да не са статични, то тогава би трябвало:
|
||
if(!list.valid()) { | ||
return I(); | ||
} | ||
|
||
if(!list.next().valid()) { | ||
return list; | ||
} | ||
|
||
I left = I(), right = I(); | ||
|
||
split(list, left, right); | ||
left = mergeSort(left); | ||
right = mergeSort(right); | ||
|
||
return merge(left, right); | ||
} | ||
|
||
public: | ||
using Iterator = I; | ||
|
||
|
@@ -266,6 +332,40 @@ class DoubleLinkedList { | |
// it1.prev() = it2 -- добър случай, четна дължина | ||
return *it1 == *it2; | ||
} | ||
|
||
// Обръща списъка in place | ||
// O(n) по време, О(1) по памет | ||
void reverse() { | ||
if (empty() || front == back) return; | ||
|
||
I prevEl = nullptr; | ||
I currEl = front; | ||
I nextEl = nullptr; | ||
|
||
back = front; | ||
|
||
while (currEl != nullptr) { | ||
nextEl = currEl.next(); | ||
|
||
currEl.ptr->next = prevEl.ptr; | ||
currEl.ptr->prev = nextEl.ptr; | ||
|
||
prevEl = currEl; | ||
currEl = nextEl; | ||
} | ||
|
||
front = prevEl.ptr; | ||
} | ||
|
||
void sort() { | ||
front = mergeSort(begin()).ptr; | ||
|
||
I it = begin(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Частта със задаването на |
||
while(it && it.next().valid()) | ||
it = it.next(); | ||
|
||
back = it.ptr; | ||
} | ||
}; | ||
|
||
#endif // DOUBLE_LINKED_LIST_HPP |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
#ifndef LINKED_LIST_HPP | ||
#define LINKED_LIST_HPP | ||
|
||
#include <utility> | ||
#include <stdexcept> | ||
|
||
|
@@ -94,6 +95,62 @@ class LinkedList { | |
deleteFirst(front->data); | ||
} | ||
|
||
void split(I list, I& left, I& right) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Виж коментарите за двойносвързания списък. |
||
if(!list.valid()) { | ||
left = right = I(); | ||
return; | ||
} | ||
|
||
if(!list.next().valid()) { | ||
left = list; | ||
right = I(); | ||
return; | ||
} | ||
|
||
I slow = list; | ||
I fast = list; | ||
|
||
while(fast.valid() && fast.next().valid()) { | ||
fast = fast.next().next(); | ||
if(fast.valid()) | ||
slow = slow.next(); | ||
} | ||
|
||
left = list; | ||
right = slow.next(); | ||
slow.ptr->next = nullptr; | ||
} | ||
|
||
I merge(I left, I right) { | ||
if(!left.valid()) | ||
return right; | ||
|
||
if(!right.valid()) | ||
return left; | ||
|
||
if(left.get() <= right.get()) { | ||
left.ptr->next = merge(left.next(), right).ptr; | ||
return left; | ||
} else { | ||
right.ptr->next = merge(left, right.next()).ptr; | ||
return right; | ||
} | ||
} | ||
|
||
I mergeSort(I list) { | ||
if(!list.valid() || !list.next().valid()) { | ||
return list; | ||
} | ||
|
||
I left = I(), right = I(); | ||
|
||
split(list, left, right); | ||
left = mergeSort(left); | ||
right = mergeSort(right); | ||
|
||
return merge(left, right); | ||
} | ||
|
||
public: | ||
using Iterator = I; | ||
|
||
|
@@ -240,6 +297,38 @@ class LinkedList { | |
} | ||
} | ||
|
||
// Обръща списъка in place | ||
// O(n) по време, О(1) по памет | ||
void reverse() { | ||
if (empty() || front == back) return; | ||
|
||
I prev = nullptr; | ||
I curr = front; | ||
I next = nullptr; | ||
|
||
back = front; | ||
|
||
while (curr != nullptr) { | ||
next = curr.next(); | ||
|
||
curr.ptr->next = prev.ptr; | ||
|
||
prev = curr; | ||
curr = next; | ||
} | ||
|
||
front = prev.ptr; | ||
} | ||
|
||
void sort() { | ||
front = mergeSort(begin()).ptr; | ||
|
||
I it = begin(); | ||
while(it && it.next().valid()) | ||
it = it.next(); | ||
|
||
back = it.ptr; | ||
} | ||
}; | ||
|
||
#endif // LINKED_LIST_HPP |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -376,3 +376,71 @@ TEST_CASE("Списък с 2 елемента не е палиндром, ако | |
CHECK(list.insertLast(2)); | ||
CHECK(!list.isPalindrome()); | ||
} | ||
|
||
|
||
TEST_CASE_TEMPLATE("Обръщане на празен списък без заделяне на нова памет", SomeList, LISTS) { | ||
SomeList list; | ||
list.reverse(); | ||
CHECK(list.empty()); | ||
} | ||
|
||
TEST_CASE_TEMPLATE("Обръщане на списък с 1 елемент без заделяне на нова памет", SomeList, LISTS) { | ||
const unsigned val = 42; | ||
SomeList list; | ||
|
||
list.insertFirst(val); | ||
list.reverse(); | ||
|
||
CHECK(!list.empty()); | ||
CHECK_EQ(list.begin().get(), val); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Да се провери, че списъкът е само от един елемент. |
||
} | ||
|
||
TEST_CASE_TEMPLATE("Обръщане на списък с числата от 1 до 10 без заделяне на нова памет", SomeList, LISTS) { | ||
SomeList list; | ||
for (int i = 1; i <= 10; i++) | ||
CHECK(list.insertLast(i)); | ||
|
||
list.reverse(); | ||
|
||
int i = 10; | ||
for (int j : list) | ||
CHECK(j == i--); | ||
CHECK(i == 0); | ||
} | ||
|
||
TEST_CASE_TEMPLATE("Сортиране на непразен списък с числата от 1 до 10 в разбъркан ред", SomeList, LISTS) { | ||
SomeList list; | ||
CHECK(list.insertLast(5)); | ||
CHECK(list.insertLast(3)); | ||
CHECK(list.insertLast(7)); | ||
CHECK(list.insertLast(1)); | ||
CHECK(list.insertLast(10)); | ||
CHECK(list.insertLast(2)); | ||
CHECK(list.insertLast(9)); | ||
CHECK(list.insertLast(4)); | ||
CHECK(list.insertLast(6)); | ||
CHECK(list.insertLast(8)); | ||
|
||
list.sort(); | ||
|
||
int i = 1; | ||
for (int j : list) | ||
CHECK_EQ(i++, j); | ||
CHECK_EQ(i, 11); | ||
} | ||
|
||
TEST_CASE_TEMPLATE("Сортиране на празен списък", SomeList, LISTS) { | ||
SomeList list; | ||
list.sort(); | ||
CHECK(list.empty()); | ||
} | ||
|
||
TEST_CASE_TEMPLATE("Сортиране на списък с 1 елемент", SomeList, LISTS) { | ||
SomeList list; | ||
CHECK(list.insertLast(42)); | ||
|
||
list.sort(); | ||
|
||
CHECK_EQ(*list.begin(), 42); | ||
CHECK_EQ(list.begin().next(), list.end()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Тази функция може да е публична и да унищожава оригиналния списък и да създав два нови със съответните вериги в тях.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ако приемем да работим със списъци вместо с итератори, първият параметър може да се пропусне и да се използва this.