一开始确实被这个双向链表整晕了,Node里面不停套Node,简直无限套娃,让人不知道该怎么下手。后来看了数据结构与算法分析这本书的图解才算整明白。
我把链表分成了三个部分:
第一部分是Node.Node是一个由两根指针,以及我们需要存储的数据构成的结构体。这个Node就是无限套娃的起源,也是链表用于存储数据的部分——Node里面的两根指针的类型仍旧是Node类型。
第二部分是迭代器,这个迭代器是用来访问链表的存储数据,对其进行读与写。迭代器在这里是一个类,类里面的数据成员仅仅只有一根Node指针。这个Node指针是用来访问Node这个结构体用的接口。迭代器的这一部分的函数都是对一些运算符号的重载,从而实现对顺序查找等等方法。
第三部分就是链表构成自身功能的核心函数,他们的实现是依赖于Node和迭代器这两部分的。
链表内在的成员是这样构造的:
class List {
private:
struct Node;
public:
class const_iterator;
class iterator :public const_iterator;
private:
int theSize;
Node* head;
Node* tail;
};
因为我对泛型编程不熟悉,所以采用int来简化代码。
Node节点是这样构造的:
struct Node {
int data;
Node* prev;
Node* next;
Node(const int& d=0, Node* p = nullptr, Node* n = nullptr) :
data{ d }, prev{ p }, next{ n }{};
Node( int&& d, Node* p = nullptr, Node* n = nullptr) :
data{ std::move(d) }, prev{ p }, next{ n }{}
};
可以看到,一根指针指向前一个节点,另一根指针指向后一个节点。同时在链表中的两根头尾Node指针,其实是用来简化问题的。他们自身是不存储任何数据的。
而迭代器的实现如下:
class const_iterator {
protected:
Node* current;
int& retrieve()const {
return current->data;
}
const_iterator(Node* p) :current{ p } {};
friend class List;
public:
const_iterator() :current{ nullptr } {};
const_iterator(const const_iterator& rhs) :current{ rhs.current } {};
const int& operator*()const {
return retrieve();
}
const_iterator& operator++() {
current = current->next;
return *this;
}
const_iterator operator++(int) {
const_iterator old = *this;
++(*this);
return old;
}
bool operator==(const const_iterator& rhs)const {
return current == rhs.current;
}
bool operator!=(const const_iterator& rhs)const {
return !(*this == rhs);
}
};
class iterator :public const_iterator {
protected:
iterator(Node* p) :const_iterator{ p } {};
friend class List;
public:
iterator() {};
int& operator*() {
return const_iterator::retrieve();
}
const int& operator*()const {
return const_iterator::operator*();
}
iterator& operator++() {
this->current = this->current->next;
return*this;
}
iterator& operator--() {
this->current = this->current->prev;
return *this;
}
iterator operator++(int) {
iterator old = *this;
++(*this);
return old;
}
iterator operator--(int) {
iterator old = *this;
--(*this);
return old;
}
};
iterator可以实现const_iterator的所有功能,从这个意义上iterator IS-A const_iterator,因此采用了继承的手法。同时,迭代器只是一个用来访问数据的接口,因此它的函数大部分都是重载函数。
而接下来就是链表的核心功能:插入和删除。
插入功能:
插入功能的实现可以用这副图来解释:
因此看图的话代码就是下面的:
iterator insert(iterator itr, const int& x) {
Node* p = itr.current;
theSize++;
return{ p->prev=p->prev->next= new Node{x,p->prev,p} };
}
iterator insert(iterator itr, const int&& x) {
Node* p = itr.current;
theSize++;
return{ p->prev = p->prev->next = new Node{std::move(x),p->prev,p}};
}
至于删除功能,代码如下:
iterator erase(iterator itr) {
Node* p = itr.current;
iterator itRal = p->next;
p->prev->next = p->next;
p->next->prev = p->prev;
delete p;
theSize--;
return itRal;
}
iterator erase(iterator from, iterator to) {
for (iterator itr = from; itr != to; itr++)
itr = erase(itr);
return to;
}
那么现在,已经实现了链表的主要功能,最后,附上链表的全部代码:
class List {
private:
struct Node {
int data;
Node* prev;
Node* next;
Node(const int& d=0, Node* p = nullptr, Node* n = nullptr) :
data{ d }, prev{ p }, next{ n }{};
Node( int&& d, Node* p = nullptr, Node* n = nullptr) :
data{ std::move(d) }, prev{ p }, next{ n }{}
};
public:
class const_iterator {
protected:
Node* current;
int& retrieve()const {
return current->data;
}
const_iterator(Node* p) :current{ p } {};
friend class List;
public:
const_iterator() :current{ nullptr } {};
const_iterator(const const_iterator& rhs) :current{ rhs.current } {};
const int& operator*()const {
return retrieve();
}
const_iterator& operator++() {
current = current->next;
return *this;
}
const_iterator operator++(int) {
const_iterator old = *this;
++(*this);
return old;
}
bool operator==(const const_iterator& rhs)const {
return current == rhs.current;
}
bool operator!=(const const_iterator& rhs)const {
return !(*this == rhs);
}
};
class iterator :public const_iterator {
protected:
iterator(Node* p) :const_iterator{ p } {};
friend class List;
public:
iterator() {};
int& operator*() {
return const_iterator::retrieve();
}
const int& operator*()const {
return const_iterator::operator*();
}
iterator& operator++() {
this->current = this->current->next;
return*this;
}
iterator& operator--() {
this->current = this->current->prev;
return *this;
}
iterator operator++(int) {
iterator old = *this;
++(*this);
return old;
}
iterator operator--(int) {
iterator old = *this;
--(*this);
return old;
}
};
private:
int theSize;
Node* head;
Node* tail;
void init() {
theSize = 0;
head = new Node;
tail = new Node;
head->next = tail;
tail->prev = head;
};
public:
iterator begin() {
return { head->next };
}
const_iterator begin()const {
return{ head->next };
}
iterator end() {
return { tail };
}
const_iterator end()const
{
return{ tail };
};
int size()const {
return theSize;
}
bool empty()const {
return size() == 0;
}
int& front() {
return *begin();
}
const int& front()const {
return *begin();
}
List() {
init();
}
List(const List& rhs) {
init();
for (auto& x : rhs) {
push_back(x);
}
}
List& operator=(const List& rhs) {
List copy(rhs);
std::swap(*this, copy);
return *this;
}
List(List&& rhs):theSize{rhs.theSize},head{rhs.head},tail{rhs.tail}
{
rhs.theSize = 0;
rhs.head = nullptr;
rhs.tail = nullptr;
}
~List() {
clear();
delete head;
delete tail;
}
iterator insert(iterator itr, const int& x) {
Node* p = itr.current;
theSize++;
return{ p->prev=p->prev->next= new Node{x,p->prev,p} };
}
iterator insert(iterator itr, const int&& x) {
Node* p = itr.current;
theSize++;
return{ p->prev = p->prev->next = new Node{std::move(x),p->prev,p}};
}
iterator erase(iterator itr) {
Node* p = itr.current;
iterator itRal = p->next;
p->prev->next = p->next;
p->next->prev = p->prev;
delete p;
theSize--;
return itRal;
}
iterator erase(iterator from, iterator to) {
for (iterator itr = from; itr != to; itr++)
itr = erase(itr);
return to;
}
void push_back(const int &x) {
insert(end(), x);
}
void push_back(const int&& x) {
insert(end(), std::move(x));
}
int& back() {
return *--end();
}
void push_front(const int&x) {
insert(begin(), x);
}
void push_front( int&& x) {
insert(begin(), std::move(x));
}
void pop_front() {
erase(begin());
}
void pop_back() {
erase(--end());
}
void clear() {
while (!empty())
pop_front();
}
};