1. 程式人生 > >順序表模板和雙向帶頭迴圈連結串列模板

順序表模板和雙向帶頭迴圈連結串列模板

類的類名和型別
從在類內定義順序表模板的建構函式和類內宣告類外定義建構函式來區別類名型別:

#include<iostream>
#include<stdlib.h>
#include<string>

using namespace std;

//順序表模板
//Seqlist 是類名
//Seqlist<T>是型別

template<class T>
class Seqlist 
{
public:
	//Seqlist()  //順序表模板的建構函式,在類裡定義,用的類名
	//	:_array(NULL)
	//	,_capacity(0)
	//	,_size(0)
	//{
	//}
	Seqlist();//在類裡宣告
private:
	T* _array;
	size_t _capacity;
	size_t _size;
};
template<class T>  //必須重新定義一個類模板
Seqlist<T>::Seqlist()//建構函式在類外定義,必須加上<T>,即用型別
	:_array(NULL)
	,_capacity(0)
	,_size(0)
{
}

如果在類外定義建構函式,需要用型別,否則會報錯:
在這裡插入圖片描述
接下來是順序表模板程式碼:

#include<iostream>
#include<stdlib.h>
#include<string>
#include<time.h>
#include<assert.h>
using namespace std;
template<class T>
class Seqlist
{
public:
	Seqlist()  //建構函式
		:_array(NULL)
		,_capacity(0)
		,_size(0)
	{
	}
	//Seqlist(const Seqlist& s) //拷貝建構函式
    Seqlist(const Seqlist<T>& s) //拷貝建構函式用型別和類名都可以
	{
		//深拷貝
		if (s._size != 0)
		{
			_array = new T[s._size]; //先開一樣大空間
			memcpy(_array, s._array, sizeof(T)*s._size);
		}
		else
			_array = NULL;
		_capacity = _size = s._size;				
	}

	//Seqlist& operator=(const Seqlist& s) //賦值
	Seqlist<T>& operator=(const Seqlist<T>& s) //賦值
	{
		if (this != &s)
		{
			if (s._size != 0)
			{
				delete[] _array; //把舊的釋放
				_array = new T[s._size];
				//memcpy(_array, s._array, sizeof(T)*s._size);
				for (size_t i = 0; i < s._size; i++)
				{
					_array[i] = s._array[i];  
					//如果是string類,就是賦值,string的賦值是深拷貝,同理下面的insert擴容拷貝
				}
			}
			else  //即s物件裡沒有資料,如果給this._array開空間,那麼this_array就有空間,而s.array沒有空間,就不是賦值
				_array = NULL;
			_capacity = _size = s._size;		
		}
		return *this;
	}

	//Seqlist<T>& operator=( Seqlist<T> s) //可以用型別,也可以用類名
	//Seqlist& operator=(Seqlist s)  //賦值的現代寫法
	//{
	//	swap(_array, s._array);
	//	swap(_size, s._size);
	//	swap(_capacity, s._capacity);
	//	return *this;
	//    //由於this的_array和s._array交換,那麼出了該棧幀後,物件s會調解構函式,那麼舊的_array會被釋放
	//}

	void Insert(size_t pos,const T& x) //在pos位置插入元素
	{
		assert(pos<=_size);//可以在_size位置插入元素,相當於尾插
		if (_size == _capacity)  //需要擴容
		{
			if (_capacity == 0)
			{
				_capacity = 3;
			}
			else
			{
				_capacity = _capacity * 2;
			}
			T* newarray = new T[_capacity];
			/*memcpy(newarray, _array, sizeof(T)*_size);*/
			//如果數組裡是string類,用註釋的拷貝方法,當擴容時,memcpy是值拷貝,
			//那麼新的newarray裡的元素會指向相同的字串空間,
			//但是delete[] _array後,由於_array裡存的是string類,類也會完成清理,
			//那麼就會將物件指向的那段字串空間釋放掉,但是當main函式結束時,
			//會調解構函式,新的_array會再次清理物件,就會造成對同一段空間的兩次釋放
			for (size_t i = 0; i < _size; i++)
			{
				newarray[i] = _array[i];//string類的賦值是深拷貝
			}
			delete[] _array;
			_array = newarray;
		}
		size_t end = _size;
		while (end > pos)
		{
			_array[end] = _array[end - 1];
			end--;
		}
		_array[pos] = x;
		_size++;
	}
	void Erase(size_t pos) //刪除pos位置元素
	{
		assert(pos <= _size);
		size_t start = pos;
		while (start < _size - 1 )
		{
			_array[start] = _array[start + 1];
			start++;
		}
		_size--;
	}
	void PushBack(const T& x) //也許插入元素是string類,需要用引用,減少一次拷貝
	{
		Insert(_size, x);
	}
	void PushFront(const T & x)
	{
		Insert(0, x);
	}
	void PopBack()
	{
		Erase(_size);
	}
	T& operator[](size_t pos)
	{
		return _array[pos];//如果不用引用返回,返回時是一個具有const屬性的臨時變數,
		//那麼用[]對該位置重寫,會報錯,因為不允許修改一個常屬性的值,所以要用引用,返回別名,就可以修改
	}
	size_t Size()
	{
		return _size;
	}
	~Seqlist() //解構函式
	{
		delete[] _array;
		_array = NULL;
		_capacity = _size = 0;
	}
private:
		T* _array;
		size_t _capacity;
		size_t _size;
};
void TestInt()
{
	Seqlist<int> s1;
	
	srand((unsigned)time(NULL));  //設定時間戳種子
	for (int i = 0; i < 10; i++)
	{
		int num = rand() % 100;
		cout << num << " ";
		s1.PushBack(num);
	}

	cout << "\n";
	for (size_t i = 0; i < s1.Size(); i++)
	{
		cout << s1[i] << " ";
	}
	cout << "\n";
	s1.Erase(3);
	s1[0] = 10;
	s1.PopBack();
	s1.PushFront(1);
	for (size_t i = 0; i < s1.Size(); i++)
	{
		cout << s1[i] << " ";
	}
	printf("\n");
	Seqlist<int> s2(s1);
	for (size_t i = 0; i < s2.Size(); i++)
	{
		cout << s2[i] << " ";
	}
	cout << "\n";
	for (size_t i = 0; i < s2.Size(); i++)
	{
		cout << s2[i] << " ";
	}
	
	
}
void Test()
{
	Seqlist<int> s1;
	Seqlist<int> s2(s1);
	srand((unsigned)time(NULL));  //設定時間戳種子
	for (int i = 0; i < 10; i++)
	{
		int num = rand() % 100;
		cout << num << " ";
		s2.PushBack(num);
	}
	printf("\n");
	for (size_t i = 0; i < s2.Size(); i++)
	{
		cout << s2[i] << " ";
	}
	printf("\n");
	Seqlist<int> s3;
	s3 = s1;
	s3.PushBack(3);
	for (size_t i = 0; i < s3.Size(); i++)
	{
		cout << s3[i] << " ";
	}

}
void TestString()
{
	Seqlist<string> s1;
	s1.PushBack("abcd");
	s1.PushBack("ab"); 
	s1.PushBack("mngg");
	s1.PushBack("loj");
	//s1.PushBack("logh");
	for (size_t i = 0; i < s1.Size(); i++)
	{
		cout << s1[i] << " ";
	}
	printf("\n");
	Seqlist<string> s2;
	s2 = s1;
	for (size_t i = 0; i < s2.Size(); i++)
	{
		cout << s2[i] << " ";
	}
}

下面是雙向帶頭迴圈連結串列模板程式碼:

#pragma once
#include<stdio.h>
#include<iostream>
#include<assert.h>
#include<string>
using namespace std;
//雙向帶頭迴圈連結串列模板
template<class T>
//每個結點元素的類
struct ListNode
{
	T _data;
	ListNode<T> *_next;
	ListNode<T> *_prev;
	ListNode(T data = T()) //建構函式
		//因為是泛型程式設計,T的型別不確定,所有用T()表示匿名物件資料,匿名物件資料用一次後會變為垃圾資料
	{
		_data = data;
		_next = NULL;
		_prev = NULL;
	}
};
template<class T>
class List
{

	typedef  ListNode<T>  Node;
public:
	List()  //建構函式
	{
		_head = new Node;
		_head->_next = _head;
		_head->_prev = _head;

	}

	// L2(L1)  //將連結串列L1拷貝構造給L2,即L2中要有L1中的節點元素
	List(const List<T>& L) //拷貝構造
	{
		//L2首先要有一個頭結點,並且頭結點指向自己
		_head = new Node;
		_head->_next = _head;
		_head->_prev = _head;


		//L2有了頭結點後,可以把L1的節點依次尾插在L2後,當然也可以申請空間,依次插入
		Node* cur = L._head->_next;
		while (cur != L._head)
		{
			//尾插
			Node* newnode = new Node(cur->_data);
			Node* tail =_head->_prev;
			// head   tail   newnode
			tail->_next = newnode;
			newnode->_prev = tail;
			newnode->_next = _head;
			_head->_prev = newnode;
			cur = cur->_next;
		}
	}
	List<T>operator=(List<T> L) //現代賦值
	{
		swap(_head, L._head);  //直接交換頭結點
		return *this;
	}
	void Insert(Node* pos, const T& data) //在pos前插入元素
	{
		assert(pos);
		Node* newnode = new Node(data);
		Node* prev = pos->_prev;
		//prev newnode pos
		prev->_next = newnode;
		newnode->_prev = prev;
		newnode->_next = pos;
		pos->_prev = newnode;
	}
	void PushFront(const T& data)
	{
		Insert(_head->_next, data);
	}
	void PushBack(const T& data)
	{
		Insert(_head, data);
	}
	void Erase(Node* pos)  //刪除pos位置結點
	{
		assert(pos != _head); //不能刪除頭結點
		Node* prev = pos->_prev;
		Node* next = pos->_next;
		// prev  pos  next 
		prev->_next = next;
		next->_prev = prev;
		delete pos;

	}
	void PopBack()  //尾刪
	{
		Erase(_head->_prev);
	}
	void PopFront() //頭刪
	{
		Erase(_head->_next);
	}
	void print()  //列印連結串列
	{

		Node* cur = _head->_next;
		while (cur != _head)
		{
			cout << cur->_data << " ";
			cur = cur->_next;
		}
		printf("\n");
	}
	Node* find(const T& data)
	{
		Node* cur = _head->_next;
		while (cur != _head)
		{
			if (cur->_data == data)
				return cur;
			cur = cur->_next;
		}
		return NULL;
	}
private:
	Node* _head;
};
int main()
{
	/*List<int> l1;
	
	l1.PushBack(1);
	l1.PushBack(2);
	l1.PushBack(3);
	List<int> l2(l1);
	l1.print();
	l2.print();
	List<int> l3;
	l3 = l2;
	l3.print();*/
	List<string> l1;
	l1.PushFront("abs");
	l1.PushFront("mkf");
	l1.PushFront("km");
	l1.PushBack("kn");
	l1.print();
	l1.Erase(l1.find("kn"));
	l1.print();
	List<string>l2(l1);
	l2.print();
	system("pause");
	return 0;

}

用模板後,不限於型別,很方便。