使用STL vector 接口:
#include
#include
using namespace std;
void TestVector( )
{
vector v1;
v1.push_back( 1 );
v1.push_back( 2 );
v1.push_back( 3 );
v1.push_back( 4 );
PrintVector( v1 );
vector v2( 3, 10 );
PrintVector( v2 );
}
int main( )
{
TestVector( );
return 0;
}
//封装是为了复用,调用这个函数即可,不用每次都写打印代码
void PrintVector( vector& v1 )//引用 //加 const 编译不通过 如果要编译通过 1.普通迭代器(可读,可写) 2.const 迭代器(可读) 3.反向迭代器(反着遍历)
{
//迭代器 是 类似指针的一个对象 -> 和智能指针相似
//[begin, end) 迭代器是 左闭右开 区间
vector::iterator it/*迭代器对象*/; //如果用const_iterator 则 ++(*it) 会报错, 因为不能给常量赋值
it = v1.begin( );
while ( v1.end( ) != it )
{
//1.打印
cout << *it << " ";
++it;
//2.打印奇数
//if ( 0 != *it % 2 )
//{
// cout << *it << " ";
//}
/*++it;*/
//3.每个数加 1
//++(*it);
//++it;
}
cout << endl;
//反向迭代器 是 倒着走
vector::const_reverse_iterator rIt = v.rbegin( );
while ( v.rend() != rIt )
{
cout << *rIt << " ";
++rIt;
}
cout << endl;
}
#include
#include
using namespace std;
void TestList( );
void PrintList( list& l );
int main( )
{
TestList( );
return 0;
}
void TestList( )
{
list l1;
l1.push_back( 1 );
l1.push_back( 2 );
l1.push_back( 3 );
l1.push_back( 4 );
list l2( 5, 1 ); //带值的初始化
}
void PrintList( /*const*/list& l )
{
list::/*const_*/iterator it = l.begin( );
while ( l.end( ) != it )
{
cout << *it << " ";
/* *it = 10; *///如果是 const迭代器 则不能赋值
++it;
}
cout << endl;
}
:
//链表的四次实现
//1.C -> 基本实现和面试题
//2.C++ -> 面向对象 C 和 C++区别 深拷贝
//3.C++ -> 泛型编程 + T -- string
//4.数据结构 -- STL
//搜索 ctrl + f -> 快捷键查找F3 -> 倒着搜索 shift + F3
//名称 小写加下划线
#pragma once//防止重定义
#include "Iterator.h"//为了实现 List 反向迭代器
template
struct __ListNode
{
T _data;
__ListNode* _next;
__ListNode* _prev;
__ListNode( const T& x )
:_data( x )
,_next( NULL )
,_prev( NULL )
{}
};
//迭代器 是一个 类 通过 运算符重载 使它像指针一样使用
//T T& T* 有了 T 为什么 还有传 T&, T* ,为了实现 const迭代器
//T const T& const T*
template
struct __ListIterator // sizeof(__ListIterator) == 4 因为它里面还是一个指向节点的指针,只是通过封装1让它变为新的类型, 大小不变,为4
{
typedef __ListNode Node;
typedef __ListIterator Self;
typedef Ref Reference;//反向迭代器
typedef Ptr Pointer;//反向迭代器
__ListIterator( Node* node )
:_node( node )
{}
Ref operator*( )//1.出了作用域存在,所以传&。而且为了效率, 如果不传引用, string对象, 深拷贝。 2. 引用是为了可以更改数据
{
return _node->_data;
}
const Ref operator*( )//const 迭代器 中 用 const 修饰 Ref即可。
{
return _node->_data;
}
Ptr operator->( )
{
return &_node->_data;
}
Self& operator++( )//前置++ //注意后置 前置的区别
{
_node = _node->_next;
return *this;
}
Self operator++( int )//后置++ 不能给& 因为出了作用域对象不存在,不能给引用
{
Self tmp( _node );//构造 1
//Self tmp( *this );//拷贝构造 2
_node = _node->_next;
return tmp;
}// 1 or 2 时 构造 or 拷贝构造, 然后 return 时, 再拷贝构造个临时对象返回。
Self& operator--( )
{
_node = _node->_prev;
return *this;
}
Self operator--( int )
{
Node* cur = _node;
_node = _node->_prev;
return Self( cur );//先构造出一个对象,再拷贝构造出临时对象返回,但是他们是在返回步骤的一个表达式里,编译器会进行优化 -> 不会先构造再拷贝构造而是 直接构造临时对象 作为返回值, 所以 这里的代码 比 后置++实现的好.
}
bool operator!=( const Self& s ) const
{
return /*this->*/_node != s._node;//指针全是通过->调用,调用函数也是->。 对象全是 通过.调用
}
Node* _node;
};
template
class List
{
typedef __ListNode Node;
public:
typedef __ListIterator Iterator;
typedef __ListIterator ConstIterator;//实现 const 迭代器
typedef ReverseIterator ConstReverseIterator;//注意这一句和后一句顺序不能颠倒, 否则, 最后一句 的ReverseIterator 已经被 typedef 这里就用不了了
typedef ReverseIterator ReverseIterator;
//此时只能实现 List 的反向迭代器, Vector的反响迭代器不能这样实现, 需要类型萃取才可以!
Node* GetNode( const T& x )
{
return Node* node = new Node( x );
}
List( )
{
_head = GetNode( T( )/*匿名对象*/ );
_head->_next = _head;
_head->_prev = _head;
}
~List( )
{
Clear( );//清掉数据 。 but 析构还没完, 除了清掉数据还要把整个链表全销毁了
delete _head;
_head = NULL;
}
Iterator Begin( )//const对象不能调用 普通成员函数
{
return Iterator( _head->_next );
}
//不能这样写
//Iterator Begin( ) const
//{
// return Iterator( _head->_next );
//}
//const 成员函数 , 被 const 对象调用 --> 不可修改
//应该返回 const 迭代器
ConstIterator Begin( ) const
{
return ConstIterator( _head->_next );
}
Iterator End( )
{
return Iterator( _head );
}
ConstIterator End( ) const
{
return ConstIterator( _head );
}
ReverseIterator RBegin( )//如图, 正向迭代器和反向迭代器的 begin 和 end 完全对应起来! 也由此图理解解引用 --> 为什么取前一个位置数据。 Node* --> 正向迭代器 --> 反向迭代器
{
return ReverseIterator( End( ) );
}
ReverseIterator REnd( )
{
return ReverseIterator( Begin( ) );
}
ConstReverseIterator RBegin( ) const
{
return ConstReverseIterator( End( ) );
}
ConstReverseIterator REnd( ) const
{
return ConstReverseIterator( Begin( ) );
}
void PushBack( const T& x )
{
//我们以前实现方法
//Node* tail = _head->_prev;
//Node* tmp = GetNode( x );
//tail->_next = tmp;
//tmp->_prev = tail;
//tmp->_next = _head;
//_head->_prev = tmp;
//STL 实现方法
Insert( End( ), x );
}
void PushFront( const T& x )
{
Insert( Begin( ), x );
}
void PopBack( )
{
//assert( !Empty( ) );//不为空才行, 否则不让你删除
//Node* tail = _head->_prev;
//Node* prev = tail->_prev;
//
//delete tail;
//prev->_next = _head;
//_head->_prev = prev;
Erase( _head->_prev );
Erase( --End( ) );
}
void PopFront( )
{
Erase( Begin( ) );
}
void Clear( )
{
Iterator it = Begin( );//注意不用写为: List::Iterator 因为这是类里面, Iterator就是属于这个类域的
while ( End( ) != it )
{
Node* del = it._node; //注意 为. 而不是 ->
++it;
delete del;
}
_head->_next = _head; //别忘了 ,还需要重置
_head->_prev = _head;
}
Iterator Find( const T& x )
{
//Node* cur = _head->_next;
//while ( _head != cur )
//{
// if ( x == cur->_data )
// {
// return cur;//隐式类型转换, Iterator为单参数构造函数。 用 cur 构造临时对象发货
// //return Iterator( cur );
// }
// else
// {
// cur = cur->_next;
// }
// //如string s = "1111", 一个是c风格字符串, 一个是字符串, 即用了 隐式类型转换
// return _head;
//}
Iterator it = Begin( ); //成员函数可以调用其他成员函数 是因为有隐含this指针
while ( End( ) != it )
{
if ( x == *it )
{
return it;
}
else
{
++it;
}
}
return it; //这里返回的 是 End这个位置
}
//在 pos 的前面进行插入
void Insert( Iterator pos, const T& x )
{
assert( pos._node );//pos这个迭代器中的结点不能为空
Node* next = pos._node;
Node* prev = next->_prev;
Node* cur = new Node( x );
prev->_next = cur;
cur->_prev = prev;
cur->_next = next;
next->_prev = cur;
}
//测试考虑到:1。正常情况, 2.边界情况(头,尾,空)
//但这种情况会引起我们下面提出的问题,迭代器失效, 所以这种方案不好, 采用下面的方案
//void Erase( Iterator pos )
//{
// assert( (End( ) != pos) && (NULL != pos._node) );//等于 End( ) 相当于删除 头节点 --> 1.没有结点情况 2.给了非法的位置 ,如有多个结点,你却给了我头节点。
// Node* del = pos._node;
// Node* prev = del->_prev;
// Node* next = del->_next;
// delete del;
// prev->_next = next;
// next->_prev = prev;
//}
Iterator Erase( Iterator pos )
{
assert( (End( ) != pos) && (NULL != pos._node) );
Node* del = pos._node;
Node* prev = del->_prev;
Node* next = del->_next;
delete del;
prev->_next = next;
next->_prev = prev;
//解决迭代器失效方法, 返回 删除位置下一个位置的迭代器
return next; //单参数构造函数 进行 隐式转换
}
bool Empty( )
{
return _head == _head->_next;
}
//[)
//模板类中成员函数也可以给成 模板函数
template
void Assign( InputIterator first, InputIterator last )
{
Clear( );
while ( first != last )
{
PushBack( *first );
++first;
}
}
protected:
Node* _head;
};
struct AA
{
int _a1;
int _a2;
};
template
void PrintList( const List& l1 )//因为只是打印, 所以你不应该修改我 传const对象
{//const 对象 应该调用 const迭代器
List::Iterator it = l1.Begin( );//const对象 不能调用 非const 的成员函数 Begin( ) -> 加一个const的成员函数 Begin( )返回const 迭代器
//这里应该是 const 迭代器.
while ( l1.End( ) != it )
{
cout << *it << " ";
++it;
}
cout << endl;
}
template
void PrintList( const List& l1 )
{
List::Iterator it = l1.Begin( );//假设const对象调用const 成员函数,但返回普通迭代器
while ( l1.End( ) != it )
{
*it = 10;//const 对象, 理论上里面数据不能修改, 但是这里可以修改, 这不合理。 所以 把普通迭代器 -> const 迭代器 :
//此时(const迭代器)编译报错, 不能给常量赋值
cout << *it << " ";
++it;
}
cout << endl;
}
void TestList( )
{
List l1;
l1.PushBack( 1 );
l1.PushBack( 2 );
l1.PushBack( 3 );
l1.PushBack( 4 );
//迭代器
List::Iterator it = l1.Begin( );拷贝构造 -> 这里是浅拷贝(没毛病!)
while ( l1.End( ) != it )//it != l1.End( ) -> it.operator!=( &it/*传 this 指针*/, l.End( ) ) 但是像我那样写可以吗?
{//判断条件若为小于 or 不等于 则是 左闭右开 区间
cout << *it << " ";
++it;
}
cout << endl;
List l2;
l2.PushBack( AA( ) );
l2.PushBack( AA( ) );
List::Iterator it2 = l2.Begin( );
while ( l2.End( ) != it2 )
{
//cout << *it2 << " ";//此时 *it2 -> 是AA (它是自定义类型,不是内置类型 ,库里面不支持它的输出)
cout << it2->_a1 << " ";//迭代器中包含指向节点的指针 迭代器通过 重载 operator->( ), 它成为指向节点中数据的指针
//(it2.operator->( ))(指向节点中数据(AA)的指针) -> _a1;
//编译器做的就是 把 it2->_a1 解释为 it2.operator->( ) ->_a1
//(it2.operator->( )) == it2-> 然后 ,如果要访问a1 应该是: it2->->_a1 但这样可读性不好,所以编译器做了优化,只需要一个 ->.
//operator->(这是函数名) 隐含了一个参数 this指针,但是不需要我们传
++it2;
}
cout << endl;
List:;Iterator pos = l1.Find( 3 );
l1.Insert( pos, 30 );
}
左右都存在 -> 赋值
左不存在 -> 拷贝构造
*it = 10;
it 如果是普通迭代器 调用 operator*( ) 返回 Ref 我也不知道 Ref 是谁。
但如果是普通迭代器 返回的是 T&
const迭代器 则返回 const T&.
*it 返回_node->_data(Ref) 或者AA& 如何访问AA的成员呢.
(*it2)._a1 对象.成员 指向对象指针->成员 我们把迭代器想象为指向结点中数据(此时为AA)的指针。 如指向AA的指针 则it2->_a1更形象 所以迭代器支持 operator->( )
(it2.operator->( ))->_a1; const迭代器,(it2.operator->( )) 返回 const T* 所以此时_a1不能赋值。 const 修饰指针指向内容。
const迭代器用于const对象, 如Print函数 我传const对象,是不想让你修改,你只遍历访问就可以了。 但我要传引用,而不是传值(因为拷贝构造代价太大). 但引用我又不能让你改变我。 所以 传 const List
const 对象 ->(使用) const 迭代器。 const对象调用成员函数Begin( )时是调用 const版本 Begin() -> 它返回const迭代器,也限制保证了const对象不能使用普通迭代器 不会出错。
string s( "1" );//拷贝构造
string s = "21213"; -> (隐式转换)先构造,再拷贝构造 但是编译器优化后 就是一个构造了. 凡是 返回值 ,赋值, 传参 在一个步骤里面时, 凡是临时对象时, 编译器都会把它优化成一个步骤。
STL 中 empty -> 时间复杂度O(1) size ->时间复杂度 O(n)
链表中别写出这样的代码
for ( size_t i = 0; i < l.size( ); ++i ){ } 不好的代码 时间复杂度为O(n*n) 因为每次 l.size( ) 都会遍历链表一遍
list中接口介绍:
max_size( ) size_t( -1 )会转换成无符号最大的数; 最多可以有多少字节
pop_front/push_front 都是调用 Insert 和 Erase 实现
resize 更改大小 你的数据多则删除数据, 你的数据少则补充数据, 多出来的位置给缺省值或你传的值
cleae 清掉数据,不清头节点
assign:
list
template
void assign( InputIterator first, InputIterator lase );
可以认为InputInterator就是一个迭代器, 因为它是一个模板函数
l2.assign( l1.begin( ), l1.end( ) ); 迭代器区间, 左闭右开
PrintList( l1 );
PrintList( l2 );
也就是把我数据给你, 同时给数据也可以进行选择, 如 ++l1.begin( ), --l1.end( ) 也就是给迭代器区间,
vectot
v.push_back( 10 );
12.assign( v.begin( ), v.end( ) ); //也就是说 它是模板函数 根据实际实际类型进行推演 迭代器类型
remove 删除值 erase 位置
尾插时: 原 双向循环链表 有结点 和 没有结点 两种情况分析如图:
1.有结点
2.没有结点:
如图分析可知,两种情况是一样的,可以用相同的逻辑处理尾插操作,而不需要分别处理。
尾删时:
Insert 在指定位置pos的前面插入数据 和
erase 删除指定位置的数据:
迭代器失效:比如我们上面代码中,第一次实现的erase方法,返回void, 即没有返回值, 此时++it,程序会挂,这就是迭代器失效:
此时,如图:
STL 中解决迭代器失效采用的方法是, erase方法会返回一个迭代器, 这个迭代器是被删除位置的下个位置的迭代器
但如下使用方式就有问题:
list::iterator it = l1.begin( );
while ( l1.end( ) != it )
{
if ( 0 == *it % 2 )
l1.erase( it );
++it;
}
正确写法:
while ( l1.end( ) != it )
{
if ( 0 == *it % 2 )
it = l1.erase( it );
else
++it;
}
此时需要做的,就是修改 erase方法:
Iterator Erase( Iterator&/*1*/ pos )
{
assert( (End( ) != pos) && (NULL != pos._node) );
Node* del = pos._node;
Node* prev = del->_prev;
Node* next = del->_next;
delete del;
prev->_next = next;
next->_prev = prev;
pos = prev;//2
return next;
}
迭代器 用 * 和 -> 修改数据
STL算法中去重:
测试各种list各种接口:
void TestList1( )
{
list l1;
l1.push_back( 3 );
l1.push_back( 2 );
l1.push_back( 1 );
l1.push_back( 0 );
l1.push_back( 4 );
l1.sort( );//默认升序, 降序需要给一个仿函数
l1.sort( less( ) );// less 小于 < 即升序 greater 大于 > 降序
l1.unique( );//去重 逻辑如图
l1.reverse( );//逆置
l1.remove( 2 );//remove 删不存在元素 没什么影响(现象).
//erase 需要 配合 Find(即返回它一个位置) 但 List 没Find方法 解决方法 --> 算法 用 STL 算法 find
list::iterator pos = find( l1.begin( ), l1.end( ), 4 );//#include
//l1.erase( pos ); //删除不存在元素时, 程序会挂, 所以像下面一样写
if ( l1.end( ) != pos )
{
l1.erase( pos );
}
PrintList( l1 );
}
注意我们模拟实现list时:一定不能建立一个 list.h 再建立一个 list.cpp因为模板不能分离编译。
反向迭代器实现 ---> Iterator.h:
//反向迭代器的++, 即正向迭代器的 --
//所以, 利用适配器模式实现反向迭代器
//STL 中 反向迭代器 模板参数是 Iterator --> 即你传我 什么容器的迭代器, 我就变成什么容器的反向迭代器
//32 位系统下指针 是4字节。 64位系统下, 8字节或4字节(关键看你是 32 位程序 还是 64位程序)。
//Node* 4字节, Iterator 包含封装Node* 但不增加它大小, 也是4字节。 从意义上变为新的类型。 反向迭代器也是4字节。
#pragma once
//class 和 typename 区别
template //编译错误 , 因为下面用到Iterator::Reference。 它不认识Iterator, 因为Iterator是模板, 所以没实例化前没有具体类型, 编译器找不到 Reference。 我们需要在 前面加 typename 才行
class ReverseIterator
{
protected:
Iterator _current;
typedef ReverseIterator Self;
public:
ReverseIterator( Iterator it )
: _current( it )
{}
typename Iterator::Reference operator*( )//编译器找不到Reference,但是在其它地方可以找到, 我们帮助编译器确认这个事情, 所以加 typename( 取模板类的内嵌类型时(没有实例化, 没有生成代码, 编译器不认识它) )
{
Iterator tmp = _current;
return *( --tmp );
//之所以要这样实现,如图,为了和正向迭代器的 begin 和 end 位置对应起来, 所以要取到第一个数据( 4 )时 ---> *( --rbegin( ) )
}
typename Iterator::Pointer operator->( )
{
return &operator*( );
}
Self& operator++( )//前置
{
--_current;
return *this;
}
Self operator++( int );//后置 返回++前的临时对象, 所以不用引用
Self& operator--( )
{
--_current;
return *this;
}
Self operator--( int );
bool operator!=( const Self& s )
{
return _current != s._current;
}
};
void PrintMyListR( List& l1 )
{
List::ReverseIterator rit = l1.RBegin( );
while ( l1.REnd( ) != rit )
{
cout << *rit << " ";
++rit;
}
cout << endl;
}
void PrintMyListR( const List& l1 )
{
List::ConstReverseIterator rit = l1.RBegin( );
while ( l1.REnd( ) != rit )
{
cout << *rit << " ";
++rit;
}
cout << endl;
}