【C++】STL之顺序容器

什么是容器?

容器,置物之所也!研究数据的特定排列方式,以利于搜寻或排序或其它特殊目的,这一专门学科我们称为数据结构(Data Structures)。容器即是运用最广的一些数据结构实现出来的!常见的数据结构有数组(array)、链表(list)、堆栈(stack)、队列(queue)、树(tree)、哈希表(hash table)、集合(set)、映射表(map)等等,根据“数据在容器中的排列”特性,这些数据结构分为序列式(sequence)和关联式(associative) 两种。容器也有顺序容器和关联容器。

  • vector

vector和array(数组)十分相似,唯一区别就是vector是动态实现的,而数组是静态空间!vector的实现主要目的是实现对其大小的控制和重新配置时的数据移动效果.
为了降低空间配置时的速度成本,vector实际配置的大小可能比客户端需求量更大一些,以备将来可能的扩充。这便是容量(capacity) 的观念。换句话说,一个vector的容量水远大于或等于其大小。一旦容量等于大小,便是满载,下次再有新增元素,整个vector 就得另觅居所,见图4-2。运用start, finish, end of storage三个迭代器,便可轻易地提供首尾标示、大小。容量、空容器判断、注标([ ])运算子、最前端元素值、最后端元素值等等机能。

具体实现如下:

template<typename T>  									//根据用户的类型进行模板实例化
class Vector
{
public:
	Vector()  											//构造函数
	{
		parr = new T[10];                      			//动态开辟数组
		cursize = 0;
		totalsize = 10;
	}
	~Vector()  											//析构函数
	{
		delete[] parr;
		cursize = totalsize = 0;
	}
	void push_back(T val)  								//尾插
	{
		insert(cursize, val);
	}
	void insert(int pos, T val)  						//按位置插入
	{
		if (pos < 0 || pos > cursize)  					//参数检查
		{
			throw std::exception("pos is error!"); 		//抛出异常
		}
		if (full())
		{
			resize();
		}
		for (int index = cursize; index > pos; index--)  //后移数据
		{
			parr[index] = parr[index - 1];
		}
		parr[pos] = val;        					     //添加数据
		cursize++;					 					 //更新有效位置
	}
	T back() 			 								 //获取最后一个元素
	{
		if (empty())
		{
			throw std::exception("vector is empty!");
		}
		return parr[cursize - 1];  
	}
	bool empty()  										//判空
	{
		return cursize == 0;
	}
	void pop_back()										//尾删
	{
		erase(cursize - 1);
	}
	void erase(int pos)  								//按位置删除
	{
		if (pos < 0 || pos >= cursize || empty())    		  //参数检查
		{
			throw std::exception("pos is error!");    		  //抛出异常
		}
		for (int index = pos; index < cursize - 1; index++)   //数据前移
		{
			parr[index] = parr[index + 1]; 
		}
		cursize--; 											  //有效位置前移
	}
private:
	bool full()										    	  //判满
	{
		return cursize == totalsize;
	}
	void resize()											 //扩容
	{
		T* pnewspace = new T[totalsize * 2];   			//开辟二倍内存
		memcpy(pnewspace, parr, sizeof(T)*totalsize);   //拷贝旧资源
		delete[] parr;    								//释放旧资源
		parr = pnewspace;  								//统一接口
		totalsize *= 2;        							//更新成员变量
	}
	T* parr; 		//通过栈上内存管理堆上的内存
	int cursize;	//当前有效元素个数,也可以理解为下一个可插入元素的下标位置
	int totalsize;  //总的元素个数 ,相当于数组的长度,即数组的格子数
};
  • vector的使用

    #include<iostream>
      #include<vector>
      
      int main()
      {
      	std::vector<int> vec; 						  //定义vector容器
      	for (int i = 0; i < 10; i++)
      	{
      		vec.push_back(i + 1);
      	}
      
      	std::vector<int>::iterator it = vec.begin();  //定义迭代器
      	while (it != vec.end())
      	{
      		std::cout << *it << " ";   				    //遍历打印
      		it++;
      	}
      	std::cout << std::endl;
      
      	return 0;
      }
    
  • list

与vector的连续线性空间相比,list显得更加复杂,它的好处是删除,添加一个元素或删除一个元素,就配置或释放个元素空间。因此,list对于空间的运用有绝对的精准,一点也不浪费。而且,对于任何位置的元素插人或元素移除,时间复杂度都是O(1)。STL的底层对于list的实现是用双向链表完成的!具体实现如下:使用单链表实现

template<typename T>
class CLink;
template<typename T>
class Node   										//结点
{
public:
	Node(T val = T());
private:
	T mdata;
	Node<T>* pnext;
	//template<typename T>
	//friend class CLink;
	friend class CLink<T>;  						//友元
};
template<typename T>
Node<T>::Node(T val) :mdata(val), pnext(NULL)  		//结点构造函数
{
}
template<typename T>
class CLink
{
public:   											//函数声明
///////////////////////////////////////////////////////////////////////
	CLink();
	~CLink();
	void InsertHead(T val);
	void InsertTail(T val);
	void InsertPos(int pos, T val);

	void DeletePos(int pos);
	void Show();
	bool empty();
///////////////////////////////////////////////////////////////////////
private:
	//Node<T>* FindfrontNode(T val);
	Node<T>* phead;
};
template<typename T>
CLink<T>::CLink() 							 //构造函数
{
	phead = new Node<T>();
}
template<typename T>
CLink<T>::~CLink() 							 //析构函数
{
	Node<T>* pCur = phead;
	Node<T>* pNext = pCur;
	while (pCur != NULL)
	{
		pNext = pCur->pnext;
		delete pCur;
		pCur = pNext;
	}
	phead = NULL;
}
template<typename T>
void CLink<T>::InsertHead(T val)  		    		 //头插
{
	InsertPos(0, val);
	//Node<T>* pnewnode = new Node<T>(val);
	//pnewnode->pnext = phead->pnext;
	//phead->pnext = pnewnode;
}

template<typename T>
void CLink<T>::InsertTail(T val)  		   			 //尾插
{
	Node<T>* pnewnode = new Node<T>(val); 			 //开辟内存并初始化
	Node<T>* ptail = phead;
	while (ptail->pnext != NULL)    	  			 //找尾巴
	{
		ptail = ptail->pnext;
	}

	ptail->pnext = pnewnode;
}

template<typename T>
void CLink<T>::InsertPos(int pos, T val)   			//按位置插入
{
	Node<T>* pfront = phead;
	while (pos-- != 0)  //0  1     					//找位置
	{
		pfront = pfront->pnext;
	}
	Node<T>* pnewnode = new Node<T>(val);   		//创建新结点并初始化
	pnewnode->pnext = pfront->pnext;  				//新结点先连接
	pfront->pnext = pnewnode;                       //再指向新结点
}

template<typename T>
void CLink<T>::DeletePos(int pos)  		           //按位置删除
{
	if (empty())
	{
		throw std::exception("CLink is empty!");   //抛出异常
	}
	Node<T>* pfront = phead;                       //标记头结点
	while (pos-- != 0)  //0  1                     //找位置
	{
		pfront = pfront->pnext;
	}
	Node<T>* pCur = pfront->pnext;                 //标记要删除的结点
	pfront->pnext = pCur->pnext;                   //断开结点
	delete pCur;                                   //释放结点
}

template<typename T>
void CLink<T>::Show() 							   //打印
{
	Node<T>* pCur = phead->pnext;                  //标记数据结点
	while (pCur != NULL)                           //遍历数据结点
	{
		std::cout << pCur->mdata << " ";
		pCur = pCur->pnext;
	}
	std::cout << std::endl;
}
template<typename T>
bool CLink<T>::empty() 								//判空
{
	return phead->pnext == NULL;
}

list的使用

int main()
{
	std::list<int> lt;
	for (int i = 0; i < 5; i++)
	{
		lt.push_front(i + 1);
	}
	
	std::list<int>::iterator it = lt.begin();
	while(it != lt.end())
	{
		std::cout << *it << " ";
		it++;
	}
	std::cout << std::endl;
	lt.pop_back();
	lt.pop_front();
	
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章