1.list
vector:顺序表
list:链表
为什么会有list?
补充vector的缺点存在的
vector缺点:
1.头部和中部的插入删除效率低 O(N) 因为需要挪动数据
2.插入数据空间不够需要增容 增容需要开新空间 拷贝数据 释放旧空间 会付出很大代价
优点:
1.支持下标的随机访问 间接的就很好的支持排序 二分查找 堆算法
list就是为了解决vector的缺陷
优点:
1.list头部 中间插入不再需要挪动数据 效率高 O(1)
2.list插入数据是新增节点 不需要增容
缺点:
1.不支持随机访问
所以实际使用中vector和list是两个相辅相成的容器
2.使用
1°四个默认构造函数/迭代器
//const迭代器
void print_list(const list<int>& lt)
{
list<int>::const_iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
void test_list1()
{
list<int> lt1;//构造
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
lt1.push_back(4);
//迭代器
list<int>::iterator it1 = lt1.begin();
while (it1 != lt1.end())
{
cout << *it1 << " ";
++it1;
}
cout << endl;
list<int> lt2(lt1);//拷贝构造
print_list(lt2);
list<int> lt3;
lt3.push_back(10);
lt3.push_back(20);
lt3.push_back(30);
lt3.push_back(40);
lt1 = lt3;//赋值
//只要一个容器支持迭代器 就可以使用范围for的操作
//范围for
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
//reverse 逆置
//reserve 保留
//反向迭代器
list<int>::reverse_iterator rit1 = lt1.rbegin();
while (rit1 != lt1.rend())
{
cout << *rit1 << " ";
++rit1;
}
cout << endl;
}
2°尾插/尾删
void test_list2()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_front(0);
lt.push_front(-1);
print_list(lt);
lt.pop_back();
lt.pop_front();
print_list(lt);
}
3°插入/删除/查找
void test_list3()
{
list<int> 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);
print_list(lt);
//lt.insert(lt.begin() + 3, 30);//不支持 链表加不到 空间不连续 vector支持
//在3的前面插入30
list<int>::iterator pos = find(lt.begin(), lt.end(), 3);
if (pos != lt.end())
{
lt.insert(pos, 30);//3前面插入30 //这里insert以后失效了嘛?
lt.erase(pos);//删除3
}
print_list(lt);
}
4°逆置/排序
void test_list4()
{
list<int> lt;
lt.push_back(3);
lt.push_back(2);
lt.push_back(1);
lt.push_back(5);
print_list(lt);
//从小到大
lt.sort();
print_list(lt);
//从大到小
lt.reverse();
print_list(lt);
}
5°迭代器失效
//list迭代器失效
void test_list5()
{
list<int> lt;
lt.push_back(3);
lt.push_back(2);
lt.push_back(1);
lt.push_back(5);
lt.push_back(4);
lt.push_back(6);
//删除所有的偶数
list<int>::iterator it = lt.begin();
while (it != lt.end())
{
if (*it % 2 == 0)
{
it = lt.erase(it);//erase后这个位置就一句失效了 需要拿it接收
//cout << *it << endl;//失效后解引用就直接报错 所以要拿it接收
}
else
{
++it;
}
}
print_list(lt);
//lt.erase(it);//这个没有问题 it失效了 没有用it 所以没有事
//*it//再用it 就有问题
//迭代器失效了没有什么事 但访问失效的迭代器就会有问题
//迭代器失效的总结:
//1.vector的iterator insert(push_back) erase都会导致失效
//2.list的iterator erase会导致失效
}
3.底层实现
底层是带头的双向循环链表
1°构造
namespace szh
{
//链表相关数据
template<class T>
struct __list_node
{
__list_node<T>* _next;
__list_node<T>* _prev;
T _data;
//构造
__list_node(const T& x = T())//全缺省 构造传参
:_data(x)
,_next(nullptr)
,_prev(nullptr)
{}
};
//链表类
template<class T>
class list
{
typedef __list_node<T> Node;
public:
//带头双向循环链表
//构造
list()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
private:
Node* _head;
};
}
- 先定义一个struct结构体 next prev data
- typedef一下 后面方便new空间
2°析构
namespace szh
{
//链表相关数据
template<class T>
struct __list_node
{
__list_node<T>* _next;
__list_node<T>* _prev;
T _data;
//构造
__list_node(const T& x = T())//全缺省 构造传参
:_data(x)
,_next(nullptr)
,_prev(nullptr)
{}
};
//链表类
template<class T>
class list
{
typedef __list_node<T> Node;
public:
//删数据
void clear()
{
iterator it = begin();
while (it != end())
{
erase(it++);//一个一个删数据
}
}
//析构 整个都不要 头结点直接干掉
~list()
{
clear();
delete _head;//头节点也删除
_head = nullptr;//制空
}
private:
Node* _head;
};
}
- 利用迭代器遍历+erase
3°拷贝构造
namespace szh
{
//链表相关数据
template<class T>
struct __list_node
{
__list_node<T>* _next;
__list_node<T>* _prev;
T _data;
//构造
__list_node(const T& x = T())//全缺省 构造传参
:_data(x)
,_next(nullptr)
,_prev(nullptr)
{}
};
//链表类
template<class T>
class list
{
typedef __list_node<T> Node;
public:
//拷贝构造 lt2(lt1)
list(const list<T>& lt)
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
类里面不需要指定迭代器类型
利用迭代器进行尾插
//const_iterator it = lt.begin();
//while (it != lt.end())
//{
// //this->pop_back(*it);
// push_back(*it);
// ++it;
//}
//范围for更简洁
for (auto e : lt)
push_back(e);
}
private:
Node* _head;
};
}
- 迭代器遍历或者范围for遍历
4°赋值
namespace szh
{
//链表相关数据
template<class T>
struct __list_node
{
__list_node<T>* _next;
__list_node<T>* _prev;
T _data;
//构造
__list_node(const T& x = T())//全缺省 构造传参
:_data(x)
,_next(nullptr)
,_prev(nullptr)
{}
};
//链表类
template<class T>
class list
{
typedef __list_node<T> Node;
public:
赋值 lt1=lt3
//list<T>& operator=(const list<T>& lt)
//{
// if (this != <)//不同
// {
// clear();//先释放this相关内容
// for (auto e : lt)//lt3的数据插入lt1
// push_back(e);
// }
// return *this;
//}
//赋值现代写法
//lt1=lt3
list<T>& operator=(list<T> lt)
{
swap(_head, lt._head);//直接交换
return *this;
}
//lt出了作用域 析构函数销毁第一个链表 也就是lt1原来的链表节点
private:
Node* _head;
};
}
- 第一种:先释放this 然后范围for+尾插
- 第二种:直接交换
5°push_back/insert
namespace szh
{
//链表相关数据
template<class T>
struct __list_node
{
__list_node<T>* _next;
__list_node<T>* _prev;
T _data;
//构造
__list_node(const T& x = T())//全缺省 构造传参
:_data(x)
,_next(nullptr)
,_prev(nullptr)
{}
};
//链表类
template<class T>
class list
{
typedef __list_node<T> Node;
public:
//尾插
void push_back(const T& x)
{
//只有一个头部再尾插 头部也是尾部
//有多个节点
//两者逻辑相同
//结构设计的优势 有没有数据插入的逻辑都是一样的
Node* tail = _head->_prev;//找尾部
Node* newnode = new Node(x);
//变成newnode tail head三者的连接
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
//四个指针的连接
//tail的next newnode的prev newnode的next head的prev
//insert(end(), x);
}
void insert(iterator pos, const T& x)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
//prev newnode cur连接
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
}
private:
Node* _head;
};
}
- 注意四个节点的连接
6°pop_back/erase
namespace szh
{
//链表相关数据
template<class T>
struct __list_node
{
__list_node<T>* _next;
__list_node<T>* _prev;
T _data;
//构造
__list_node(const T& x = T())//全缺省 构造传参
:_data(x)
,_next(nullptr)
,_prev(nullptr)
{}
};
//链表类
template<class T>
class list
{
typedef __list_node<T> Node;
public:
void pop_back()
{
//erase(iterator(_head->_prev));
erase(--end());
}
void erase(iterator pos)
{
assert(pos != end());
Node* cur = pos._node;//.对象
Node* prev = cur->_prev;
Node* next = cur->_next;
delete cur;
//delete[] cur;
prev->_next = next;
next->_prev = prev;
}
private:
Node* _head;
};
}
- pos位置前后的连接
7°push_front/pop_front
namespace szh
{
//链表相关数据
template<class T>
struct __list_node
{
__list_node<T>* _next;
__list_node<T>* _prev;
T _data;
//构造
__list_node(const T& x = T())//全缺省 构造传参
:_data(x)
,_next(nullptr)
,_prev(nullptr)
{}
};
//链表类
template<class T>
class list
{
typedef __list_node<T> Node;
public:
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_front()
{
erase(begin());
}
private:
Node* _head;
};
}
- 复用insert和erase
8°迭代器
namespace szh
{
//链表相关数据
template<class T>
struct __list_node
{
__list_node<T>* _next;
__list_node<T>* _prev;
T _data;
//构造
__list_node(const T& x = T())//全缺省 构造传参
:_data(x)
,_next(nullptr)
,_prev(nullptr)
{}
};
//模拟迭代器
//__list_iterator<T, T&, T*> -> iterator
//__list_iterator<T, const T&, const T*> -> const_iterator
template<class T, class Ref, class Ptr>
struct __list_iterator
{
//list类型
typedef __list_node<T> Node;
typedef __list_iterator<T, Ref, Ptr> Self;//增加模板参数 //__list_iterator<T, const T&, const T*> -> const_iterator
//定义一个节点
Node* _node;
//初始化
__list_iterator(Node* node)
:_node(node)//初始化节点
{}
//*it
Ref operator*()//T&->Ref
{
return _node->_data;
}
Ptr operator->()//T*->Ptr
{
return &_node->_data;
}
//++it 返回类型应该是迭代器
Self operator++()
{
_node = _node->_next;
//++其实是往下走一个节点
return *this;//返回++之后的值
}
//it++ 返回++之前的值
Self operator++(int)
{
Self tmp(*this);
//_node = _node->_prev;
++(*this);
return tmp;
}
Self operator--()
{
_node = _node->_prev;
return *this;
}
Self operator--(int)
{
Self tmp(*this);
//_node = _node->_prev;
--(*this);
return tmp;
}
//it!=end()
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;
//const_iterator是如何实现的
typedef __list_iterator<T, const T&, const T*> const_iterator;
iterator begin()//第二个位置
{
return iterator(_head->_next);
}
iterator end()//第一个位置
{
return iterator(_head);
}
const_iterator begin() const //第二个位置
{
return const_iterator(_head->_next);
}
const_iterator end() const//第一个位置
{
return const_iterator(_head);
}
private:
Node* _head;
};
}
- T T& T* ->T Ref Ptr 三个模板参数 对应传参
9°测试
namespace szh
{
void test_list1()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
list<int>::iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
struct Date
{
int _year = 0;
int _month = 1;
int _day = 1;
};
void test_list2()
{
模拟指针的行为
//int* p1 = new int;
//*p1;
//Date* p2 = new Date;
//*p2;
//p2->_year;//这样访问
list<Date> lt;
lt.push_back(Date());
lt.push_back(Date());
list<Date>::iterator it = lt.begin();
while (it != lt.end())
{
//cout << *it << " ";//*it 节点里面的数据 Date 不支持输出
cout << it->_year << "-" << it->_month << "-" << it->_day << endl;//需要重载operator-> 本质是it->->_year 但是为了可读性 编译器特殊处理了一下
cout << (*it)._year << "-" << (*it)._month << "-" << (*it)._day << endl;//拿到对象后再访问year month day 这种方式不需要重载operator->
++it;
}
cout << endl;
}
void print_list(const list<int>& lt)
{
list<int>::const_iterator it = lt.begin();//const调非const begin的this需要const
while (it != lt.end())
{
//*it = 1;//可改 应该用const迭代器 所以begin和end应该有两个版本
cout << *it << " ";
++it;
}
cout << endl;
}
void test_list3()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
print_list(lt);
}
void test_list4()
{
list<int> lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
lt1.push_back(4);
list<int> lt2(lt1);//深浅拷贝问题
print_list(lt2);//lt2先析构 lt1再次析构 程序崩溃 所以要自己实现拷贝构造
list<int> lt3;
lt3.push_back(10);
lt3.push_back(20);
lt3.push_back(30);
lt3.push_back(40);
lt1 = lt3;
print_list(lt1);
}
}
#include "list.h"
int main()
{
//szh::test_list1();
//szh::test_list2();
//szh::test_list3();
//szh::test_list4();
return 0;
}
【C++】9.list 完