【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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章