STL库初阶

一.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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

对玛导至昏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值