一.string类——串
1.字符与字节
注意:1字=2字节(1 word = 2 byte),1字节=8位(1 byte = 8bit),0000 0000 在VS内存中显示成00,可以看成16进制,但本质还是二进制。
2.string常见对象函数
int main()
{
//构造
string s1;
string s2("hello world");
string s3 = "hello world"; //隐式类型转换 const char* -> string
string s4(s3, 6, 3); //从s3的第pos个位置向后取len个来构造s4
//string s4(const string& str , size_t pos ,size_t len=npos) npos是size_t的最大值
string s5("hello world",5); //用前n个构造
string s6(10, '*'); //初始化10个*
//修改
s2.push_back(' '); //在后面添加一个字符
s2.append("world"); //在后面添加一串字符串
s2 += ' ';
s2 += "world"; //字符串加等
s2.reserve(100); //保留,提前开空间,只会改变capacity
s2.resize(100); //size,capacity都改变
s2.resize(100,'x');
//reverse翻转逆置
}
迭代器:
int main()
{
//迭代器 - 像指针但又不是指针
string::iterator it = s1.begin(); //begin指向第一个元素
//string iterator 就像一个数据类型,跟int it类似
while (it != s1.end()) //end指向最后一个数据的下一个
{
cout << *it << " ";
++it;
}
cout << endl;
//反向迭代器
string::reverse_iterator rit = s1.rbegin(); //也可以 auto rit = s1.rbegin();
while (rit != s1.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
return 0;
}
void Func(const sring& s) //当const的时候
{
string::const_iterator it = s.begin(); //当传值const时,用const_iterator
while (it != s.end())
{
*it += 1; //这个时候*it就不能被修改了
cout << *it << " ";
++it;
}
cout << endl;
}
其他:
int main()
{
string s1("world");
s1.insert(0, "hello"); //在第0个位置插入字符串
cout << s1 << endl;
//插入空格
s1.insert(5, 1, ' '); //在第5个位置插入1个字符
s1.insert(5, " ");
s1.insert(s1.begin() + 5, ' ');
cout << s1 << endl;
//删除
string s2("hello world");
s2.erase(5,1); //从第五个位置删除一个字符
s2.erase(5); //从第五个位置开始删除所有字符 (缺省了maxsize)
s2.erase(s2.begin() + 5); //用迭代器删除第5个位置
cout << s2 << endl;
//因为实际上是个字符顺序表,所以说每次插入和删除都会伴随着数据往后挪动
//所以尽量少用insert和erase;
string s1("hello world i love you");
size_t pos = s1.find(' '); //find函数,找到了返回该字符下标,没找到返回size_t的最大值
while(pos != string::npos)
//npos就是那个最大值 (npos是-1,但是是无符号数,所以就是最大值了)
{
s1.replace(pos, 1, "%20"); //从pos下标开始,往后替换1个位%20
pos = s1.find(' ', pos + 3); //新的pos是之前的pos+3的位置,(缺省值是0)
}
cout << s1 << endl;
//针对上面的问题另一个解决方案
string newstr;
int num = 0;
for (auto ch : s1)
{
if (ch == ' ')
num++;
}
newstr.reserve(s1.size() + 2 * num); //提前开好空间,+=就不用开了
for (auto ch : s1)
{
if (ch != ' ')
newstr += ch;
else
newstr += "%20";
}
cout << newstr << endl;
//交换
//string的交换比类模板的交换效率更高,因为string只是修改s1,s2指针的指向,而类模板是纯交换
string s3 = "xxxx";
string s4 = "66666";
s3.swap(s4); //string的交换
swap(s3, s4); //模板的交换
string s5 = "hello world";
cout << s5 << endl;
cout << s5.c_str() << endl; //与上面结果一样
s5 += '\0';
s5 += "xxx";
cout << s5 << endl;
cout << s5.c_str() << endl;
//s5.c_str(),以字符串指针的形式返回(const char*),该string。str->char数组类型
//这样的话若string中有'\0'就会停下,
//要是以流输出的形式打印,string中遇到'\0'不会停下,会一直输出到第size个
return 0;
}
int main()
{
string file("string.cpp");
size_t pos = file.find('.'); //从前往后找第一个
size_t pos = file.rfind('.'); //从后面往前找第一个
if (pos != string::npos)
{
string suffix = file.substr(pos, file.size() - pos);
//从pos位置开始,取长度个字符,构成后缀(suffix)的字符串
cout << suffix << endl;
}
string str("pllcaspdpinaosncajowdkad");
size_t found = str.find_first_of("abcd"); //找到str中的“abcd”中的字符(a和b和c和d)
size_t found = str.find_first_not_of("abcd"); //找到str中不是“abcd”中字符(a和b和c和d)的字符
size_t found = str.find_last_of("abcd"); //从后往前找
while (pos != string::npos)
{
str[found] = '*';
found = str.find_first_of("abcd", found + 1); //修改串中的"abcd"
}
cout << str << endl;
return 0;
}
int main()
{
string str;
cin >> str; //获取字符串 ,中间以空格或换行分割
getline(cin, str); //只用换行来分割,相当于c的gets( )
return 0;
}
注意:cin>>s;可以读取字符串,但是要读取一行的话用 getline(cin,s);
3.编写string类
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<assert.h>
using std::cout;
using std::endl;
using std::ostream;
using std::istream;
namespace mst //命名空间为了防止与std的string冲突
{
class string
{
public:
//string(const char* str = nullptr) 不可以,因为strlen(str)会对空指针进行解引用
//string(const char* str = '\0') 不可以,因为'\0'最后还是会被转换为空指针
string(const char* str = "") //用常量字符串的就行了,空串自带\0
: _size(strlen(str))
{
_capacity = _size;
_str = new char[_capacity + 1]; //capacity是有效字符的容量,所以要加一,
strcpy(_str, str);
}
string(const string& s) //拷贝构造
:_size(s._size)
, _capacity(s._capacity)
{
_str = new char[s._capacity + 1]; //+1是留给'\0'的
strcpy(_str, s._str);
}
string& operator=(const string& s) //类的赋值操作
//注意赋值是两个已经存在的对象,所以需要注意他们已经开好的空间大小
{
if (this != &s) //当左右两边不同时才操作
{
char* tmp = new char[s._capacity + 1]; //先确定能开出来,在释放旧空间
strcpy(tmp, s._str);
delete[] _str; //所以最好一开始就把旧空间释放掉,在开成与对象大小一样的空间
_str = tmp;
_size = s._size;
_capacity = s._capacity;
return *this;
}
return *this;
}
//获取字符串
const char* c_str()
{
return _str;
}
size_t size() const //加了const之后,不仅普通对象可以调用,const对象也可以调用
{
return _size;
}
//给普通对象调用的[]
char& operator[](size_t pos) //因为返回这个值的引用(别名),所以既可以读取这个值,也可以修改这个值
{
assert(pos < _size); //防止越界
return _str[pos]; //返回pos位置字符的引用
}
//给const 类型调用的[]接口函数
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
//指针迭代器
typedef char* iteartor;
typedef const char* const_iteartor; //const类型的迭代器
iteartor begin()
{
return _str;
}
iteartor end()
{
return _str + _size;
}
const_iteartor begin() const //const类型的迭代器
{
return _str;
}
const_iteartor end() const
{
return _str + _size;
}
//字符串比大小
//只要是函数里面不修改成员变量数据的函数,最好都在后面加上const
bool operator>(const string& s) const
{
return strcmp(_str, s._str) > 0;
}
bool operator==(const string& s) const
{
return strcmp(_str, s._str) == 0;
}
bool operator>=(const string& s) const
{
return *this > s || *this == s; //这是复用之前写的东西
}
bool operator<(const string& s) const
{
return !(*this >= s);
}
bool operator<=(const string& s) const
{
return !(*this > s);
}
bool operator!=(const string& s) const
{
return !(*this == s);
}
//手动扩容
void reserve(size_t n)
{
if (n > _capacity) //不要缩容
{
char* tmp = new char[n + 1]; //记得要给'\0'多开一个空间
strcpy(tmp, _str);
delete[] _str; //先拷贝再删除旧空间
_str = tmp;
_capacity = n;
}
}
void push_back(char ch)
{
if (_size >= _capacity)
{
if (_capacity == 0)
reserve(4);
else
reserve(_capacity * 2); //这里不要用realloc那些
}
_str[_size] = ch;
_size++;
_str[_size] = '\0'; //记得加上斜杠0
//也可以直接 insert(_size,ch);
}
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str + _size, str);
_size += len;
//也可以直接 insert(_size,str);
}
string& operator+=(const char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
void insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size >= _capacity)
{
if (_capacity == 0)
reserve(4);
else
reserve(_capacity * 2);
}
size_t end = _size + 1;
while (end > pos) //这里不要大于等于,因为当end减到-1的时候,又变回最大正数了,死循环了
{
_str[end] = _str[end - 1];
--end;
}
_str[pos] = ch;
++_size;
}
void insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
size_t end = _size + len;
if (_size + len > _capacity)
{
reserve(_size + len);
}
//挪动数据
while (end > pos + len - 1)
{
_str[end] = _str[end - len];
--end;
}
//拷贝数据,不要用strcpy因为会拷贝'\0'
strncpy(_str + pos, str, len);
_size += len;
}
void erase(size_t pos, size_t len = npos)
{
if (len == npos || pos + len >= _size) //一路删到最后的情况
{
_str[pos] = '\0';
_size = pos;
}
else //删除一部分,需要挪动数据
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
}
//s1.swap(s2)比swap(s1,s2)的效率高多了,因为swap(s1,s2)还要发生深拷贝,而s1.swap(s2)只是交换了指针
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_capacity, s._capacity);
std::swap(_size, s._size);
}
void resize(size_t n, char ch = '\0')
{
if (n <= _size) //新的size比原size小 ---删除数据
{
_size = n;
_str[_size] = '\0';
}
else
{
if (n > _capacity) //新的size比capacity还大,就要先扩容再填数据
{
reserve(n);
}
size_t i = _size;
while (i < n)
{
_str[i] = ch;
i++;
}
_size = n;
_str[_size] = '\0';
}
}
size_t find(char ch, size_t pos = 0)
{
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
size_t find(const char* str, size_t pos = 0)
{
char* p = strstr(_str + pos, str);
if (p == nullptr)
{
return npos;
}
else
{
return p - _str;
}
}
void clear()
{
_str[0] = '\0';
_size = 0;
}
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
private:
char* _str;
size_t _size; //_size的指针指向'\0',一共有size个,但是数组下标从0开始,所以最后一个数据下标是_size-1。
size_t _capacity;
static const size_t npos = -1; //只有整形,加const,才能对static的变量在类内部给缺省值
//static const double npos = 1.1; 这样就不行
};
ostream& operator<<(ostream& out, const string& s) //重载的是<<,不是cout
{
for (auto ch : s)
{
out << ch;
}
return out;
}
//流插入重载不能做成员函数,this指针把第一个位置抢了,cout做不了第一个参数,所以我们当时用的友元函数
//但是流插入重载并不是必须是用友元函数,像这样放到全局变量里也可以
istream& operator>>(istream& in, string& s)
{
s.clear(); //清理掉之前的缓冲区
char ch = in.get(); //用>>会自动跳过空格和换行,就识别不到了,所以用in.get()
while (ch != ' ' && ch != '\n')
{
s += ch;
ch = in.get();
}
return in;
//为了防止多次输入字符导致加等开空间浪费,有以下类似数据池的办法
s.clear();
char ch = in.get();
char buf[128];
size_t i = 0;
while (ch != ' ' && ch != '\n')
{
buf[i++] = ch;
if (i == 127)
{
buf[127] = '\0';
s += buf; //每次都是加字符串,开空间的次数就少了
i = 0;
}
ch = in.get();
}
if (i != 0)
{
buf[i] = '\0';
s += buf;
}
return in;
}
}
遍历string的三种方式:
1.[]遍历
void test1()
{
string s1("hello world");
for (size_t i = 0; i < s1.size(); i++)
{
cout << s1[i] << endl;
s1[i]++; //可以写,可以读,还可以修改,(因为[]返回的是string&)
}
}
2.指针迭代器
//指针迭代器
typedef char* iteartor;
typedef const char* const_iteartor; //const类型的迭代器
iteartor begin()
{
return _str;
}
iteartor end()
{
return _str + _size;
}
const_iteartor begin() const //const类型的迭代器
{
return _str;
}
const_iteartor end() const
{
return _str + _size;
}
typedef char* iteartor;
iteartor begin()
{
return _str;
}
iteartor end()
{
return _str + _size;
}
void test2()
{
string s1("hello world");
string::iteartor it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
++it;
}
}
3.范围for 注意:有了迭代器才能用范围for
//只有你自己实现的迭代器跟范围for规定所需要的命名相同才可以用(begin(),end()),Begin()都不行
void test3()
{
string s1("hello world");
for (auto ch : s1)
{
cout << ch << endl;
}
const string s2("hello world");
for (auto ch : s1)
{
cout << ch << endl; //这时候需要调用const的迭代器
}
}
二.vector类——顺序表
1.vector常见对象函数的使用
#include<vector> //任意类型的顺序表
using namespace std;
void test1()
{
vector<int> v;
vector<int> v1(v); //拷贝构造
vector<int> v2(10, 1);//n个val的构造
std::string s1("hello");
vector<int> v3(s1.begin(), s2.end()); //用迭代器区间构造
//所有的迭代器区间都是左闭右开
vector<int> v4(++v2.begin(), --v2.end()); //这样++和--就可以去掉第一个和最后一个
//auto rit = v.rbegin();
vector<int>::reverse_iterator rit = v.rbegin(); //反向迭代器
while (rit!=v.rend())
{
cout << *rit << endl;
++rit;
}
vector<int>::iterator pos = find(v.begin(), v.end(), 2); //迭代器区间加要查找的数
if (pos != v.end())
{
v.insert(pos, 20); //vector的insert需要用迭代器先找到位置
}
pos = find(v.begin(), v.end(), 2);
v.erase(pos); //要用迭代器重新确定一次pos位置才能继续用,迭代器失效
//insert,erase用完之后的pos指针最好都视为迭代器失效
//头插,头删等直接用就是了
v.insert(v.begin(),20);
v.erase(v.begin());
for (auto ch : v)
{
cout << ch << " ";
}
}
void test2()
{
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(3);
v.push_back(20);
std::vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 ==0 )
{
it = v.erase(it); //正是因为erase会失效,所以会有返回值,保证及时有效
}
else
{
++it;
}
}
for (size_t i = 0; i < v.size(); i++)
{
cout << v[i] << " ";
}
cout << endl;
}
遍历的方式
//1.
for (size_t i = 0; i < v.size(); i++)
{
cout << v[i] << endl;
}
//2.迭代器
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it << endl;
++it;
}
//3.
for (auto ch : v)
{
cout << ch << endl;
}
vector的嵌套:
2. vector<char>与string的区别
1.string的结尾有'\0',而vector没有,所以vector不容易更好的与c进行兼容。
2.vector比较大小按长度或其他比,string比较大小只按ascll码比较
3.vector的实现
注意:指针的左闭右开
int main()
{
int arr[10] = { 0 };
printf("%d\n", &arr[9] - &arr[0]); // 结果是9:0 1 2 3 4 5 6 7 8 9 10
return 0;
}
namespace mst
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
vector() :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
{}
vector(size_t n, const T& val = T()) //T()是匿名对象,是T都默认构造,理解为所有类型的初始值,这里的初始化不能给0
//本来匿名对象的生命周期只有一行,因为没人用他,而 const T& 就可以延长生命周期,这里的val就是匿名对象的别名
//因为匿名对象有常性,所以用const T&
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr) //别忘了初始化
{
reserve(n); //先开空间提高效率
for (size_t i = 0; i < n; i++)
{
push_back(val);
}
}
template <class InputIterator>
vector(InputIterator first, InputIterator last) //针对任意迭代器
:_start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr) //别忘了初始化
{
while (first != last)
{
push_back(*first);
++first;
}
}
vector(const vector<T>& v)
{
_start = new T[v.capacity()]; //要自己先开出一片空间,不然直接复制的话会导致两个指针指向同一片空间(浅拷贝)
//memcpy(_start, v._start, sizeof(T) * v.size());
for (size_t i = 0; i < v.size(); i++)
{
_start[i] = v._start[i];
}
_finish = _start + v.size();
//_end_of_storage = _start + v.capacity(); 不用了,reserve会自己处理_end_of_storage
}
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage , v._end_of_storage);
}
//v1 = v2 ,把v2赋值给v1
vector<T>& operator=(vector<T> v)
{
swap(v);
return *this;
}
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin()const
{
return _start;
}
const_iterator end()const
{
return _finish;
}
void resize(size_t n, T val = T()) //匿名对象,对任意类型的默认构造(模板这个地方支持了内置类型的默认构造,其他地方的内置类型是没有的)
{
if (n < size())
{
_finish = _start + n; //移动finish,相当于是删除元素
}
else
{
if (n > capacity())
reserve(n);
while (_finish != _start + n)
{
*_finish = val;
_finish++;
}
}
}
void reserve(size_t n)
{
if (n > capacity()) //防止缩容
{
size_t sz = size(); //先提前记录
T* tmp = new T[n];
if (_start != nullptr) //new失败了不会返回空,这里是判断旧空间是否为空,为空就不需要拷贝过来了
{
//memcpy(tmp, _start, sizeof(T) * size()); //对于自定义类型,不能直接全拷贝,不然两个指针指向同一块空间
for (size_t i = 0; i < sz; i++)
{
tmp[i] = _start[i];
}
delete[] _start; //要先释放掉,在赋值新的过来
}
//三个指针要重新定位
_start = tmp;
//_finish = _start + sz; //注意:这个时候_start已经是扩容之后的_start,而_finish还是之前的_finish,这下size()=两个地址相减,减出来就不对了
_finish = _start + sz;
_end_of_storage = _start + n;
}
}
size_t capacity() const
{
return _end_of_storage - _start;
}
size_t size() const
{
return _finish - _start;
}
T& operator[](size_t pos) //返回引用,因为[i]也能应该被修改
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos)const //const类型的[]
{
assert(pos < size());
return _start[pos];
}
void push_back(const T& x)
{
if (_finish == _end_of_storage) //满了
{
reserve(capacity() == 0 ? 4 : capacity() * 2);
}
*_finish = x; //因为左闭右开,所以_finish指针可以直接解引用
++_finish;
}
void pop_back()
{
assert(!empty());
--_finish;
}
bool empty()
{
return _start == _finish;
}
iterator insert(iterator pos,const T& val)
{
assert(pos >= _start);
assert(pos <= _finish);
if (_finish == _end_of_storage) //满了会越界
{
size_t len = pos - _start; //先提前计算pos与_start的相对位置
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + len; //更新pos的位置,解决pos失效的问题
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
end--;
}
*pos = val;
++_finish;
return pos; //返回新的插入的位置
}
//迭代器失效:
//每当扩容的时候,_start,_finish,等指针也会跟着变,但是传过来的pos位置没有变;
//这就导致 while (end >= pos) 出现错误,解决方法是把pos更新一下
iterator erase(iterator pos)
{
assert(pos >= _start);
assert(pos < _finish);
iterator start = pos + 1;
while (start != _finish)
{
*(start - 1) = *start;
++start;
}
--_finish;
return pos;
}
~vector()
{
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
private:
iterator _start;
iterator _finish;
iterator _end_of_storage;
};
}
自定义类型拷贝构造的注意点:
对于自定义类型,这里就不要用memcpy了,_str还是指向同一块空间了 ,而是使用先开空间,再分别拷贝的深拷贝。
三.List类——带头双向循环链表
1.List对象函数
void 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 pos = find(lt.begin(), lt.end(), 2);
if (pos != lt.end())
{
lt.insert(pos, 30);
}
list<int> lt2;
lt2.swap(lt); //两个链表交换
for (auto e : lt)
{
cout << e << " ";
}
}
剩下的直接去看cplusplus:
cplusplus.com - The C++ Resources Network
迭代器类型:
2.List的实现的细节点:
(1)begin()为第一个有效数据的位置,end()为最后一个数据的下一个位置。
(2)迭代器的const:
法1:自己直接实现
template<class T>
struct _list_iterator //迭代器
{
typedef ListNode<T> node;
node* _node;
_list_iterator(node* n)
:_node(n)
{}
T& operator*() //对解引用进行重载,而且解引用的那个能被修改,所以是T&
{
return _node->_data;
}
typedef _list_iterator<T> self;
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& s)
{
return _node != s._node; //判断迭代器的指针是否相等
}
bool operator==(const self& s)
{
return _node == s._node;
}
//迭代器的成员函数只有一个结点,最后会被list释放,所以迭代器不需要写析构函数,用默认的就行了
};
//const的迭代器要让指向的内容不被修改,不是迭代器不能被修改
template<class T>
struct _list_const_iterator //迭代器
{
typedef ListNode<T> node;
node* _node;
_list_const_iterator(node* n)
:_node(n)
{}
const T& operator*() //因为const的迭代器要让指向的内容不被修改,所以是const T&
{
return _node->_data;
}
typedef _list_const_iterator<T> self;
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& s)
{
return _node != s._node; //判断迭代器的指针是否相等
}
bool operator==(const self& s)
{
return _node == s._node;
}
};
法2:增加模板参数(推荐)
template<class T,class Ref>
struct _list_iterator //迭代器
{
typedef ListNode<T> node;
node* _node;
_list_iterator(node* n)
:_node(n)
{}
Ref operator*() //对解引用进行重载,而且解引用的那个能被修改,所以是T&
{
return _node->_data;
}
typedef _list_iterator<T,Ref> self;
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& s)
{
return _node != s._node; //判断迭代器的指针是否相等
}
bool operator==(const self& s)
{
return _node == s._node;
}
//迭代器的成员函数只有一个结点,最后会被list释放,所以迭代器不需要写析构函数,用默认的就行了
};
template<class T>
class list //list类
{
typedef ListNode<T> node;
public:
typedef _list_iterator<T,T&> iterator;
typedef _list_iterator<T,const T&> const_iterator;
}
(3)对“—>”的重载
struct AA
{
int _a1;
int _a2;
AA(int a1, int a2)
:_a1(a1),_a2(a2)
{}
};
void test1()
{
list<AA> lt;
lt.push_back(AA(1, 1));
lt.push_back(AA(2, 2));
lt.push_back(AA(3, 3));
list<AA>::iterator it = lt.begin();
while (it != lt.end())
{
cout << (*it)._a1 << " ";
//这里的解引用加“."就相当于箭头,从指向结构体的指针直接访问到成员
++it;
}
cout << endl;
}
3.List的实现
namespace mst
{
template<class T>
struct ListNode //结点
{
ListNode<T>* _next;
ListNode<T>* _prev;
T _data;
ListNode(const T& x = T()) //结构体也有构造函数
:_next(nullptr),_prev(nullptr),_data(x)
{}
};
template<class T,class Ref,class Ptr>
struct _list_iterator //迭代器
{
typedef ListNode<T> node;
node* _node;
_list_iterator(node* n)
:_node(n)
{}
Ref operator*() //对解引用进行重载,而且解引用的那个能被修改,所以是T&
{
return _node->_data;
}
Ptr operator->() //返回值T* 是“T的指针”的类型
{
return &_node->_data;
}
typedef _list_iterator<T,Ref,Ptr> self;
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& s)
{
return _node != s._node; //判断迭代器的指针是否相等
}
bool operator==(const self& s)
{
return _node == s._node;
}
//迭代器的成员函数只有一个结点,最后会被list释放,所以迭代器不需要写析构函数,用默认的就行了
};
//1.迭代器要么就是原生指针
//2.迭代器要么就是自定义类型对原生指针的封装,模拟指针的行为
//前面这里用struct是为了把他们封装成共有
template<class T>
class list //list类
{
typedef ListNode<T> node;
public:
typedef _list_iterator<T,T&,T*> 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 //const修饰*this,修饰的是一个指针,指针不能被改变,但是指针修饰的内容可以变
{
return iterator(_head->_next);
}
const_iterator end() const
{
return 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)
{
empty_init();
while (first != last)
{
push_back(*first); //push_back需要头节点
++first;
}
}
void swap(list<T>& tmp)
{
std::swap(_head, tmp._head);
}
//拷贝构造
list(const list<T>& lt)
{
empty_init(); //要先初始化一下,不然this是随机值
list<T> tmp(lt.begin(), lt.end());
swap(tmp);
}
list<T>& operator=(list<T> lt) //这里不能传引用(list<T>& lt),不然lt也会跟着变
{
swap(lt);
return *this;
}
void push_back(const T& x)
{
insert(end(), x);
}
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_back()
{
erase(--end());
}
void pop_front()
{
erase(begin());
}
void insert(iterator pos,const T& x)
{
node* cur = pos._node;
node* prev = cur->_prev;
node* newnode = new node(x);
newnode->_next = cur;
cur->_prev = newnode;
prev->_next = newnode;
newnode->_prev = prev;
}
iterator erase(iterator pos)
{
assert(pos != end()); //头结点不能被erase
node* cur = pos._node;
node* next = cur->_next;
node* prev = cur->_prev;
prev->_next = next;
next->_prev = prev;
delete cur; //删除完之后会导致迭代器失效,因为你指向的那个位置已经被释放了
return iterator(next); //所以返回下一个的位置
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
//erase(it++)也行
}
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
private:
ListNode<T>* _head; //别忘了加模板参数<T>
};
}
四.Stack类
using namespace std;
namespace mst
{
//适配器模式 /配接器
//template<class T , class Container = vector<int>> //Container 容器,vector,list之类的,支持缺省容器
template<class T,class Container = deque<int>>
class stack
{
public:
void push(const T& x)
{
_con.push_back(x); //把容器的尾部当成栈顶
}
void pop()
{
_con.pop_back();
}
const T& top()
{
return _con.back();
}
size_t size()
{
return _con.size();
}
bool empty()
{
return _con.empty();
}
private:
//vector<T> _v;
Container _con;
};
void test()
{
stack<int,vector<int>> st1; //顺序栈
stack<int, list<int>> st2; //链式栈
stack<int> st; //用了缺省值了
st.push(1);
st.push(2);
st.push(3);
st.push(4);
while (!st.empty())
{
cout << st.top() << " ";
st.pop();
}
cout << endl;
}
}
五.Queue类
1.队列实现
using namespace std;
namespace mst
{
//适配器模式 /配接器
//template<class T , class Container = list<int>> //Container 容器,vector,list之类的,支持缺省容器
template<class T,class Container = deque<int>>
class queue
{
public:
void push(const T& x)
{
_con.push_back(x); //把容器的尾部当成队列尾
}
void pop()
{
_con.pop_front(); //容器头部(队头)出
}
const T& front()
{
return _con.front();
}
const T& back()
{
return _con.back();
}
size_t size()
{
return _con.size();
}
bool empty()
{
return _con.empty();
}
private:
//vector<T> _v;
Container _con;
};
void test()
{
queue<int,list<int>> q1; //顺序栈
queue<int, list<int>> q2; //链式栈
queue<int> q; //用了缺省值了
q.push(1);
q.push(2);
q.push(3);
q.push(4);
while (!q.empty())
{
cout << q.front() << " ";
q.pop();
}
cout << endl;
}
}
队列的容器不适合选vector,因为vector没有头删函数,而队列出队需要头删
2.deque类——list与vector的结合体,是一个指针数组,每个指针指向一块小数组
访问效率:比List高,比Vector低。
固定小数组的大小,随机访问的效率会提高;不固定(小数组可以扩容),中间的插入删除效率提高。STL中的deque的小数组是固定大小的,因此当使用栈或队列这种需要头尾删插的,就大多把deque当作底层容器。
3.deque的迭代器
node是中控器的节点,cur`first`last`是buffer的指针。
4.优先级队列
按优先级出队,默认大的数优先级高,因为底层是一个大堆
#include<queue>
void test()
{
priority_queue<int> pq;
pq.push(1);
pq.push(0);
pq.push(7);
pq.push(4);
pq.push(5);
while (!pq.empty())
{
cout << pq.top() << " ";
pq.pop();
}
cout << endl;
}
变成小堆输出,要修改符号
#include<functional>
void test()
{
priority_queue<int,vector<int>,greater<int>> pq; //注意这里的greater是与大小反着来的
pq.push(1);
pq.push(0);
pq.push(7);
pq.push(4);
pq.push(5);
while (!pq.empty())
{
cout << pq.top() << " ";
pq.pop();
}
cout << endl;
}
优先级队列的实现
namespace mst
{
template<class T>
struct less
{
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
template<class T>
struct greater
{
bool operator()(const T& x, const T& y)
{
return x > y;
}
};
//默认大堆(大堆用小于)
template<class T, class Container = vector<T>, class Compare = less<T>>
class priority_queue
{
public:
void adjust_up(int child) //child,parent是下标,向上调整用child节点
{
Compare com; //定义一个仿函数的对象
int parent = (child - 1) / 2;
while (child > 0)
{
//if (_con[child] > _con[parent])
if (com(_con[parent], _con[child]))
{
swap(_con[child], _con[parent]);
child = parent; //更换下标
parent = (child - 1) / 2; //算新的的父亲节点
}
else
{
break;
}
}
}
void adjust_down(int parent) //向下调整用parent节点
{
Compare com; //定义一个仿函数的对象
int left_child = parent * 2 + 1;
int right_child = left_child + 1;
int max_child = left_child;
while (left_child < _con.size())
{
if (right_child < _con.size() && com(_con[left_child], _con[right_child])) //_con[right_child] > _con[left_child]
{
max_child = right_child;
}
if (com(_con[parent], _con[max_child])) //_con[max_child] > _con[parent]
{
swap(_con[max_child], _con[parent]);
//还完之后还得继续往下调整
parent = max_child;
left_child = parent * 2 + 1;
right_child = left_child + 1;
max_child = left_child;
}
else
{
break;
}
}
}
void push(const T& x)
{
_con.push_back(x);
adjust_up(_con.size() - 1); //从最后一个开始向上调整
}
void pop()
{
swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
adjust_down(0); //从顶部开始向下调整
}
T& top()
{
return _con[0];
}
size_t size()
{
return _con.size();
}
bool empty()
{
return _con.empty();
}
private:
Container _con;
};
}
参考网站:cplusplus.com - The C++ Resources Network