mirror of https://github.com/inolen/redream.git
added Sort to IntrusiveList
This commit is contained in:
parent
7258b89778
commit
582e615e60
|
@ -35,11 +35,7 @@ template <typename T>
|
|||
class IntrusiveList {
|
||||
public:
|
||||
// For the iterator, remember that a C++ iterator's range is [begin, end),
|
||||
// meaning the end iterator will be wrapping an invalid node. To support this,
|
||||
// one option would be to maintain an instance of T who's prev is always equal
|
||||
// to tail and next is always nullptr. However, that would require T to have
|
||||
// a default constructor. Instead, we're using a sentinel node address and
|
||||
// some conditional logic inside of the decrement and increment operators.
|
||||
// meaning the end iterator will be wrapping an invalid node.
|
||||
template <bool is_const_iterator, bool is_reverse_iterator>
|
||||
class shared_iterator
|
||||
: public std::iterator<std::bidirectional_iterator_tag, T> {
|
||||
|
@ -185,6 +181,86 @@ class IntrusiveList {
|
|||
|
||||
void Clear() { head_ = tail_ = nullptr; }
|
||||
|
||||
// Implements the mergesort for linked lists as described at
|
||||
// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
|
||||
template <class Compare>
|
||||
void Sort(Compare comp) {
|
||||
T *head = head_;
|
||||
T *tail = nullptr;
|
||||
int k = 1;
|
||||
|
||||
while (true) {
|
||||
int merges = 0;
|
||||
T *p = head;
|
||||
|
||||
head = nullptr;
|
||||
tail = nullptr;
|
||||
|
||||
while (p) {
|
||||
// track the number of lists merged this pass
|
||||
merges++;
|
||||
|
||||
// step q forward k places, tracking the size of p
|
||||
int psize = 0;
|
||||
int qsize = k;
|
||||
T *q = p;
|
||||
while (psize < k && q) {
|
||||
psize++;
|
||||
q = q->next_;
|
||||
}
|
||||
|
||||
// merge the list starting at p of length psize with the list starting
|
||||
// at q of at most, length qsize
|
||||
while (psize || (qsize && q)) {
|
||||
T *next;
|
||||
|
||||
if (!psize) {
|
||||
next = q;
|
||||
q = q->next_;
|
||||
qsize--;
|
||||
} else if (!qsize || !q) {
|
||||
next = p;
|
||||
p = p->next_;
|
||||
psize--;
|
||||
} else if (comp(q, p)) {
|
||||
next = q;
|
||||
q = q->next_;
|
||||
qsize--;
|
||||
} else {
|
||||
next = p;
|
||||
p = p->next_;
|
||||
psize--;
|
||||
}
|
||||
|
||||
// move merged node to tail
|
||||
if (!tail) {
|
||||
head = tail = next;
|
||||
} else {
|
||||
tail->next_ = next;
|
||||
tail = tail->next_;
|
||||
}
|
||||
}
|
||||
|
||||
p = q;
|
||||
}
|
||||
|
||||
if (tail) {
|
||||
tail->next_ = nullptr;
|
||||
}
|
||||
|
||||
// if only 1 pair of lists was merged, this is the end
|
||||
if (merges <= 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
k *= 2;
|
||||
}
|
||||
|
||||
// update internal head and tail with sorted head and tail
|
||||
head_ = head;
|
||||
tail_ = tail;
|
||||
}
|
||||
|
||||
private:
|
||||
T *head_;
|
||||
T *tail_;
|
||||
|
|
|
@ -10,6 +10,13 @@ struct Person : public IntrusiveListNode<Person> {
|
|||
const char *name;
|
||||
};
|
||||
|
||||
struct PersonComparator {
|
||||
bool operator()(const Person *a, const Person *b) const {
|
||||
// sort is descending order
|
||||
return strcmp(b->name, a->name) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
class IntrusiveTestEmptySet : public ::testing::Test {
|
||||
public:
|
||||
IntrusiveTestEmptySet() : arena(1024) {}
|
||||
|
@ -191,3 +198,18 @@ TEST_F(IntrusiveTestABCSet, ValidOnRemove) {
|
|||
people.Remove(aaa);
|
||||
ASSERT_EQ(bbb, *it);
|
||||
}
|
||||
|
||||
// sort tests
|
||||
TEST_F(IntrusiveTestEmptySet, EmptySort) {
|
||||
people.Sort(PersonComparator());
|
||||
ASSERT_EQ(NULL, people.head());
|
||||
ASSERT_EQ(NULL, people.tail());
|
||||
}
|
||||
|
||||
TEST_F(IntrusiveTestABCSet, Sort) {
|
||||
people.Sort(PersonComparator());
|
||||
auto it = people.begin();
|
||||
ASSERT_STREQ("ccc", (it++)->name);
|
||||
ASSERT_STREQ("bbb", (it++)->name);
|
||||
ASSERT_STREQ("aaa", (it++)->name);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue