迭代器:
1. 迭代器概念
- 起始迭代器(红色): 遍历链表
- 终止迭代器(蓝色): 结束标志
2. 迭代类的分类
- STL标准模板库中,每一种迭代器都有4种类
2.1 正向非常迭代类
2.1.1 起始迭代器、终止迭代器
2.1.2 迭代器指定位置添加、删除节点
2.2 正向常迭代类
常迭代器本省没有常属性,迭代器可以改,可以++,--做链表遍历, 但是不能通过常迭代器更改链表中的节点。
和非常迭代器的区别就是:所有通过迭代器对链表进行改操作的路全被堵上。
所以对于常迭代类对象不会提供insert()和erase()函数
3. 查找功能
IT 就是迭代器类,查找不成功返回第二个参数(迭代器)
4 排序功能
迭代器i 和 j 是给的排序范围
迭代器p作为基准与 迭代器i 和 迭代器j 范围内的节点做顺序调整, p在整个快速排序过程中和 i或j相同。基准就是本次排序针对的是哪个节点
循环: 对p和i(j) 做一次判断可能会调整顺序,然后i++ 或者 j-- 缩小需要顺序调整的范围, 然后把p置为 i或j, 直到p, i, j 三个迭代器相同, 也就找到了基准在链表中的正确位置。
然后换基准,再重复上述过程,也就是递归。
自制链表容器:
#include <stdexcept>
#include <iostream>
using namespace std;
// list<T>代表双向链表类,链表里的每个节点是T类型的数据
template <class T> class list {
public:
// 缺省构造, 空链表
list():m_head(NULL), m_tail(NULL){}
// 拷贝构造, 用一个存在的链表拷贝构造一个新的链表(自己实现的拷贝构造都是深拷贝, 编译器提供的缺省都是浅拷贝)
list(const list & that):m_head(NULL), m_tail(NULL){
for(node* p_node = that.m_head; p_node; p_node = p_node->m_next){
push_back(p_node->m_data);
}
}
// 析构函数
~list(){
clear();
}
// 链表判空
bool empty(){
return m_head == NULL && m_tail == NULL;
}
// 添加头结点
void push_front(T const& data){
/*
// 不考虑特殊情况代码如下
// 1. 创建一个节点,前指针为NULL, 后指针指向头节点
m_head = new node(NULL, data, m_head);
// 2. 新头节点的后一个节点的前指针指向投头节点
m_head->m_next->m_prev = m_head;
*/
m_head = new node(NULL, data, m_head);
// 2. 不能保证新new的节点有下一个节点
if (m_head->m_next){
// 空指针使用 ->m_next, 会有段错误
m_head->m_next->m_prev = m_head;
} else {
// 3. 如果没有, 现在添加的这个新节点既是头节点也是尾节点
m_tail = m_head;
}
}
// 删除头结点
void pop_front(){
/*
不考虑特殊情况代码如下:
// 1. 备份头节点的下一个节点,然后删掉头节点
node* p_node = m_head->m_next;
delete m_head;
// 2. 把这个节点的前指针指向空
p_node->m_prev = NULL;
// 3. 再把这个节点设为头节点
m_head = p_node;
*/
// 防止用户调用pop时没有做非空链表判断
if(empty()){
return ;
}
node* p_node = m_head->m_next;
delete m_head;
// 没法保证头节点还有下一个节点
// 2. 如果有,把它设置为头节点
if(p_node){ // 空指针使用 ->m_prev 会有段错误
p_node->m_prev = NULL;
} else {
// 3. 如果没有,那链表现在就成空链表了,把m_head和m_tail都置为NULL
m_tail = NULL;
}
m_head = p_node;
}
// 获取头结点元素 (这里用的引用,所以可以通过返回的引用去更改头结点的数据)
T& front(){
if(empty()){
throw underflow_error("null node");
}
return m_head->m_data;
}
// 下面写一个重载版本
// C++中,只要常对象只能使用常函数
// 一个常链表对象(任何节点不能被更改)只能调下面的函数,下面返回的也是一个const类型的引用,不能被修改
T const& front()const{
// 常对象调常函数,常函数把常对象的常属性再去调用非常函数, 然后再返回值用常引用接收
return const_cast<list *>(this)->front();
}
// 添加尾结点
void push_back(T const& data){
m_tail = new node(m_tail, data, NULL);
if(m_tail->m_prev){
m_tail->m_prev->m_next = m_tail;
} else {
m_head = m_tail;
}
}
// 删除尾结点
void pop_back(){
if(empty()){
return;
}
node *p_node = m_tail-> m_prev;
delete m_tail;
if(p_node){
p_node->m_next = NULL;
} else {
m_head = NULL;
}
m_tail = p_node;
}
// 获取尾节点元素
T& back(){
if(empty()){
throw underflow_error("null node");
}
return m_tail->m_data;
}
// 获取尾节点元素(重载版本)
T const& back()const{
return const_cast<list *>(this)->back();
}
// 清空链表
void clear(){
while (empty())
{
pop_front();
}
}
// 获取链表大小
size_t size(){
size_t i = 0;
for(node* p_node = m_head; p_node; p_node = p_node->m_next){
i++;
}
return i;
}
private:
// 节点类(模板嵌套)
class node {
public:
node(node* prev, T const& data, node* next):m_prev(prev), m_data(data), m_next(next){}
node* m_prev; // 前指针
T m_data; // 节点数据
node* m_next; // 后指针
};
public:
/// 正向非常迭代类(模板嵌套) ///
class iterator{
public:
iterator(node* start, node* cur, node* end):m_start(start), m_cur(cur), m_end(end){}
T& operator*(){
if(m_cur == NULL){
throw underflow_error("null node");
}
return m_cur->m_data;
}
iterator& operator++(){
if(m_cur == NULL){// 当迭代器m_cur指向尾节点的下一个节点(NULL)
// 这里做一个循环迭代
m_cur = m_start;
} else {
// 迭代器的m_cur指向下一个节点,然后返回迭代器本身
m_cur = m_cur->m_next;
}
return *this;
}
iterator& operator--(){
if(m_cur == NULL){ // 当迭代器m_cur指向头节点的前一个节点(NULL)
m_cur = m_end;
} else {
m_cur = m_cur->m_prev;
}
return *this;
}
// 怕左操作数是常对象,这里搞成常函数
bool operator==(iterator const& that)const{
return m_start == that.m_start && m_cur == that.m_cur && m_end == that.m_end;
}
bool operator!=(iterator const& that)const{
// return m_start =! that.m_start || m_cur != that.m_cur || m_end != that.m_end;
// 但是下面这样写更优雅
return !(*this==that); //复用上面的==重载
}
private:
node* m_start; // 开始指向
node* m_cur; // 当前指向
node* m_end; // 终止指向
friend class list; // 在list类中可以使用 iterator 的私有成员
};
// 获取起始迭代器对象(红色)遍历链表
iterator begin(){
return iterator(m_head, m_head, m_tail);
}
// 获取终止迭代器对象(蓝色)结束标志
iterator end(){
return iterator(m_head, NULL, m_tail);
}
// 迭代器指向位置前方添加新节点
void insert(iterator const& loc, T const& data){
/*
// 不考虑极限情况,代码如下
// 1. 创建一个节点,前指针指向迭代器当前指向节点, 后指针指向迭代器当前指向的节点
node* p_node = new node(loc.m_cur->m_prev, data, loc.m_cur);
// 2. 完成添加新节点的步骤(双向)
p_node->m_prev->m_next = p_node;
p_node->m_next->m_prev = p_node;
*/
// 极限情况,loc迭代器指向无效节点,尾节点的下一个位置, 添加新节点就是添加尾节点
if(loc == end()){
push_back(data);
} else { // loc指向有效节点
node* p_node = new node(loc.m_cur->m_prev, data, loc.m_cur);
// 但不能保证新new的节点一定有前一个节点, 但是可以保证新new的节点一定有后一个节点 loc.m_cur(之前做过判断是有效节点)
if(p_node->m_prev){ // 如果有前一个节点
p_node->m_prev->m_next = p_node;
} else { // 如果没有, 说明p_node是将要加入的头节点
m_head = p_node;
}
p_node->m_next->m_prev = p_node;
}
}
// 删除迭代器指向的节点
void erase(iterator const& loc){
/*
// 不考虑极限情况
// 1. 架空 p_del, 然后delete p_del
p_del->m_prev->m_next = p_del->m_next;
p_del->m_next->m_prev = p_del->m_prev;
delete p_del;
*/
if(loc == end()){ // 如果loc指向无效节点
return;
}
node *p_del = loc.m_cur;
// 没有办法保证 p_del 一定有前一个节点(比如p_del是头节点)
if (p_del->m_prev) {
p_del->m_prev->m_next = p_del->m_next;
} else {
m_head = p_del->m_next;
}
// 没有办法保证 p_del 一定有下一个节点(比如p_del是尾节点)
if(p_del->m_next){
p_del->m_next->m_prev = p_del->m_prev;
} else {
m_tail = p_del->m_prev;
}
delete p_del;
}
/// 正向常迭代类(模板嵌套) ///
class const_iterator{
public:
const_iterator(iterator const& it):m_it(it){}
T const& operator*(){
// 触发 iterator的*操作符重载,然后用常引用接收
return *m_it;
}
const_iterator& operator++(){
++m_it;
return *this;
}
const_iterator& operator--(){
--m_it;
return *this;
}
bool operator==(const_iterator const & that)const{
// m_it是iterator类对象,这里会触发 iterator 的 == 操作符重载
return m_it==that.m_it;
}
bool operator!=(const_iterator const & that)const{
// 触发 const_iterator 的 == 操作符重载
// 然后再触发 iterator 的 == 操作符重载
return !(*this==that);
}
private:
iterator m_it;
};
// 获取起始常迭代器
// 这里的const_iterator begin const()和 iterator begin() 构成重载
// 如果是list类的常对象就调用下面这个begin()
const_iterator begin() const{
return iterator(m_head, m_head, m_tail);
}
// 获取终止常迭代器
const_iterator end() const{
return iterator(m_head, NULL, m_tail);
}
node* m_head; // 链表头
node* m_tail; // 链表尾
};
// 查找(利用"=="),放在了全局域. 是因为STL把容器的查找也放在了全局域
// b这个迭代器不参与查找, 返回值是迭代器b说明查找失败
template<class IT, class T>IT find(IT const& a, IT const& b, T const& data){
for(IT it=a; it!=b; ++it){
if(*it==data){
return it;
}
}
return b;
}
// 排序("<")
template <class IT> void sort(IT const& begin, IT const& end){
IT p = begin; // 基准
IT last = end;
--last; // 排序不包含迭代器end指向的节点
// 找到p指向的节点在链表中的正确位置
for(IT i = begin, j = last; i != j;){ // 最后一句写到for循环里也可以
while (i!=p && *i<*p)
{
++i;
}
if(i!=p){
swap(*i, *p);
p = i;
}
while (j!=p && *p<*j)
{
--j;
}
if(j!=p){
swap(*p, *j);
p = j;
}
}
// 递归
// 基准节点 前面或者后面 至少还有两个节点才能参与sort
IT it = begin;
++it;
if(p!=begin && p!= it){
// 迭代
sort(begin, p); // p不参与排序
}
it = p;
++it;
if(it!=end && it!=last){
sort(it, end);
}
}
// 排序("比较器")
template <class IT, class CMP>
void sort(IT const& begin, IT const& end, CMP cmp){
IT p = begin; // 基准
IT last = end;
--last;
for(IT i = begin, j = last; i != j;){
while (i!=p && cmp(*i, *p)) // cmp 是对象,对象后面加操作符,触发操作符重载
{
++i;
}
if(i!=p){
swap(*i, *p);
p = i;
}
while (j!=p && cmp(*p, *j))
{
--j;
}
if(j!=p){
swap(*p, *j);
p = j;
}
}
// 递归
// 基准节点 前面或者后面 至少还有两个节点才能参与sort
IT it = begin;
++it;
if(p!=begin && p!= it){
// 迭代
sort(begin, p ,cmp); // p不参与排序
}
it = p;
++it;
if(it!=end && it!=last){
sort(it, end, cmp);
}
}
// 以上代码模拟容器
// -----------------------
// 以下代码模拟普通用户
void print(string const & word, list<int>& l){
cout << word << endl;
typedef list<int>::iterator IT;
// it就是 正向非常迭代类对象
for(IT it=l.begin(); it != l.end(); ++it){
cout <<*it << " ";
}
cout << endl << "----------------" << endl;
}
// 比较器类
class CMP{
public:
bool operator()(int const& a, int const& b){
return a > b; // > 降序排序,< 升序排序
}
};
int main() {
list<int> ls;
for(int i=0; i<5; i++){
ls.push_front(i);
}
for(int i=0; i<5; i++){
ls.push_back(i+10);
}
print("添加节点后: ", ls);
ls.pop_front();
ls.pop_back();
print("删除头尾节点以后: ", ls);
// 利用正向非常迭代器
ls.insert(++ls.begin(), 1000); // 增
print("在迭代器指定的位置添加节点以后: ", ls);
ls.erase(ls.begin()); // 删
print("删除迭代器指向的节点以后: ", ls);
typedef list<int>::iterator IT;
IT it = ls.begin();
*it = 800; // 改
print("更改迭代器指向的节点以后: ", ls);
IT fit = find(ls.begin(), ls.end(), 10); // 查
if(fit != ls.end()){
ls.erase(fit);
}
print("找到10并删除后: ", ls);
sort(ls.begin(), ls.end()); // 排
print("升序排序后: ", ls);
CMP cmp; // 比较器, 自己决定排序方式
sort(ls.begin(), ls.end(), cmp);
print("降序排序后: ", ls);
// 利用正向常迭代器(但用的并不多)
// 常属性链表,定义好就不能再修改了,这里用拷贝构造赋初值
const list<int>c_ls(ls);
typedef list<int>::const_iterator C_IT;
// // 这里的c_ls.begin()调的也是常属性重载版本
for(C_IT c_it = c_ls.begin(); c_it != c_ls.end(); ++c_it){
cout << *c_it << ' ';
}
cout << endl <<"----------------" << endl;
// C_IT c_it = c_ls.begin();
// *c_it = 100; // 编译Error, 不能通过常迭代器修改链表的节点
return 0;
}
}