目录
一.List定义
二.List的使用
(1)push_back后的遍历
(2)erase,insert那些跟vector一样用法,没什么好说的
(3)sort(List支持排序,但效率低)
【1】简介 List::sort
【2】库里面的sort不支持List的原因(三类迭代器:双向迭代器————新增):
【3】std::sort 与 List::sort 比较,List::sort就是 "飞屋"
三.List模拟实现
重点:
(1)return iterator(_head->_next); 是返回了匿名对象
(2)begin()迭代器能否直接返回 _head->_next ?——可以
(3)it->_a1 解释
(4)为什么list要封装新的iterator,而不能用原生指针?vector却可以
(5)const迭代器
(6)erase迭代器失效
1.第一阶段
List.h
Test.cpp
2.第二阶段 const迭代器
List.h
3.第三阶段
易错点:
四.list 相关题目
Lists are sequence containers that allow constant time insert and erase operations anywhere within the sequence, and iteration in both directions.
List是一个顺序容器,这个顺序容器允许O(1)的时间复杂度任意插入和删除,还支持双向迭代器。(List底层是双向带头链表)
List底层是链表,链表空间不连续,就舍弃的[]+下标的遍历方法,通常使用迭代器遍历。
void test_list1()
{
list lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
list::iterator it = lt.begin();
while (it != lt.end()) //迭代器遍历
{
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : lt) //范围for遍历
{
cout << e << " ";
}
cout << endl;
//list::reverse_iterator rit = lt.rbegin();
auto rit = lt.rbegin();
while (rit != lt.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
}
库里面的sort不支持List,所以List要有自己的sort(原因在下面),因为结构问题,List::sort效率很低,vector用 std::sort 排序会很快,不建议用List::sort。
void test_list3()
{
list lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_front(10);
lt.push_front(20);
lt.push_front(30);
lt.push_front(40);
lt.push_back(1);
lt.push_back(1);
lt.push_back(1);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.sort();
lt.unique();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
}
实现结构的角度,迭代器再实际中分为三类:
1、单向迭代器。只能进行++操作
2、双向迭代器。只能进行++,-- 操作
3、随机迭代器。只能进行++,--,+,- 操作
随机迭代器继承双向迭代器的全部功能,因此需要传双向迭代器的地方也可以传随机迭代器。
双向迭代器继承单向迭代器,需要传单向迭代器的地方也可以传 双向 或 随机迭代器。
随机迭代器属于单向迭代器也属于双向迭代器,即:单向迭代器包含随机迭代器,双向迭代器也包含随机迭代器。
库里面的sort是随机迭代器
List是双向迭代器
双向包含随机,但随机不包含双向,所以std::sort的使用不能包含双向
std::sort 底层是快排(要包含头文件
总结:短数据情况下(1w个数据以内)List::sort是有价值可以用,大数据就要用vector排序了,连续空间的优势
iterator()是匿名对象,匿名对象只存在于构造该对象的那行代码,离开构造匿名对象的那行代码后立即调用析构函数。
①return iterator(_head->_next); 这一句等价于:②先构造一个iterator类的 it对象,因为不是引用返回,所以需要返回it的临时拷贝,临时拷贝又是it拷贝构造得到的。
②iterator it( head-> next);
return it;
对比:②经历了构造对象it,然后拷贝构造一个临时变量;①利用匿名对象优化:把构造匿名对象+拷贝构造临时变量 优化成了 直接构造一个对象并返回。
iterator begin()
{
return iterator(_head->_next); //iterator()是匿名对象,详情看(1)
//return _head->_next;
}
iterator end()
{
return iterator(_head); //iterator()是匿名对象,详情看(1)
}
单参数的构造函数支持隐式类型转换:返回值是iterator,原本是先构造iterator类的对象,再拷贝构造出一个临时变量,通过编译器优化就成了直接构造一个对象并返回。
iterator begin()
{
return iterator(_head->_next); //iterator()是匿名对象,详情看(1)
//return _head->_next; //能否直接返回 _head->_next ?——可以,详情看(2)
}
cout << (*it)._a1 << "-"<< (*it)._a2 <<" "; 访问AA中的数据是正常操作,也可以指针指向的写法访问 it->_a1 。因为 *it 返回的是 _node->_data ,所以 it-> 返回的是 &_node->_data,按理来说指针指向方式 访问AA数据元素写法应该是 it->->_a1 编译器为了可读性进行优化处理,优化以后,省略了一个->,成为it->_a1。
struct __list_iterator中:
T& operator*()
{
return _node->_data;
}
T* operator->()
{
//return &(operator*());
return &_node->_data;
}
void test_list2()
{
list lt;
lt.push_back(AA(1, 1));
lt.push_back(AA(2, 2));
lt.push_back(AA(3, 3));
lt.push_back(AA(4, 4));
// 迭代器模拟的是指针行为
// int* it *it
// AA* it *it it->
list::iterator it = lt.begin();
while (it != lt.end())
{
//cout << (*it)._a1 << "-"<< (*it)._a2 <<" ";
cout << it->_a1 << "-" << it->_a2 << " "; //it->_a1解释,详情见(3)
++it;
}
cout << endl;
}
template
struct __list_iterator //详情见(3),list为什么不能用原生指针?因为物理空间不连续
{……}
vector相当于家里有矿的孩子,不用努力,家里给(系统支持原生指针做迭代器的++--等)。
list 相当于普通家庭,需要自己努力(自己写自定义类型支持迭代器__list_iterator,还要重载运算符)
两者外表(调用)看起来一模一样,实际底层完全不同
为了支持像printf这样形参传的是const对象,正常思路是再复制一遍__list_iterator类,然后全部改成const做成__list_const_iterator类,但是这样复用性不好,不便于维护,所以我们采用模板控制const类型参数:反正是对 operator* 和 -> 返回值的改变,不如直接添加两个模板参数T&和T*,分成const和非const两种模板 。lt对象带const时,就用const_iterator模板;对象是非const时就用iterator模板,就用。调用 lt.begin()时,begin()返回的临时对象类型是const_iterator,则__list_iterator 传入的3个模板类型是const_iterator,
也就是__list_iterator
template
struct __list_iterator
{
// *it
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
//return &(operator*());
return &_node->_data;
}
……
template
class list
{
typedef list_node Node;
public:
typedef __list_iterator iterator;
typedef __list_iterator const_iterator;
const_iterator begin() const
{
// list_node*
return const_iterator(_head->_next);
}
const_iterator end() const
{
return const_iterator(_head);
}
……
void print_list(const list& lt)
{
list::const_iterator it = lt.begin();
while (it != lt.end())
{
//*it = 10; // 不允许修改
cout << *it << " ";
++it;
}
cout << endl;
}
insert不会导致迭代器失效,无论怎么插入,iterator pos都指向原位。只不过返回值是要返回新节点的迭代器。
erase会导致迭代器失效,只要删除了pos位置的节点,那pos就是野指针了。
#pragma once
namespace bit
{
template
struct list_node
{
list_node* _next;
list_node* _prev;
T _data;
list_node(const T& val = T())
:_next(nullptr)
, _prev(nullptr)
, _data(val)
{}
};
template
struct __list_iterator //详情见(4),list为什么不能用原生指针?因为物理空间不连续
{
typedef list_node Node;
typedef __list_iterator self;
Node* _node;
__list_iterator(Node* node) //这里写构造函数是为了传参,默认生成的构造函数无法传参
:_node(node)
{}
// 析构函数 -- 节点不属于迭代器,不需要迭代器释放
// 拷贝构造和赋值重载 -- 默认生成的浅拷贝就可以
T& operator*()
{
return _node->_data;
}
T* operator->()
{
//return &(operator*());
return &_node->_data;
}
self& operator++()
{
_node = _node->_next;
return *this;
}
self operator++(int)
{
self tmp(*this);
_node = _node->_next;
return tmp;
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
self operator--(int)
{
self tmp(*this);
_node = _node->_prev;
return tmp;
}
bool operator!=(const self& it)
{
return _node != it._node;
}
bool operator==(const self& it)
{
return _node == it._node;
}
};
template
class list
{
typedef list_node Node;
public:
typedef __list_iterator iterator;
iterator begin()
{
return iterator(_head->_next); //iterator()是匿名对象,详情看(1)
//return _head->_next; //能否直接返回 _head->_next ?——可以,详情看(2)
}
iterator end()
{
return iterator(_head); //iterator()是匿名对象,详情看(1)
}
list()
{
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
}
void push_back(const T& x)
{
Node* tail = _head->_prev;
Node* newnode = new Node(x);
// _head tail newnode
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
}
private:
Node* _head;
};
void test_list1()
{
list lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
list::iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
struct AA
{
AA(int a1 = 0, int a2 = 0)
:_a1(a1)
, _a2(a2)
{}
int _a1;
int _a2;
};
void test_list2()
{
list lt;
lt.push_back(AA(1, 1));
lt.push_back(AA(2, 2));
lt.push_back(AA(3, 3));
lt.push_back(AA(4, 4));
// 迭代器模拟的是指针行为
// int* it *it
// AA* it *it it->
list::iterator it = lt.begin();
while (it != lt.end())
{
//cout << (*it)._a1 << "-"<< (*it)._a2 <<" ";
cout << it->_a1 << "-" << it->_a2 << " "; //it->_a1解释,详情见(3)
++it;
}
cout << endl;
}
}
int main()
{
bit::test_list2();
return 0;
}
#pragma once
#include
namespace bit
{
template
struct list_node
{
list_node* _next;
list_node* _prev;
T _data;
list_node(const T& val = T())
:_next(nullptr)
, _prev(nullptr)
, _data(val)
{}
};
// typedef __list_iterator iterator;
// typedef __list_iterator const_iterator;
template
struct __list_iterator
{
typedef list_node Node;
typedef __list_iterator self;
Node* _node;
__list_iterator(Node* node)
:_node(node)
{}
// 析构函数 -- 节点不属于迭代器,不需要迭代器释放
// 拷贝构造和赋值重载 -- 默认生成的浅拷贝就可以
// *it
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
//return &(operator*());
return &_node->_data;
}
self& operator++()
{
_node = _node->_next;
return *this;
}
self operator++(int)
{
self tmp(*this);
_node = _node->_next;
return tmp;
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
self operator--(int)
{
self tmp(*this);
_node = _node->_prev;
return tmp;
}
bool operator!=(const self& it)
{
return _node != it._node;
}
bool operator==(const self& it)
{
return _node == it._node;
}
};
// 复用性很差
// 单独实现一个类,支持不能修改迭代器指向节点的数据
//template
//struct __list_const_iterator;
template
class list
{
typedef list_node Node;
public:
typedef __list_iterator iterator;
typedef __list_iterator const_iterator;
const_iterator begin() const
{
// list_node*
return const_iterator(_head->_next);
}
const_iterator end() const
{
return const_iterator(_head);
}
iterator begin()
{
return iterator(_head->_next);
//return _head->_next;
}
iterator end()
{
return iterator(_head);
}
list()
{
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
}
// lt2(lt1)
/*list(const list& lt)
{
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
for (auto e : lt)
{
push_back(e);
}
}*/
void empty_init()
{
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
}
template
list(InputIterator first, InputIterator last)
{
empty_init();
while (first != last)
{
push_back(*first);
++first;
}
}
// 17:00 继续
void swap(list& lt)
{
std::swap(_head, lt._head);
}
// lt2(lt1) -- 现代写法
list(const list& lt)
{
empty_init();
list tmp(lt.begin(), lt.end());
swap(tmp);
}
// lt2 = lt1
list& operator=(list lt)
{
swap(lt);
return *this;
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
}
void push_back(const T& x)
{
//Node* tail = _head->_prev;
//Node* newnode = new Node(x);
_head tail newnode
//tail->_next = newnode;
//newnode->_prev = tail;
//newnode->_next = _head;
//_head->_prev = newnode;
insert(end(), x);
}
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_back()
{
erase(--end());
}
void pop_front()
{
erase(begin());
}
// 插入在pos位置之前
iterator insert(iterator pos, const T& x)
{
Node* newNode = new Node(x);
Node* cur = pos._node;
Node* prev = cur->_prev;
// prev newnode cur
prev->_next = newNode;
newNode->_prev = prev;
newNode->_next = cur;
cur->_prev = newNode;
return iterator(newNode);
}
iterator erase(iterator pos)
{
assert(pos != end());
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
// prev next
prev->_next = next;
next->_prev = prev;
delete cur;
return iterator(next);
}
private:
Node* _head;
};
void print_list(const list& lt)
{
list::const_iterator it = lt.begin();
while (it != lt.end())
{
//*it = 10; // 不允许修改
cout << *it << " ";
++it;
}
cout << endl;
}
erase中的assert漏加,clear要用迭代器实现,list 构造函数重载漏写empty_init()函数,swap应该用库里面swap交换节点,swap函数不能用const(否则无法改变,就是无法交换了),operator=参数不能带引用
增加内容:insert,erase,头尾插,头尾删,clear,析构,构造函数的重载1个,拷贝构造(远古现代写法,共2个),赋值运算符重载(现代写法1个)
链表的拷贝构造要深拷贝,如果浅拷贝就会导致两个链表的头结点_head指向同一个链表
#pragma once
#include
namespace bit
{
template
struct list_node
{
list_node* _next;
list_node* _prev;
T _data;
list_node(const T& val = T())
:_next(nullptr)
, _prev(nullptr)
, _data(val)
{}
};
// typedef __list_iterator iterator;
// typedef __list_iterator const_iterator;
template
struct __list_iterator
{
typedef list_node Node;
typedef __list_iterator self;
Node* _node;
__list_iterator(Node* node)
:_node(node)
{}
// 析构函数 -- 节点不属于迭代器,不需要迭代器释放
// 拷贝构造和赋值重载 -- 默认生成的浅拷贝就可以
// *it
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
//return &(operator*());
return &_node->_data;
}
self& operator++()
{
_node = _node->_next;
return *this;
}
self operator++(int)
{
self tmp(*this);
_node = _node->_next;
return tmp;
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
self operator--(int)
{
self tmp(*this);
_node = _node->_prev;
return tmp;
}
bool operator!=(const self& it)
{
return _node != it._node;
}
bool operator==(const self& it)
{
return _node == it._node;
}
};
// 复用性很差
// 单独实现一个类,支持不能修改迭代器指向节点的数据
//template
//struct __list_const_iterator;
template
class list
{
typedef list_node Node;
public:
typedef __list_iterator iterator;
typedef __list_iterator const_iterator;
const_iterator begin() const
{
// list_node*
return const_iterator(_head->_next);
}
const_iterator end() const
{
return const_iterator(_head);
}
iterator begin()
{
return iterator(_head->_next);
//return _head->_next;
}
iterator end()
{
return iterator(_head);
}
list()
{
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
}
void empty_init()
{
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
}
//这是构造函数,是使用lt的值来进行构造的
template
list(InputIterator first, InputIterator last)
{
empty_init();
while (first != last)
{
push_back(*first);
++first;
}
}
// lt2(lt1) 拷贝构造远古写法
/*list(const list& lt)
{
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
for (auto e : lt)
{
push_back(e);
}
}*/
// 17:00 继续
void swap(list& lt) //不能加const,要不然修改不了了
{
std::swap(_head, lt._head);
}
// lt2(lt1) -- 拷贝构造现代写法
//链表的拷贝构造要深拷贝,如果浅拷贝就会导致两个链表的头结点_head指向同一个链表
list(const list& lt)
{
empty_init();
list tmp(lt.begin(), lt.end());
swap(tmp);
}
// lt2 = lt1
list& operator=(list lt)
{
swap(lt);
return *this;
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
}
void push_back(const T& x)
{
//Node* tail = _head->_prev;
//Node* newnode = new Node(x);
_head tail newnode
//tail->_next = newnode;
//newnode->_prev = tail;
//newnode->_next = _head;
//_head->_prev = newnode;
insert(end(), x);
}
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_back()
{
erase(--end());
}
void pop_front()
{
erase(begin());
}
// 插入在pos位置之前
iterator insert(iterator pos, const T& x)
{
Node* newNode = new Node(x);
Node* cur = pos._node;
Node* prev = cur->_prev;
// prev newnode cur
prev->_next = newNode;
newNode->_prev = prev;
newNode->_next = cur;
cur->_prev = newNode;
return iterator(newNode);
}
iterator erase(iterator pos)
{
assert(pos != end());
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
// prev next
prev->_next = next;
next->_prev = prev;
delete cur;
return iterator(next);
}
private:
Node* _head;
};
void print_list(const list& lt)
{
list::const_iterator it = lt.begin();
while (it != lt.end())
{
//*it = 10; // 不允许修改
cout << *it << " ";
++it;
}
cout << endl;
}
void test_list1()
{
list lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
list::iterator it = lt.begin();
while (it != lt.end())
{
*it = 20;
cout << *it << " ";
++it;
}
cout << endl;
print_list(lt);
}
struct AA
{
AA(int a1 = 0, int a2 = 0)
:_a1(a1)
, _a2(a2)
{}
int _a1;
int _a2;
};
void test_list2()
{
list lt;
lt.push_back(AA(1, 1));
lt.push_back(AA(2, 2));
lt.push_back(AA(3, 3));
lt.push_back(AA(4, 4));
// 迭代器模拟的是指针行为
// int* it *it
// AA* it *it it->
list::iterator it = lt.begin();
while (it != lt.end())
{
//cout << (*it)._a1 << "-"<< (*it)._a2 <<" ";
cout << it->_a1 << "-" << it->_a2 << " ";
++it;
}
cout << endl;
}
void test_list3()
{
list lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_front(1);
lt.push_front(2);
lt.push_front(3);
lt.push_front(4);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.pop_front();
lt.pop_front();
lt.pop_back();
lt.pop_back();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
}
void test_list4()
{
list lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
lt.push_back(6);
// 要求在偶数的前面插入这个偶数*10
auto it1 = lt.begin();
while (it1 != lt.end())
{
if (*it1 % 2 == 0)
{
lt.insert(it1, *it1 * 10);
}
++it1;
}
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
}
void test_list5()
{
list lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
lt.push_back(6);
// 删除所有的偶数
/*auto it1 = lt.begin();
while (it1 != lt.end())
{
if (*it1 % 2 == 0)
{
lt.erase(it1);
}
++it1;
}*/
auto it1 = lt.begin();
while (it1 != lt.end())
{
if (*it1 % 2 == 0)
{
it1 = lt.erase(it1);
}
else
{
++it1;
}
}
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.clear();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.push_back(10);
lt.push_back(20);
lt.push_back(30);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
}
void test_list6()
{
list lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
lt.push_back(6);
list lt1(lt);
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
list lt2;
lt2.push_back(10);
lt2.push_back(20);
lt1 = lt2;
for (auto e : lt2)
{
cout << e << " ";
}
cout << endl;
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
}
}
for (int i=1; i<10; ++i) myvector.push_back(i); // 1 2 3 4 5 6 7 8 9
std::reverse(myvector.begin(),myvector.end()); // 9 8 7 6 5 4 3 2 1
可知
std::reserve 是将包括begin迭代器开始到end前一个位置进行逆置