1.list的使用
1.1list****的构造
|-------------------------------------------------------------------|-------------------------------------------------------|
| 构造函数((constructor)) | 接口说明 |
| list (size_type n, const value_type& val = value_type()) | 构造的list中包含n个值为val的 元素 |
| list() | 构造空的****list |
| list (const list& x) | 拷贝构造函数 |
| list (InputIterator first, InputIterator last) | 用**[first, last)**区间中的元素构造 list |
1.2list iterator****的使用
|-----------------------|---------------------------------------------------------------------------------------------------------------------|
| 函数声****明 | 接口说明 |
| begin + end | 返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器 |
| rbegin + rend | 返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的reverse_iterator,即begin****位置 |
1.3 list capacity
|-----------|------------------------------------------------------|
| 函数声明 | 接口说明 |
| empty | 检测list是否为空,是返回true,否则返回****false |
| size | 返回list中有效节点的个数 |
1.4 list element access
|-----------|--------------------------------|
| 函数声明 | 接口说明 |
| front | 返回list的第一个节点中值的引用 |
| back | 返回list的最后一个节点中值的引用 |
1.5 list modifiers
|----------------|-------------------------------------------------|
| 函数声明 | 接口说明 |
| push_front | 在list首元素前插入值为val的元素 |
| pop_front | 删除list中第一个元素 |
| push_back | 在list尾部插入值为val的元素 |
| pop_back | 删除list中最后一个元素 |
| insert | 在list position位置中插入值为val的元素 |
| erase | 删除list position位置的元素 |
| swap | 交换两个list中的元素 |
| clear | 清空list中的有效元素 |
emplace_back和emplace_front与push_back和push_front很像,只有在比如说push_back({1,2}),里面传的是自定义类型类的隐式类型转换时,不能使用emplace_back({1,2}),emplace_back(1,2)直接写这种,在这种使用上emplace_back更有效率。
assign是clear与构造n个val的结合,
1.6 list operations
|----------|----------------------------------------------|
| 函数声明 | 接口说明 |
| remove | 删除链表的指定元素(指定元素没有也不会报错) |
| unique | 移除连续的重复元素 |
| merge | 合并已经排序好的两个链表 |
| sort | 排序:list完成的是双向迭代器(用到的是归并排序),但算法库的sort需要用随机迭代器 |
为什么算法里有排序还要再类里实现sort那
迭代器按照性质可以分为单向迭代器(++)、双向迭代器(++/--)、随机迭代器(++/--/+/-)
算法用RandomAccessIterator需要用随机迭代器(只能用迭代器是随机迭代器的容器string、vector、deque)
算法用BidrectionalIterator需要用双向迭代器(双向迭代器的容器list、map、set),也可以用随机迭代器
算法用InputIterator需要用单项迭代器(单项迭代器的容器forword_list、unorder_map、unorder_set),双向迭代器和随机迭代器也可以使用
用sort排序,默认是排升序,如果想要排降序,需要用到仿函数greater<int> gt,一般用匿名对象,如lt1.sort(greater<int>());
splice粘贴:把链表的一部分粘到另一个链表的某一个位置之前
- list的模拟
首先在进行list的模拟的过程中,你需要对结点进行构造,
template<class T>
struct list_node
{
list_node* _next;
list_node* _prev;
T _data;
list_node(const T& x = T())
:_next(nullptr)
, _prev(nullptr)
, _data(x)
{
}
};
为什么用struct 来申请这个类,因为这样下面申请的链表list的类才能够访问其中的成员变量,当然这不是绝对的,也可以把成员变量放在class定义的类的public区。
在c语言中创建结点实现链表的时候,并不需要构造函数,但这里需要。这里为后面进行new 操作提供了前提。
template<class T>
class list
{
typedef list_node<T> Node;
public:
list()
{
_node = new Node;
_node->_next = _node;
_node->_prev = _node;
}
void push_back(const T& x)
{
Node* newnode = new Node(x);
Node* prev = _node->_prev;
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = _node;
_node->_prev = newnode;
}
private:
Node* _node;
};
注意:typedef放在public区和private区是不一样的,想要在外部访问就typedef在public区,不然就放在private区。
template<class T>
struct list_iterator
{
typedef list_node<T> Node;
Node* _node;
list_iterator(Node* node)
:_node(node)
{
}
T& operator*()
{
return _node->_data;
}
list_iterator<T>& operator++()
{
_node = _node->_next;
return *this;
}
//const需要存在,因为list.begin()可能返回的是右值
bool operator==(const list_iterator<T>& it)
{
return _node == it._node;
}
bool operator!=(const list_iterator<T>& it)
{
return _node != it._node;
}
};
当使用迭代器的时候,就要对迭代器进行解引用++、--等相关操作,但是node*解引用并不能得到数据,所以简单的将node* typedef成iterator是不可以的。所以要重新封装迭代器,由于node*不能满足迭代器的需求,但每个结点之间的关联还是需要node* 的,所以重新封装node*,重载解引用、++、--等。
当处理的是自定义类型的数据时, 就涉及到应用->的操作,
struct A
{
A(int a1 = 1, int a2 = 1)
:_a1(a1)
, _a2(a2)
{}
int _a1;
int _a2;
};
int main()
{
zyf::list<A> lt2;
lt2.push_back({ 1,1 });
lt2.push_back({ 2,2 });
lt2.push_back({ 3,3 });
lt2.push_back({ 4,4 });
zyf::list<A>::iterator it = lt2.begin();
while (it != lt2.end())
{
cout << it->_a1 << ";" << it->_a2 << endl;
++it;
}
return 0;
}
而对于->的重载是
T* operator->()
{
return &_node->_data;
}
其实这里看起来并不对,为什么可以node*->_a1直接取,事实上本应该有两个->,但是编译器为了可读性,做了特殊处理,省略了一个箭头。it->指向的是A*,再->指向的就是_a1,或者_a2.
在遍历的过程中,对iterator指向的内容不想修改,就需要const T&和const T*
template<class T>
struct list_iterator
{
typedef list_node<T> Node;
Node* _node;
list_iterator(Node* node)
:_node(node)
{
}
T& operator*()
{
return _node->_data;
}
T* operator->()
{
return &_node->_data;
}
list_iterator<T>& operator++()
{
_node = _node->_next;
return *this;
}
bool operator==(const list_iterator<T>& it)
{
return _node == it._node;
}
bool operator!=(const list_iterator<T>& it)
{
return _node != it._node;
}
};
template<class T>
struct list_const_iterator
{
typedef list_node<T> Node;
Node* _node;
list_const_iterator(Node* node)
:_node(node)
{
}
const T& operator*()
{
return _node->_data;
}
const T* operator->()
{
return &_node->_data;
}
list_const_iterator<T>& operator++()
{
_node = _node->_next;
return *this;
}
bool operator==(const list_const_iterator<T>& it)
{
return _node == it._node;
}
bool operator!=(const list_const_iterator<T>& it)
{
return _node != it._node;
}
};
template<class T>
class list
{
typedef list_node<T> Node;
public:
typedef list_iterator<T> iterator;
typedef list_const_iterator<T> const_iterator;
这里用两段来对iterator和const_iterator进行封装,但这里可以用模板变成一段
template<class T, class Ref, class Ptr>
struct list_iterator
{
typedef list_node<T> Node;
typedef list_iterator<T, Ref, Ptr> self;
Node* _node;
list_iterator(Node* node)
:_node(node)
{
}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
self& operator++()
{
_node = _node->_next;
return *this;
}
bool operator==(const self& it)
{
return _node == it._node;
}
bool operator!=(const self& it)
{
return _node != it._node;
}
};
template<class T>
class list
{
typedef list_node<T> Node;
public:
typedef list_iterator<T, T&, T*> iterator;
typedef list_iterator<T, const T&, const T*> const_iterator;