Skip to content

Commit

Permalink
完成习题20-14单向链表
Browse files Browse the repository at this point in the history
  • Loading branch information
ZZy979 committed Feb 17, 2024
1 parent 2bb3c7b commit e3078bc
Show file tree
Hide file tree
Showing 6 changed files with 510 additions and 57 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,7 @@ $ ctest
* [20-8~20-10](ch20/simple_text_editor.cpp)
* [20-12](ch20/exec20-12.cpp)
* [20-13](ch20/doubly_linked_list_v4.h)
* [20-14](ch20/singly_linked_list.h) (简化的`std::forward_list`[WIP]
* [20-14](ch20/singly_linked_list.h) (简化的`std::forward_list`
* [20-15](ch20/pvector.h)
* [20-16](ch20/ovector.h)
* [20-17](ch20/ownership_vector.h)
Expand Down
2 changes: 1 addition & 1 deletion ch20/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ add_output_test(
COMMAND exec20-12
OUTPUT_FILE ${TESTDATA_DIR}/exec20-12_output_empty.txt
)
add_gtest(NAME singly_linked_list_test SOURCE singly_linked_list_test.cpp LIBRARY singly_linked_list)
add_gtest(NAME singly_linked_list_test SOURCE singly_linked_list_test.cpp LIBRARY singly_linked_list counted_element)
add_gtest(NAME pvector_test SOURCE pvector_test.cpp LIBRARY pvector counted_element)
add_gtest(NAME ovector_test SOURCE ovector_test.cpp LIBRARY ovector counted_element)
add_gtest(NAME ownership_vector_test SOURCE ownership_vector_test.cpp LIBRARY ownership_vector counted_element)
Expand Down
26 changes: 13 additions & 13 deletions ch20/doubly_linked_list_v4.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,6 @@ class list {

void assign(size_type n, const T& val);

// replace the contents with [first, last)
template<class InputIt, class = std::enable_if_t<is_input_iterator<InputIt>::value>>
void assign(InputIt first, InputIt last);

Expand Down Expand Up @@ -254,25 +253,26 @@ typename list<T>::iterator list<T>::erase(iterator first, iterator last) {
// replace the contents with n copies of val
template<class T>
void list<T>::assign(size_type n, const T& val) {
iterator it = begin();
for (; it != end() && n > 0; ++it, --n)
*it = val;
iterator curr = begin(), end_ = end();
for (; curr != end_ && n > 0; ++curr, --n)
*curr = val;
if (n > 0)
insert(end(), n, val);
insert(end_, n, val);
else
erase(it, end());
erase(curr, end_);
}

// replace the contents with [first, last)
template<class T>
template<class InputIt, class>
void list<T>::assign(InputIt first2, InputIt last2) {
iterator first1 = begin(), last1 = end();
for (; first1 != last1 && first2 != last2; ++first1, ++first2)
*first1 = *first2;
if (first2 == last2)
erase(first1, last1);
void list<T>::assign(InputIt first, InputIt last) {
iterator curr = begin(), end_ = end();
for (; curr != end_ && first != last; ++curr, ++first)
*curr = *first;
if (first == last)
erase(curr, end_);
else
insert(last1, first2, last2);
insert(end_, first, last);
}

// transfer elements in [first, last) from l to this list, insert before p
Expand Down
3 changes: 1 addition & 2 deletions ch20/doubly_linked_list_v4_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ TEST_F(DoublyLinkedListV4Test, CopyAssignment) {
list<double> l2 = {1, 2, 3};
l2 = l_;
EXPECT_EQ(l2.size(), l_.size());
for (int i = 0; i < l2.size(); ++i)
EXPECT_TRUE(std::equal(l2.begin(), l2.end(), l_.begin()));
EXPECT_TRUE(std::equal(l2.begin(), l2.end(), l_.begin()));
*std::next(l_.begin()) = 99;
l2.front() = 88;
EXPECT_DOUBLE_EQ(l_.front(), 0);
Expand Down
287 changes: 247 additions & 40 deletions ch20/singly_linked_list.h
Original file line number Diff line number Diff line change
@@ -1,66 +1,273 @@
#pragma once

#include <initializer_list>
#include <iterator>
#include <type_traits>

struct Node_base {
Node_base* succ = nullptr;

void insert_after(Node_base* n);
void insert_after(Node_base* first, Node_base* last);
void erase_after();
};

// insert n after this node
void Node_base::insert_after(Node_base* n) {
n->succ = succ;
succ = n;
}

// insert (first, last] after this node
void Node_base::insert_after(Node_base* first, Node_base* last) {
auto tmp = first->succ;
if (last) {
first->succ = last->succ;
last->succ = succ;
}
else {
first->succ = nullptr;
}
succ = tmp;
}

// remove next node from list
void Node_base::erase_after() {
succ = succ->succ;
}

// singly-linked list node
template<class Elem>
struct Link {
Link* succ;
Elem val;
template<class T>
struct Link : public Node_base {
T val;

explicit Link(const T& v = T()) :Node_base{}, val(v) {}
};

// singly-linked list iterator
template<class T>
struct Slist_iterator {
using iterator_category = std::forward_iterator_tag;
using value_type = T;
using difference_type = ptrdiff_t;
using pointer = T*;
using reference = T&;

explicit Slist_iterator(Node_base* p = nullptr) :node(p) {}

Slist_iterator& operator++() { node = node->succ; return *this; }

reference operator*() const { return static_cast<Link<T>*>(node)->val; }
pointer operator->() const { return &static_cast<Link<T>*>(node)->val; }

bool operator==(const Slist_iterator& b) const { return node == b.node; }
bool operator!=(const Slist_iterator& b) const { return node != b.node; }

Node_base* node; // current link
};

// singly-linked list const iterator
template<class T>
struct Slist_const_iterator {
using iterator_category = std::forward_iterator_tag;
using value_type = T;
using difference_type = ptrdiff_t;
using pointer = const T*;
using reference = const T&;

explicit Slist_const_iterator(const Node_base* p = nullptr) :node(p) {}

Slist_const_iterator& operator++() { node = node->succ; return *this; }

reference operator*() const { return static_cast<const Link<T>*>(node)->val; }
pointer operator->() const { return &static_cast<const Link<T>*>(node)->val; }

bool operator==(const Slist_const_iterator& b) const { return node == b.node; }
bool operator!=(const Slist_const_iterator& b) const { return node != b.node; }

const Node_base* node; // current link
};

template<class Iter, class Tag>
using is_iterator = std::is_convertible<typename std::iterator_traits<Iter>::iterator_category, Tag>;

template<class Iter>
using is_input_iterator = is_iterator<Iter, std::input_iterator_tag>;

// singly-linked list
template<class Elem>
template<class T>
class slist {
class iterator;
public:
using size_type = size_t;
using value_type = T;
using iterator = Slist_iterator<T>;
using const_iterator = Slist_const_iterator<T>;

slist() = default;

explicit slist(size_type n, const T& val = T()) {
for (auto p = &head; n; --n, p = p->succ)
p->succ = new Link<T>(val);
}

template<class InputIt, class = std::enable_if_t<is_input_iterator<InputIt>::value>>
slist(InputIt first, InputIt last) {
for (auto p = &head; first != last; ++first, p = p->succ)
p->succ = new Link<T>(*first);
}

slist(std::initializer_list<T> lst) :slist(lst.begin(), lst.end()) {}

slist(const slist& l) :slist(l.begin(), l.end()) {}

slist& operator=(const slist& l) {
if (this != &l) assign(l.begin(), l.end());
return *this;
}

slist() :first(nullptr), sz(0) {}
slist(slist&& l) noexcept :head(l.head) { l.head.succ = nullptr; }

~slist() {
while (first) {
auto next = first->succ;
delete first;
first = next;
}
slist& operator=(slist&& l) noexcept {
clear();
head.succ = l.head.succ;
l.head.succ = nullptr;
return *this;
}

~slist() { clear(); }

// iterator to one before forst element
iterator before_begin() { return iterator(&head); }
const_iterator before_begin() const { return const_iterator(&head); }

// iterator to first element
iterator begin() { return iterator(first); }
iterator begin() { return iterator(head.succ); }
const_iterator begin() const { return const_iterator(head.succ); }

// iterator to one beyond last element
iterator end() { return iterator(nullptr); }
const_iterator end() const { return const_iterator(nullptr); }

iterator insert_after(iterator p, const Elem& v); // insert v into list after p
iterator erase_after(iterator p); // remove the element after p from the list
iterator insert_after(iterator p, const T& v);
iterator insert_after(iterator p, size_type n, const T& val);

void push_front(const Elem& v); // insert v at front
void pop_front(); // remove the first element
template<class InputIt, class = std::enable_if_t<is_input_iterator<InputIt>::value>>
iterator insert_after(iterator p, InputIt first, InputIt last);

Elem& front(); // the first element
iterator erase_after(iterator p);
iterator erase_after(iterator first, iterator last);

int size() const { return sz; };
void clear();

private:
Link<Elem>* first;
int sz;
};
void assign(size_type n, const T& val);

template<class Elem>
class slist<Elem>::iterator {
public:
explicit iterator(Link<Elem>* p) :curr(p) {}
iterator& operator++() { curr = curr->succ; return *this; } // forward
Elem& operator*() { return curr->val; } // get value (dereference)
bool operator==(const iterator& b) const { return curr == b.curr; }
bool operator!=(const iterator& b) const { return curr != b.curr; }
template<class InputIt, class = std::enable_if_t<is_input_iterator<InputIt>::value>>
void assign(InputIt first, InputIt last);

// insert v at front
void push_front(const T& v) { insert_after(before_begin(), v); }

// remove the first element
void pop_front() { erase_after(before_begin()); }

// the first element
T& front() { return *begin(); }
const T& front() const { return *begin(); }

bool empty() const { return head.succ == nullptr; }

void splice_after(iterator p, slist& l) { splice_after(p, l, l.before_begin(), l.end()); }

// transfer elements in (first, last) from l to this list, insert after p
void splice_after(iterator p, slist& l, iterator first, iterator last) { _splice_after(p, first, last); }

private:
Link<Elem>* curr; // current link
iterator _splice_after(iterator p, iterator first, iterator last);

Node_base head; // virtual head node
};

template<class Elem>
void slist<Elem>::push_front(const Elem& v) {
if (!first)
first = new Link<Elem>{nullptr, nullptr, v};
else
first = first->prev = new Link<Elem>{nullptr, first, v};
++sz;
// insert v into list after p
template<class T>
typename slist<T>::iterator slist<T>::insert_after(iterator p, const T& v) {
auto tmp = new Link<T>(v);
p.node->insert_after(tmp);
return iterator(tmp);
}

// insert n copies of val into list after p
template<class T>
typename slist<T>::iterator slist<T>::insert_after(iterator p, slist::size_type n, const T& val) {
if (n == 0) return p;
slist<T> tmp(n, val);
return _splice_after(p, tmp.before_begin(), tmp.end());
}

// insert [first, last) into list after p
template<class T>
template<class InputIt, class>
typename slist<T>::iterator slist<T>::insert_after(iterator p, InputIt first, InputIt last) {
if (first == last) return p;
slist<T> tmp(first, last);
return _splice_after(p, tmp.before_begin(), tmp.end());
}

// remove element following p from the list
template<class T>
typename slist<T>::iterator slist<T>::erase_after(iterator p) {
auto next = p.node->succ;
p.node->erase_after();
delete static_cast<Link<T>*>(next);
return iterator(p.node->succ);
}

// remove (first, last) from the list
template<class T>
typename slist<T>::iterator slist<T>::erase_after(iterator first, iterator last) {
while (first.node->succ != last.node)
erase_after(first);
return last;
}

template<class T>
void slist<T>::clear() {
while (head.succ) {
auto p = head.succ->succ;
delete static_cast<Link<T>*>(head.succ);
head.succ = p;
}
}

// replace the contents with n copies of val
template<class T>
void slist<T>::assign(slist::size_type n, const T& val) {
iterator prev = before_begin(), curr = begin(), end_ = end();
for (; curr != end_ && n > 0; ++prev, ++curr, --n)
*curr = val;
if (n > 0)
insert_after(prev, n, val);
else if (curr != end_)
erase_after(prev, end_);
}

// replace the contents with [first, last)
template<class T>
template<class InputIt, class>
void slist<T>::assign(InputIt first, InputIt last) {
iterator prev = before_begin(), curr = begin(), end_ = end();
for (; curr != end_ && first != last; ++prev, ++curr, ++first)
*curr = *first;
if (first != last)
insert_after(prev, first, last);
else if (curr != end_)
erase_after(prev, end_);
}

template<class T>
typename slist<T>::iterator slist<T>::_splice_after(iterator p, iterator first, iterator last) {
Node_base* e = first.node;
while (e->succ != last.node) e = e->succ;
if (first.node == e) return iterator(p);
p.node->insert_after(first.node, e);
return iterator(e);
}
Loading

0 comments on commit e3078bc

Please sign in to comment.