C++之序列式容器(vector、list、deque、string)

一、vector

1.vector的定义及使用

  • 定义: vector是可以改变大小数组的顺序容器。
  • 使用
#include <iostream>
#include <vector>

using namespace std;

void testVector1()
{
	vector<int> vc;
	vc.push_back(1); //尾插
	vc.push_back(2); //尾插
	vc.push_back(3); //尾插
	vc.push_back(4); //尾插
	
	size_t n = vc.size(); //计算vector的大小

						  
    //for循环遍历vector方法一:
	cout << "方法一for循环遍历:" << ' ';
	for (int i = 0; i < n; ++i)
	{
		cout << vc[i] << ' ';
	}
	cout << endl;

	//迭代器遍历vector方法二:
	cout << "方法二迭代器遍历:" << ' ';
	auto it = vc.begin();
	while (it != vc.end())
	{
		cout << *it << ' ';
		++it;
	}
	cout << endl;

	//范围for遍历vector方法三:
	cout << "方法三范围for遍历:" << ' ';
	for (auto e : vc)
	{
		cout << e << ' ';
	}
	cout << endl;

}
void testVector2()
{
	//初始化方法一:创建10个5的数组
	vector<int> vc(10, 5);
	cout << "vc:" << ' ';
	for (auto e : vc)
		cout << e << ' ';
	cout << endl;

	//初始化方法二:用迭代器区间初始化
	vector<int> v1(vc.begin(), vc.end());
	cout << "v1:" << ' ';
	for (auto e : v1)
		cout << e << ' ';
	cout << endl;

	vector<int> v2(++v1.begin(), --v1.end());
	cout << "v2:" << ' ';
	for (auto e : v2)
		cout << e << ' ';
	cout << endl;

}
void testVector3()
{
	vector<int> vc;
	vc.push_back(1); //尾插
	vc.push_back(2); //尾插
	vc.push_back(3); //尾插
	vc.push_back(4); //尾插

	cout << "正向遍历vector:" << ' ';
	for (auto e : vc)
		cout << e << ' ';
	cout << endl;
	//反向遍历vector
	cout << "反向遍历vector:" << ' ';
	vector<int>::reverse_iterator it = vc.rbegin();
	while (it != vc.rend())
	{
		cout << *it << ' ';
		++it;
	}
	cout << endl;

}
void testVector4()
{
	int a[] = { 1, 2, 3, 4 };
	//用迭代器初始化,指针是天然的迭代器
	vector<int> vc(a, a + sizeof(a) / sizeof(int));

	vector<int>::iterator pos = find(vc.begin(), vc.end(), 2);
	cout << "2在数组中的位置:" << pos - vc.begin() << endl;

	cout << "原数组:" << ' ';
	for (auto e : vc)
		cout << e << ' ';
	cout << endl;

	vc.insert(pos, 10);
	cout << "在pos位置插入10:" << ' ';
	for (auto e : vc)
		cout << e << ' ';
	cout << endl;

	pos = find(vc.begin(), vc.end(), 4);
	vc.erase(pos);
	cout << "删除pos位置后:" << ' ';
	for (auto e : vc)
		cout << e << ' ';
	cout << endl;
}
int main()
{
	cout << "testVector1() : " << endl;
	testVector1();
	cout << endl;

	cout << "testVector2() : " << endl;
	testVector2();
	cout << endl;

	cout << "testVector3() : " << endl;
	testVector3();
	cout << endl;

	cout << "testVector4() : " << endl;
	testVector4();
	cout << endl;

	return 0;
}

在这里插入图片描述

2.vector的模拟实现

#include <iostream>
#include <assert.h>

using namespace std;

namespace sheena
{
	template<class T>
	class my_vector
	{
	public:
		typedef T* iterator;
		//构造函数
		my_vector()
			:_start(nullptr)
			,_end(nullptr)
			,_end_of_storage(nullptr)
		{}
		//拷贝构造函数
		my_vector(const my_vector<T>& mv)
		{
			_start = new T[mv.size()];
			memcpy(_start, mv._start, sizeof(T) * mv.size());
			_end = _start + mv.size();
			_end_of_storage = _start + mv.capacity();

		}
		//析构函数
		~my_vector()
		{
			if (_start)
			{
				delete[]_start;
				_start = _end = _end_of_storage = nullptr;
			}
		}



		size_t size()const
		{
			return _end - _start;
		}
		size_t capacity()const
		{
			return _end_of_storage - _start;
		}



		T& operator[](size_t pos)
		{
			return _start[pos];
		}
		const T& operator[](size_t pos)const
		{
			return _start[pos];
		}



		void swap(my_vector<T>& mv)
		{
			swap(_start, mv._start);
			swap(_end, mv._end);
			swap(_end_of_storage, mv._end_of_storage);
		}

		my_vector& operator=(my_vector<T> mv)
		{
			swap(mv);

			return *this;
		}

		//不仅修改了size,而且还可能修改capacity
		my_vector resize(size_t n, const T& val = T())
		{
			if (n < size())
			{
				_end = _start + n;
				return;
			}
			if (n > capacity())
				_end_of_storage = n;

			while (_end != _start + n)
			{
				*_end = val;
				++_end;
			}
		}
		//只修改capacity
		void reserve(size_t n)
		{
			if (n > capacity)
			{
				size_t sz = size();
				T* tmp = new T[n];
				for (size_t i = 0; i < sz; ++i)
					tmp[i] = _start[i];
				delete[]_start;
				_start = tmp;
				_end = _start + sz;
				_end_of_storage = _start + n;
			}
		}


		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _end;
		}



		void insert(iterator& pos, const T* x)
		{
			assert(pos < _end && pos >= _start);
			if (_end == _end_of_storage)
			{
				size_t n = pos - _start;
				size_t newcapacity = capacity() == 0 ? 2 : 2 * capacity();
				reserve(newcapacity);
				pos = _start + n;
			}

			iterator end = _end - 1;
			while (end > pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = x;
			++_end;
		}
		void erase(iterator pos)
		{
			assert(pos < _end && pos >= _start);
			iterator begin = pos + 1;
			while (begin < _end)
			{
				*(begin - 1) = *begin;
				++begin;
			}
			--_end;
		}
		void push_back(const T& x)
		{
			insert(end(), x);
		}
		void pop_back()
		{
			assert(_end >= _start);
			erase(--end());
		}


	private:
		iterator _start;
		iterator _end;
		iterator _end_of_storage;
	};
}

二、list

1.list的定义及使用

  • 带头循环链表
#include <iostream>
#include <list>

using namespace std;

//迭代器遍历:
void Print1(list<int> lst)
{
	list<int>::iterator it = lst.begin();
	while (it != lst.end())
	{
		cout << *it << ' ';
		++it;
	}
	cout << endl;
}
//范围for遍历:
void Print2(list<int> lst)
{
	for (auto e : lst)
		cout << e << ' ';
	cout << endl;
}
void testList1()
{
	list<int> lst;
	lst.push_back(1);//尾插
	lst.push_back(2);//尾插
	lst.push_back(3);//尾插
	Print1(lst);
	lst.push_front(9);//头插
	lst.push_front(8);//头插
	lst.push_front(7);//头插
	Print1(lst);

	lst.pop_back();//尾删
	Print2(lst);
	lst.pop_front();//头删
	Print2(lst);

}
void testList2()
{
	list<int> lst(4, 100);//创建4个100的双向链表

	cout << "去重之前的lst:" << ' ';
	Print1(lst);
	lst.unique();//去重
	cout << "去重之后的lst:" << ' ';
	Print2(lst);

	lst.push_back(2);
	lst.push_back(9);
	lst.push_back(99);
	lst.push_front(500);
	lst.push_front(22);
	lst.push_front(500);
	lst.push_back(99);

	//lst.unique();
	cout << "排序之前的lst:" << ' ';
	Print2(lst);
	lst.sort();//排序
	cout << "排序之后的lst:" << ' ';
	Print1(lst);

	cout << "去重之后的lst:" << ' ';
	lst.unique();//去重前提是必须有序
	Print1(lst);
}
//迭代器失效
void testList3()
{
	list<int> lst;
	lst.push_back(2);
	lst.push_back(9);
	lst.push_back(99);
	lst.push_front(500);
	lst.push_front(22);
	Print1(lst);

	list<int>::iterator it = lst.begin();
	while (it != lst.end())
	{
		//迭代器失效
		lst.erase(it);//删除节点
		++it;
	}
	Print2(lst);
}
void testList4()
{
	list<int> lst;
	lst.push_back(2);
	lst.push_back(9);
	lst.push_back(99);
	lst.push_front(500);
	lst.push_front(22);
	cout << "删除lst节点之前:" << ' ';
	Print1(lst);

	list<int>::iterator it = lst.begin();
	while (it != lst.end())
		lst.erase(it++);//删除节点
	cout << "删除lst节点之后:" << ' ';
	Print2(lst);
}
int main()
{
	cout << "testList1:" << endl;
	testList1();
	cout << endl;
	cout << "testList2:" << endl;
	testList2();
	cout << endl;

	cout << "testList4:" << endl;
	testList4();
	cout << endl;

	return 0;
}

在这里插入图片描述

2.list的模拟实现

#include <iostream>

using namespace std;

namespace sheena
{
	template <class T>
	struct _list_node
	{
		_list_node<T>* _next;
		_list_node<T>* _prev;
		T _data;

		_list_node(const T& x = T())
			:_next(nullptr)
			,_prev(nullptr)
			,_data(x)
		{}
	};

	template <class T, class Ref, class Ptr>
	struct _list_iterator
	{
		typedef _list_node<T> node;
		typedef _list_iterator<T, Ref, Ptr> Self;
		node* _node;

		_list_iterator(node* node)
			:_node(node)
		{}
		//内置类型
		Ref operator*()
		{
			return _node->_data;
		}
		//自定义类型
		Ptr operator->()
		{
			return &_node->_data;
		}

		//前置++
		Self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		//后置++
		Self operator++()
		{
			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;
		}

		//it1 != it2
		bool operato!=(const Self& it)
		{
			return _node != it._node;
		}
		bool operator==(const Self& it)
		{
			return _node == it._node;
		}
	};

	template <class T>
	class my_list
	{
		typedef _list_node<T> node;
	public:
		typedef _list_iterator<T, T&, T*> iterator;
		typedef _list_iterator<const T, const T&, const T*> const_iterator;
		const_iterator begin()const
		{
			return const_iterator(_head->_next);
		}
		const_iterator end()const
		{
			return const_iterator(_head);
		}
		iterator begin()
		{
			return iterator(_head->_next);
		}
		iterator end()
		{
			return iterator(_head);
		}

		my_list()//构造函数
		{
			_head = new node<T>();
			_head->_next = _head;
			_head->_prev = _head;
		}
		my_list(const my_list<T>& ls)//拷贝构造函数
		{
			if (_head != ls._head)
			{
				_head->_next = _head;
				_head->_prev = _head;
				auto it = ls.begin();
				while (it != ls.end())
				{
					push_back(*it);
					++it;
				}
			}
		}
		my_list<T>& operator=(my_list<T> ls)//赋值函数
		{
			swap(_head, ls._head);
			return *this;
		}
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}
		~my_list()//析构函数
		{
			clear();
			delete _head;
			_head = nullptr;
		}


		void insert(iterator pos, const T& x)
		{
			node* cur = pos._node;
			node* prev = pos._node->_prev;

			node* newnode = new node(x);

			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
		}
		void push_back(const T& x)
		{
			insert(end(), x);
		}
		void push_front(const T& x)
		{
			insert(begin(), x);
		}
		

		iterator erase(iterator pos)
		{
			node* cur = pos->_node;
			node* prev = cur->_prev;
			node* next = cur->_next;

			prev->_next = next;
			next->_prev = prev;

			delete cur;
			cur = nullptr;

			return iterator(next);
		}
		void pop_back()
		{
			erase(--end());
		}
		void pop_front()
		{
			erase(begin());
		}

	private:
		node* _head;
	};
}

3.vector和list对比

(1)vector底层是动态顺序表,一段连续的空间。而list底层是带头结点的双向循环链表
(2)vector支持随机访问,访问某个元素的效率是O(1)。而list不支持随机访问,放入元素的效率是O(N)。
(3)vector尾插尾删的效率是O(1),任意位置插入和删除的效率低,时间复杂度为O(N),插入时可能需要增容(开辟新空间,拷贝元素,释放旧空,代价大)。而list尾插尾删、头插头删、任意位置的插入、删除效率都是O(1),不支持随机访问。
(4)vector底层是连续的空间,不容易造成内存碎片,空间利用率高,缓存利用率高。list底层是节点的动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低。
(5)vector的迭代器是原生态指针。而list的迭代器是对原生态指针进行的封装。
(6)vector在插入元素时,要给所有迭代器重新赋值,因为插入元素可能会导致重新扩容,导致原来的迭代器失效,删除时,当前迭代器需要重新赋值否则会失效。而list插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不会受影响。
(7)如果需要随机访问时,不关心插入、删除效率时就是用vector。如果有大量插入和删除操作,不需要随机访问时就用list。

三、deque

1.deque的定义及接口

  • deque是双端队列,底层是一段假象的连续空间,实际是分段连续的空间,而deque的迭代器维护了假象的连续。(其迭代器包含cur、first、last、node。有start迭代器和finish迭代器)
函数声明接口说明
size()返回deque的有效元素的个数
empty()返回deque是否为空,若是空,返回true;若不空,返回false
operator[]随机访问deque元素
front()返回deque首元素的引用
back()返回deque最后一个元素的引用
push_front(val)deque的头插
push_back(val)deque的尾插
insert(pos, val)在deque中pos位置插入val
pop_front()deque的头删
pop_back()deque的尾删
erase(pos)删除deque中pos位置的值
swap()交换deque中的内容
clear()将deque的元素清空

2.接口的使用

#include <iostream>
#include <deque>

using namespace std;

void Print(deque<int> dq)
{
	for (auto e : dq)
		cout << e << " ";
	cout << endl;
}
void Print2(deque<int> dq)
{
	deque<int>::iterator it = dq.begin();
	while (it != dq.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}
void Print3(deque<int> dq)
{
	deque<int>::reverse_iterator rit = dq.rbegin();
	while (rit != dq.rend())
	{
		cout << *rit << " ";
		++rit;
	}
	cout << endl;
}
void testDeque1()
{
	cout << "用10个6构造deque:" << " ";
	deque<int> dq1(10, 6);
	Print2(dq1);

	cout << "用数组构造deque:" << " ";
	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
	deque<int> dq2(arr, arr + sizeof(arr) / sizeof(arr[0]));
	Print(dq2);

	cout << "用dq2拷贝构造dq3:" << " ";
	deque<int> dq3(dq2);
	Print(dq3);

	cout << "pop_front():" << " ";
	dq3.pop_front();
	Print(dq3);

	cout << "pop_back():" << " ";
	dq3.pop_back();
	Print(dq3);

	cout << "push_back(val):" << " ";
	dq3.push_back(50);
	Print(dq3);

	cout << "push_front(val):" << " ";
	dq3.push_front(122);
	Print(dq3);

	cout << "insert(pos, val):" << " ";
	dq3.insert(dq3.begin () + 5, 500);
	Print(dq3);

	cout << "erase(pos):" << " ";
	dq3.erase(dq3.begin() + 3);
	Print2(dq3);


	cout << "size():" << " ";
	cout << dq3.size() << endl;

	cout << "front():" << " ";
	cout << dq3.front() << endl;

	cout << "back():" << " ";
	cout << dq3.back() << endl;

	cout << "clear()清空后dq3的size是:" << " ";
	dq3.clear();
	cout << dq3.size() << endl;
}
void testDeque2()
{
	int arr[] = {1,2,3,4,5,6,7,8,9 };
	deque<int> dq(arr, arr + sizeof(arr) / sizeof(arr[0]));

	cout << "operator[]访问:" << " ";
	cout << "dq[3] = " << dq[3] << endl;

	deque<int> dq2(10, 6);
	cout << "swap()之前的dq和dq2:" << endl;
	cout << "dq :" << " ";
	Print2(dq);
	cout << "dq2 :" << " ";
	Print(dq2);
	cout << "swap()之后的dq和dq2:" << endl;
	dq.swap(dq2);
	cout << "dq :" << " ";
	Print2(dq);
	cout << "dq2 :" << " ";
	Print(dq2);

	cout << "反向打印deque:" << " ";
	Print3(dq2);

}
int main()
{
	cout << "testDeque1:" << endl;
	testDeque1();
	cout << endl;

	cout << "testDeque2:" << endl;
	testDeque2();
	cout << endl;

	return 0;
}

在这里插入图片描述

  • swap()是交换两个deque。
  • insert(pos, val)和erase(pos)中的pos是迭代器。

3.deque的总结

  • 在序列式容器中,如果只是简单的存储元素,使用vector即可,如果对任意位置的插入、删除比较多,使用list即可。而deque的最大的应用就是作为标准库中的stack和queue的底层结构。

四、string

1.string的定义及使用

#include <iostream>
#include <string>

using namespace std;

void testString1()
{
	string s1;//无参数初始化
	string s2("hello");//带参初始化
	cout << "带参初始化s2:" << s2 << endl;
	string s3(s2);//拷贝构造
	cout << "拷贝构造s3:" << s3 << endl;

	cout << "s2的size为:    " << s2.size() << endl;
	cout << "s2的length为:  " << s2.length() << endl;
	cout << "s2的capacity为:" << s2.capacity() << endl;

	cout << "clear()清空s2之后的size为:    " << " ";
	s2.clear();
	cout << s2.size() << endl;
	cout << "clear()清空s2之后的capacity为:" << " ";
	cout << s2.capacity() << endl;
}
void testString2()
{
	string s = "hello sheena";
	cout << "初始状态:" << endl;
	cout << "size:    " << s.size() << endl;
	cout << "capacity:" << s.capacity() << endl;
	cout << endl;
	cout << "resize(n)之后状态(增容):" << endl;
	string s1(s);
	s1.resize(20);
	cout << "s1:      " << s1 << endl;
	cout << "size:    " << s1.size() << endl;
	cout << "capacity:" << s1.capacity() << endl;
	cout << endl;
	string s2(s);
	s2.resize(20, 'q');
	cout << "resize(n, c)之后状态(增容):" << endl;
	cout << "s2:      " << s2 << endl;
	cout << "size:    " << s2.size() << endl;
	cout << "capacity:" << s2.capacity() << endl;
	cout << endl;
	cout << "resize(n)之后状态(缩容):" << endl;
	string s3(s);
	s3.resize(10);
	cout << "s3:     " << s3 << endl;
	cout << "size:   " << s3.size() << endl;
	cout << "capacity:" << s3.capacity() << endl;
	cout << endl;

}
void Print1(const string s)
{
	for (int i = 0; i < s.size(); ++i)
		cout << s[i] << " ";
	cout << endl;
}
void Print2(const string s)
{
	cout << endl;
	cout << "遍历字符串方法二顺序遍历:" << " ";
	auto it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	cout << "遍历字符串方法二逆序遍历:" << " ";
	auto rit = s.rbegin();
	while (rit != s.rend())
	{
		cout << *rit << " ";
		++rit;
	}
	cout << endl;
}
void Print3(const string s)
{
	for (auto& e : s)
		cout << e << " ";
	cout << endl;
}
int main()
{
	cout << "testString1:" << endl;
	testString1();
	string s = "hello sheena!";
	cout << "遍历字符串方法一for循环:" << " ";
	Print1(s);
	cout << endl;
	cout << "遍历字符串方法二迭代器: " << " ";
	Print2(s);
	cout << endl;
	cout << "遍历字符串方法三范围for:" << " ";
	Print3(s);
	cout << endl;
	cout << endl;
	cout << "testString2:" << endl;
	testString2();

	return 0;
}

在这里插入图片描述

  • size()和length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致。一般情况下基本都是用size()。
  • clear()知识将string中有效字符清空,不改变底层空间大小。(上面的样例中clear()之后,size变成了0,而capacity依旧是15)
  • resize(size_t n)与resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时。resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时。如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。(如上面的样例所示,缩容之后capacity不变,改变的是size)
  • reserve(size_t res_arg = 0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserve不会改变容量大小。

2.string的模拟实现

//模拟实现一个简单的string类

#include <iostream>

using namespace std;

namespace sheena
{
	class my_string
	{
	public:
		//构造函数
		my_string(const char* str = "")//字符串""中有一个'\0'
			:_str(new char[strlen(str) + 1])//+1是为了加'\0'
		{
			if (nullptr == str)
				str = "";
			strcpy(_str, str);//strcpy可以拷贝'\0'
		}
		//拷贝构造函数
		my_string(const my_string& s)
			:_str(nullptr)
		{
			my_string tmp(s._str);
			swap(tmp._str, _str);
		}
		//赋值函数
		my_string& operator=(my_string s)
		{
			swap(s._str, _str);
			return *this;
		}
		//析构函数
		~my_string()
		{
			if (_str)
			{
				delete[]_str;
				_str = nullptr;
			}
		}
	private:
		char* _str;
	};
}

3.string和vector对比

(1)string中有’\0’,vector< char >中没有’\0’。
(2)string常要比较大小,vector< char >没必要比较大小。
(3)string重载输入输出,vector< char >用push_back等。

五、resize和reverse的区别

  • resize既分配了空间,也创建了对象。reserve只修改capacity大小,不修改size大小,resize既修改了size大小,也可以修改capacity的大小(当resize(n)中的n < capacity时)。
  • resever:仅仅改变空间(capacity)(如果已经知道需要多少空间直接开空间),提高效率,只有增容没有缩容(开空间时避免增容)。
  • resize:开空间+初始化。不仅改变空间(capacity),而且放了n个值进去(即改变了size),size可以缩小也可以放大。
  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值