list
的使用我们
list
表示带头双向循环链表。
我们再找我们的list中每个数据的位置,就不可以再用我们的下标+[]
了。
我们就必须用迭代器。
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;
list<int>::iterator found = find(lt.begin(), lt.end(), 3);
lt.insert(found, 20);
for (auto n : lt)
{
cout << n << " ";
}
cout << endl;
}
int main()
{
test_list1();
return 0;
}
sort
的使用我们库里面有
sort
,可以给我们的容器使用,那为什么我们链表不用我们库里的sort
,而自己有自己的sort
。
是因为他用不了库里面的sort
。
理论而言,模板的语法上可以传任意类型的参数,但是内部使用迭代器是由要求的,名字会暗示你要传随机迭代器
其他的用法基本跟string和vector相似,我们大家举一反三就可以。
就是我们链表的结点的内容,我们用一个类来表示
需要注意的就是我们用struct表示,因为他默认是公开的,我们之后都很一直要用它,设置成私有我们访问不了很麻烦
template<class T>
struct list_node
{
T _val;
list_node<T>* _next;
list_node<T>* _prev;
};
我们list成员变量,已经我们怎么初始化
template<class T>
class list
{
public:
//构造函数,进行初始化
list()
{
//先创建一个哨兵位的头结点,
//构成最简单的带头双向循环链表
_head = new list_node;
_head->_next = _head;
_head->_prev = _head;
}
private:
//成员变量
//链表的头结点。
list_node* _head;
};
我上面的内容其实是有点错误的。
我们要想知道错误的点是什么,我们就明白类名,和类型的区别。
template<class T>
class list
{
public:
list()
{
_head = new list_node<T>;
_head->_next = _head;
_head->_prev = _head;
}
private:
list_node<T>* _head;
};
但是我们为了美观、易读和我们以后操作方便,我们将这个类型重命名一下。
template<class T>
class list
{
typedef list_node<T> node;
public:
list()
{
_head = new node;
_head->_next = _head;
_head->_prev = _head;
}
private:
node* _head;
};
跟我们的链表尾插的功能基本上是一样的。
void push_back(const T& x)
{
//创建一个新结点,我们再new的时候走的构造函数。
// 我们可以这样理解,node(x)是一个匿名对象。
//所以我们将其 类list_node 写一个构造函数。
node* newnode = new node(x);
//找到尾
node* tail = _head->_prev;
//插入
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
}
我们将list_node的构造函数补上。
template<class T>
struct list_node
{
T _val;
list_node<T>* _next;
list_node<T>* _prev;
//默认构造
list_node(const T& x =T())
:_next(nullptr)
,_prev(nullptr)
,_val(x)
{}
};
insert
头插,尾插对于list插入来说,并不会导致迭代器的失效
//pos位置之前
void insert(iterator pos, const T& x)
{
node* newnode = new node(x);
node* cur = pos._node;
node* prev = cur->_prev;
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
}
当有了
insert
,list
的头插和为尾插我们就可以很好的解决
//尾插
void push_back(const T& x)
{
insert(end(), x);
}
//头插
void push_front(const T& x)
{
insert(begin(), x);
}
erase
头删 尾删他会导致迭代器失效,因为迭代器指向的位置没了
iterator erase(iterator pos)
{
assert(pos != end());
node* next = pos._node->_next;
node* prev = pos._node->_prev;
prev->_next = next;
next->_prev = prev;
delete pos._node;
return iterator(next);
}
当有了erase
,list
的头删和为尾删我们就可以很好的解决
//头删
void pop_front(const T& x)
{
erase(begin());
}
//尾删
void pop_back(const T& x)
{
erase(--end());
}
我们在进行析构,我们先写一个函数将我们的结点清除。但是不清除头结点。
在删除的时候会迭代器失效,所以我们在erase加一个返回值解决迭代器失效的问题。返回下一个结点的位置
void clear()
{
iterator it = begin();
while (it != end())
{
//防止迭代器失效,增加返回值
it = erase(it);
}
}
析构就是将我们的头结点也删除掉
~list()
{
clear();
delete _head;
_head = nullptr;
}
就是用迭代器来构造我们的list
//迭代器区间的构造函数
template<class Iterator>
list(Iterator first, Iterator last)
{
//我们没有头结点,就不能push_back
_head = new node;
_head->_next = _head;
_head->_prev = _head;
while (first != last)
{
push_back(*first);
first++;
}
}
list(const list<T>& lt)
{
_head = new node;
_head->_next = _head;
_head->_prev = _head;
for (auto e : lt)
{
push_back(e);
}
}
另一种拷贝构造的写法,比较现代的写法
void swap(list<T>& tmp)
{
std::swap(_head, tmp._head);
}
list(const list<T>& lt)
{
empty_init();
list<T> tmp(lt.begin(), lt.end());
swap(tmp);
}
比较现代的写法
void swap(list<T>& tmp)
{
std::swap(_head, tmp._head);
}
list<T>& operator = (list<T> lt)
{
swap(lt);
return *this;
}
对于我们字符串和数组的迭代器,我们可以认为一个指针,因为他们的空间连续,所以我们对指针加加,我们就可以将他们遍历一遍。
但是对于链表,它的每一个结点都是一个类。
我们想将它遍历一遍,它的空间也不连续,我们对指针加加也不能指向下一个空间。
大佬将迭代器设为一个类。我们通过将其封装能够让我们找到下一个结点的位置。下面我们就看看怎么封装的。
1.迭代器要么是原生指针
2.迭代器要么就是自定义类型对原生指针的封装,模拟指针的行为。
首先我们要知道我们迭代器要有什么,并且要实现什么功能。
功能:我们能够遍历链表,并且能够将链表的值返回出来。
实现:就可以用操作符重载,将++
和*
重载一下。
++
找到链表的下一个位置,
*
返回链表的值。
!=
,再遍历最后我们要停下来就要进行比较。
template<class T>
//因为迭代器也是公开的所以我们用struct
struct __list_iterator
{
//重命名我们如果想要修改的化就修改重命名的内容
//而且看着很容易
typedef __list_iterator<T> iterator;
typedef list_node<T> node;
//用结点的指针构造一个迭代器
node* _node;
//迭代器的构造函数
__list_iterator(node* n)
:_node(n)
{}
T& operator*()
{
return _node->_val;
}
iterator& operator++()
{
_node= _node->_next;
return *this;
}
//后置加加
iterator operator++(int)
{
iterator tmp(_node);
_node = _node->_next;
return tmp;
}
//前置--
iterator& operator--()
{
_node = _node->_prev;
return *this;
}
//后置减减
iterator operator--(int)
{
iterator tmp(_node);
_node = _node->_prev;
return tmp;
}
bool operator!=(const iterator& s)
{
return _node != s._node;
}
bool operator==(const iterator& s)
{
return _node == s._node;
}
};
这时,我们将迭代器的类写完了,我们就可以用迭代器。
template<class T>
class list
{
typedef list_node<T> node;
//将类的迭代器重命名一下,方便我们的使用
typedef __list_iterator<T> iterator;
iterator begin()
{
//构建一个迭代器返回
//第一种方法
//iteartor it(_head->next);
//return it;
//第二种方法用匿名对象
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
public:
list()
{
_head = new node;
_head->_next = _head;
_head->_prev = _head;
}
private:
node* _head;
};
我们封装的作用就体现出来了,让我们的操作跟前面vector一样。
要有const迭代器,就再写一个const迭代器的类。如下面代码
template<class T>
//因为迭代器也是公开的所以我们用struct
struct __list_const_iterator
{
//重命名我们如果想要修改的化就修改重命名的内容
//而且看着很容易
typedef __list_const_iterator<T> iterator;
typedef list_node<T> node;
//用结点的指针构造一个迭代器
node* _node;
//迭代器的构造函数
__list_const_iterator(node* n)
:_node(n)
{}
const T& operator*()
{
return _node->_val;
}
//前置加加
iterator& operator++()
{
_node = _node->_next;
return *this;
}
//后置加加
iterator operator++(int)
{
iterator tmp(_node);
_node = _node->_next;
return tmp;
}
//前置--
iterator& operator--()
{
_node = _node->_prev;
return *this;
}
//后置减减
iterator operator--(int)
{
iterator tmp(_node);
_node = _node->_prev;
return tmp;
}
bool operator!=(const iterator& s)
{
return _node != s._node;
}
bool operator==(const iterator& s)
{
return _node == s._node;
}
};
在类中用const迭代器
template<class T>
class list
{
typedef list_node<T> node;
public:
typedef __list_const_iterator<T> const_iterator;
const_iterator begin() const
{
return const_iterator(_head->_next);
}
const_iterator end()const
{
return const_iterator(_head);
}
list()
{
_head = new node;
_head->_next = _head;
_head->_prev = _head;
}
private:
node* _head;
};
我们要注意const修饰的对象是谁,有的修饰this指针,有的修饰迭代器,不一样,这些小细节,错一个都不行。
我们提一个问题,能不能在typedef中进行这样的操作,然后就不用写const迭代器呢。
typedef __list_iterator<T> iterator;
typedef const iterator const_iterator;
//就是将我们的正向迭代器前加一个const表示我们的const迭代器,
//既然我们后面没有用这种方法,肯定不行,至于为什们大家必须想明白
const int * p = &i
,const修饰的是 *p
,p指向的内容(*p)不可修改,但是p却可以修改。
int * const p = &i
,const修饰的对象是 p
,p不可以修改,但是p指向的内容(*p)可以修改。
typedef const __list_iterator const_iterator;
_node = _node->_next;
也就不可以++了。所以它可以类比成==> T *const。
但是我们发现,我们的正向迭代器和const正向迭代器,好像基本一样,就是引用返回的地方不一样,那为什么不合在一块呢?
既然我们就几个地方不一样,那么我们再用模板的是再多一个参数,让编译器自己做类的实例化,我们相似的代码写一份就好了呀。
//增加一个模板参数
template<class T,class Ref>
struct __list_iterator
{
typedef __list_iterator<T,Ref> iterator;
typedef list_node<T> node;
node* _node;
__list_iterator(node* n)
:_node(n)
{}
Ref operator*()
{
return _node->_val;
}
iterator& operator++()
{
_node = _node->_next;
return *this;
}
iterator operator++(int)
{
iterator tmp(_node);
_node = _node->_next;
return tmp;
}
iterator& operator--()
{
_node = _node->_prev;
return *this;
}
iterator operator--(int)
{
iterator tmp(_node);
_node = _node->_prev;
return tmp;
}
bool operator!=(const iterator& s)
{
return _node != s._node;
}
bool operator==(const iterator& s)
{
return _node == s._node;
}
};
那我们在list类中怎么用它呢。
template<class T >
class list
{
typedef list_node<T> node;
public:
typedef __list_iterator<T, T&> iterator;
typedef __list_iterator<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);
}
list()
{
_head = new node;
_head->_next = _head;
_head->_prev = _head;
}
private:
node* _head;
};
只要我们再用迭代器的时候我们传两个参数,就可以很容易的简化代码,但是效率并不会提升,只是编译器帮我们做了这些工作。
如果有一个自定义类型,我们想去用这个自定义类型当我们的链表,并且我们还想要访问我们的自定义类型里的数据,我们怎么办。
struct AA
{
//默认构造
AA(int a1 = 0, int a2 = 0)
: _a1(a1)
,_a2(a2)
{}
int _a1;
int _a2;
};
void test_list2()
{
list<AA> lt;
lt.push_back(AA(1, 2));
lt.push_back(AA());
lt.push_back(AA(2, 1));
list<AA>::iterator it = lt.begin();
while (it != lt.end())
{
cout << (*it)._a1 << ":" << (*it)._a2 << endl;
it++;
}
cout << endl;
}
想访问AA中的元素,我们上面给了一种做法就是解引用然后再找,因为我们的迭代器中重载了解引用操作符,所以我们可以用。
我们知道迭代器是一个指针,指针直接用
->
不就可以了吗?
所以只需要重载->
就可以了。
->
T* operator->()
{
return &_node->_val;
}
list<AA>::iterator it = lt.begin();
while (it != lt.end())
{
cout << it->_a1 << ":" << it->_a2 << endl;
it++;
}
这样用了我们就可以使用
->
去访问自定义类型中的元素了。
->
?
->
。
->
, it->
表示AA类型的地址,不应该再用一个 ->
才能够指向 _a1
吗?
对于const迭代器的重载
->
,我们返回应该返回const T*
.
const T* operator->()
{
return &_node->_val;
}
所以我们可以在模板中再加一个参数,用来指这个。
我们有三个模板参数就是为了搞const迭代器
//增加一个模板参数
template<class T,class Ref,class Ptr>
struct __list_iterator
{
typedef __list_iterator<T,Ref,Ptr> iterator;
typedef list_node<T> node;
node* _node;
__list_iterator(node* n)
:_node(n)
{}
Ref operator*()
{
return _node->_val;
}
Ptr operator->()
{
return &_node->_val;
}
iterator& operator++()
{
_node = _node->_next;
return *this;
}
iterator operator++(int)
{
iterator tmp(_node);
_node = _node->_next;
return tmp;
}
iterator& operator--()
{
_node = _node->_prev;
return *this;
}
iterator operator--(int)
{
iterator tmp(_node);
_node = _node->_prev;
return tmp;
}
bool operator!=(const iterator& s)
{
return _node != s._node;
}
bool operator==(const iterator& s)
{
return _node == s._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;
//typedef __list_const_iterator const_iterator;
iterator begin()
{
//构建一个迭代器返回
//第一种方法
//iteartor it(_head->next);
//return it;
//第二种方法用匿名对象
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);
}
list()
{
_head = new node;
_head->_next = _head;
_head->_prev = _head;
}
private:
node* _head;
};
list.h
#pragma once
#include
namespace tongtong
{
template<class T>
struct list_node
{
T _val;
list_node<T>* _next;
list_node<T>* _prev;
list_node(const T& x =T())
:_next(nullptr)
,_prev(nullptr)
,_val(x)
{}
};
//template
因为迭代器也是公开的所以我们用struct
//struct __list_iterator
//{
// //重命名我们如果想要修改的化就修改重命名的内容
// //而且看着很容易
// typedef __list_iterator iterator;
// typedef list_node node;
// //用结点的指针构造一个迭代器
// node* _node;
// //迭代器的构造函数
// __list_iterator(node* n)
// :_node(n)
// {}
// T& operator*()
// {
// return _node->_val;
// }
// //前置加加
// iterator& operator++()
// {
// _node = _node->_next;
// return *this;
// }
// //后置加加
// iterator operator++(int)
// {
// iterator tmp(_node);
// _node = _node->_next;
// return tmp;
// }
// //前置--
// iterator& operator--()
// {
// _node = _node->_prev;
// return *this;
// }
// //后置减减
// iterator operator--(int)
// {
// iterator tmp(_node);
// _node = _node->_prev;
// return tmp;
// }
// bool operator!=(const iterator& s)
// {
// return _node != s._node;
// }
// bool operator==(const iterator& s)
// {
// return _node == s._node;
// }
//};
//template
//因为迭代器也是公开的所以我们用struct
//struct __list_const_iterator
//{
// //重命名我们如果想要修改的化就修改重命名的内容
// //而且看着很容易
// typedef __list_const_iterator iterator;
// typedef list_node node;
// //用结点的指针构造一个迭代器
// node* _node;
// //迭代器的构造函数
// __list_const_iterator(node* n)
// :_node(n)
// {}
// const T& operator*()
// {
// return _node->_val;
// }
// //前置加加
// iterator& operator++()
// {
// _node = _node->_next;
// return *this;
// }
// //后置加加
// iterator operator++(int)
// {
// iterator tmp(_node);
// _node = _node->_next;
// return tmp;
// }
// //前置--
// iterator& operator--()
// {
// _node = _node->_prev;
// return *this;
// }
// //后置减减
// iterator operator--(int)
// {
// iterator tmp(_node);
// _node = _node->_prev;
// return tmp;
// }
// bool operator!=(const iterator& s)
// {
// return _node != s._node;
// }
// bool operator==(const iterator& s)
// {
// return _node == s._node;
// }
//};
//增加一个模板参数
template<class T,class Ref,class Ptr>
struct __list_iterator
{
typedef __list_iterator<T,Ref,Ptr> iterator;
typedef list_node<T> node;
node* _node;
__list_iterator(node* n)
:_node(n)
{}
Ref operator*()
{
return _node->_val;
}
Ptr operator->()
{
return &_node->_val;
}
iterator& operator++()
{
_node = _node->_next;
return *this;
}
iterator operator++(int)
{
iterator tmp(_node);
_node = _node->_next;
return tmp;
}
iterator& operator--()
{
_node = _node->_prev;
return *this;
}
iterator operator--(int)
{
iterator tmp(_node);
_node = _node->_prev;
return tmp;
}
bool operator!=(const iterator& s)
{
return _node != s._node;
}
bool operator==(const iterator& s)
{
return _node == s._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;
//typedef __list_const_iterator const_iterator;
iterator begin()
{
//构建一个迭代器返回
//第一种方法
//iteartor it(_head->next);
//return it;
//第二种方法用匿名对象
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);
}
void empty_init()
{
_head = new node;
_head->_next = _head;
_head->_prev = _head;
}
list()
{
empty_init();
}
//迭代器区间的构造函数
template<class Iterator>
list(Iterator first, Iterator last)
{
//我们没有头结点,就不能push_back
empty_init();
while (first != last)
{
push_back(*first);
first++;
}
}
//list(const list& lt)
//{
// empty_init();
// for (auto e : lt)
// {
// push_back(e);
// }
//}
void swap(list<T>& tmp)
{
std::swap(_head, tmp._head);
}
list(const list<T>& lt)
{
empty_init();
list<T> tmp(lt.begin(), lt.end());
swap(tmp);
}
list<T>& operator = (list<T> lt)
{
swap(lt);
return *this;
}
//尾插
void push_back(const T& x)
{
//创建一个新结点,我们再new的时候走的构造函数。
// 我们可以这样理解,node(x)是一个匿名对象。
//所以我们将其 类list_node 写一个构造函数。
//node* newnode = new node(x);
找到尾
//node* tail = _head->_prev;
插入
//tail->_next = newnode;
//newnode->_prev = tail;
//newnode->_next = _head;
//_head->_prev = newnode;
insert(end(), x);
}
//头插
void push_front(const T& x)
{
insert(begin(), x);
}
//pos位置之前
void insert(iterator pos, const T& x)
{
node* newnode = new node(x);
node* cur = pos._node;
node* prev = cur->_prev;
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
}
iterator erase(iterator pos)
{
assert(pos != end());
node* next = pos._node->_next;
node* prev = pos._node->_prev;
prev->_next = next;
next->_prev = prev;
delete pos._node;
return iterator(next);
}
//头删
void pop_front(const T& x)
{
erase(begin());
}
//尾删
void pop_back(const T& x)
{
erase(--end());
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
//清理结点,但是不清理头结点
void clear()
{
iterator it = begin();
while (it != end())
{
//防止迭代器失效,增加返回值
it = erase(it);
}
}
private:
node* _head;
};
void print_list(const list<int>& lt)
{
list<int>::const_iterator cit = lt.begin();
while (cit != lt.end())
{
/*(*cit) *= 2;*/
cout << *cit << " ";
cit++;
}
cout << endl;
}
void test_list1()
{
list<int> 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;
//auto rit = lt.begin();
//rit++;
//lt.insert(rit, 20);
//for (auto c : lt)
//{
// cout << c << " ";
//}
//cout << endl;
//print_list(lt);
//lt.clear();
for (auto c : lt)
{
cout << c << " ";
}
cout << endl;
list<int> lt2 ;
lt2 = lt;
for (auto c : lt2)
{
cout << c << " ";
}
cout << endl;
for (auto c : lt)
{
cout << c << " ";
}
cout << endl;
}
struct AA
{
//默认构造
AA(int a1 = 0, int a2 = 0)
: _a1(a1)
,_a2(a2)
{}
int _a1;
int _a2;
};
void test_list2()
{
list<AA> lt;
lt.push_back(AA(1, 2));
lt.push_back(AA());
lt.push_back(AA(2, 1));
list<AA>::iterator it = lt.begin();
while (it != lt.end())
{
cout << it->_a1 << ":" << it->_a2 << endl;
it++;
}
cout << endl;
}
}
test.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
using namespace std;
#include"list.h"
int main()
{
tongtong::test_list1();
return 0;
}