从最初学习C语言到C++,就有一个认识就是作为一个优秀的程序猿一定要学会 ”偷懒“ ,用程序员的话来说就是复用,在C的基础上开发的C++就深刻阐明了这一道理,因为这样有太多的好处,增加了代码的可移植性,提高了可维护性。C++的模板就很有用,可以大大提高写代码的效率,同时优秀的代码总是最简洁最有效的代码。下面我们来用模板实现链表和顺序表。
顺序表
SeqList.h文件
#define _CRT_SECURE_NO_DEPRECATE #include <iostream> #include <assert.h> #include <string> using namespace std; template <typename T> class SeqList { private: T* _a; size_t _size; size_t _capacity; public: SeqList() :_a(NULL) , _size(0) , _capacity(0) {} SeqList(const SeqList<T>& s) { //_a = (T*)malloc(sizeof(T)*s._size); _a = new T[s._size]; //memcpy(_a, s._a, sizeof(T)*s._size); for (size_t i = 0; i < s._size; ++i) { _a[i] = s._a[i]; } _size = s._size; _capacity = s._capacity; } void Swap(SeqList& s) { swap(_a, s._a); swap(_size, s._size); swap(_capacity, s._capacity); } SeqList<T>& operator=(SeqList<T> s) { Swap(s); return *this; } ~SeqList() { if (_a != NULL) { delete[] _a; //free(_a); _size = _capacity = 0; } } void PushBack(const T& x) { _CheckCapacity(); _a[_size] = x; _size++; } void PopBack() { if (_size > 0) { --_size; } } void PushFront(const T& x) { _CheckCapacity(); if (_size == 0) { _a[0] = x; ++_size; } else { int i = 0; for (i = _size; i > 0; i--) { _a[i] = _a[i - 1]; } _a[0] = x; ++_size; } } void _CheckCapacity() { if (_size >= _capacity) { _capacity = _capacity * 2 + 3; //_a = (T*)realloc(_a, _capacity*sizeof(T)); T* tmp = new T[_capacity]; if (_a != NULL) { //memcpy(tmp, _a, sizeof(T)*_size); //适用于内置类型 for (size_t i = 0; i < _size; ++i) { tmp[i] = _a[i]; } delete[] _a; } _a = tmp; } } void PopFront() { if (_size == 1) { --_size; } else { for (size_t cout = 1; cout < _size; cout++) { _a[cout - 1] = _a[cout]; } --_size; } } void Insert(size_t pos, const T& x) { assert(pos > 0 && pos < _size); _CheckCapacity(); if (pos == 1) { PushFront(x); } else if (pos == _size-1) { PushBack(x); } else { for (size_t i = _size; i >= pos; i--) { _a[i] = _a[i - 1]; } _a[pos - 1] = x; _size++; } } void Erase(size_t pos) { assert(pos > 0 && pos < _size); if (pos == 1) { PopFront(); } else if (pos == _size - 1) { PopBack(); } else { for (size_t i = pos - 1; i < _size; i++) { _a[i] = _a[i + 1]; } _size--; } } int Find(const T& x) { for (size_t i = 0; i < _size; i++) { if (_a[i] == x) { return i; } } return -1; } void Print() { for (size_t i = 0; i < _size; ++i) { cout << _a[i] << " "; } cout << endl; } T& Back() { assert(_size > 0); return _a[_size - 1]; } size_t Size() { return _size; } bool Empty() { return _size == NULL; } }; template <class T, class Container = SeqList<T>> class Stack { public: void Push(const T& x) { _con.PushBack(x); } void Pop() { _con.PopBack(); } const T& Top() { return _con.Back(); } size_t Size() { return _con.Size(); } bool Empty() { return _con.Empty(); } protected: Container _con; };
SeqList.cpp
#include "SeqList.h" void TestSeqList1() { SeqList<int> S1; S1.PushBack(1); S1.PushBack(2); S1.PushBack(3); S1.PushBack(4); S1.Print(); S1.PopBack(); S1.PopBack(); S1.PopBack(); S1.PopBack(); S1.Print(); } void TestSeqList2() { SeqList<int> S2; S2.PushFront(1); S2.PushFront(2); S2.PushFront(3); S2.PushFront(4); S2.Print(); S2.PopFront(); S2.PopFront(); S2.PopFront(); S2.PopFront(); S2.Print(); } void TestSeqList3() { SeqList<int> S3; S3.PushBack(1); S3.PushBack(2); S3.PushBack(3); S3.PushBack(4); S3.Insert(1, 5); S3.Insert(2, 6); S3.Insert(4, 7); S3.Print(); S3.Erase(1); S3.Erase(3); S3.Print(); } void TestSeqList4() { SeqList<int> S4; S4.PushBack(1); S4.PushBack(2); S4.PushBack(3); S4.PushBack(4); S4.Print(); SeqList<int> S2(S4); S2.Print(); SeqList<int> S3; S3 = S2; S3.Print(); } void TestSeqList5() { SeqList<string> S1; S1.PushBack("11"); S1.PushBack("22"); S1.PushBack("33"); S1.PushBack("44"); S1.Print(); S1.PopBack(); S1.PopBack(); S1.PopBack(); S1.PopBack(); S1.Print(); } void TestStack() { Stack<int> s; s.Push(1); s.Push(2); s.Push(3); while (!s.Empty()) { cout << s.Top() << " "; s.Pop(); } cout << endl; } int main() { TestSeqList1(); TestSeqList2(); TestSeqList3(); TestSeqList4(); TestSeqList5(); TestStack(); system("pause"); return 0; }
其实之前用C++和C都写过顺序表和链表,而是用模板只是编写与类型无关的代码,只需要修改类中和函数中有关类型的代码即可。同时还实现了适配器的 栈 因为栈是先进后出,所以我们用顺序表实现起来更加高效,只需要在原来的类中增加适配器能够调用类的接口,也是复用减少了代码量。适配器模式的栈需要注意模板的第二个容器参数。
List.h
#define _CRT_SECURE_NO_DEPRECATE #include <iostream> #include <assert.h> using namespace std; template <typename T> struct ListNode { ListNode<T>* _next; ListNode<T>* _prev; T _data; ListNode(const T& x) :_data(x) , _next(NULL) , _prev(NULL) {} }; template <typename T> class List { typedef ListNode<T> Node; protected: Node* _head; Node* _tail; public: List() :_head(NULL) , _tail(NULL) {} //L2(L1) List(const List<T>& L) :_head(NULL) , _tail(NULL) { if (L._head == NULL) { _head = _tail = NULL; } else { Node* head = L._head; while (head) { PushBack(head->_data); head = head->_next; } } } void Distory() { while (_head) { Node* del = _head; _head = _head->_next; delete del; } _head = _tail = NULL; } ~List() { Distory(); } //List& operator=(const List& L) //{ // if (this != &L) // { // Distory(); // Node* head = L._head; // while (head) // { // PushBack(head->_data); // head = head->_next; // } // } // return *this; //} void Swap(List<T> &l) { swap(_head, l._head); swap(_tail, l._tail); } //List& operator=(List L) //{ // Swap(L); // return*this; //} List<T>& operator=(List<T> &L) { if (this != &L) { Swap(L); } return *this; } void PushBack(const T& x) { if (_head == NULL) { _head = _tail = new Node(x); return; } else { Node* tmp = new Node(x); _tail->_next = tmp; tmp->_prev = _tail; _tail = _tail->_next; } } void PopBack() { if (_head == NULL) { return; } else if (_head == _tail) { delete _head; _head = _tail = NULL; return; } else { Node* del = _tail; _tail = _tail->_prev; delete del; _tail->_next = NULL; } } void PushFront(const T& x) { if (_head == NULL) { _head = _tail = new Node(x); return; } else { Node* tmp = new Node(x); _head->_prev = tmp; tmp->_next = _head; _head = _head->_prev; } } void PopFront() { assert(_head); if (_head->_next == NULL) { delete _head; _head = _tail = NULL; } else { Node* del = _head; _head = _head->_next; delete del; _head->_prev = NULL; } } void Insert(Node* pos, const T& x) { assert(pos); if (pos == _head) { PushFront(x); } else { Node* tmp = new Node(x); tmp->_next = pos; pos->_prev->_next = tmp; tmp->_prev = pos->_prev; pos->_prev = tmp; } } void Erase(Node* pos) { assert(pos); if (pos == _head) { PopFront(); //if (_head->_next == NULL) //{ // _head = _tail = NULL; //return; //} //else //{ // _head = pos->_next; // pos->_prev = NULL; //} } else { if (pos->_next == NULL) { pos->_prev->_next = NULL; } else { Node* del = pos; pos->_prev->_next = pos->_next; pos->_next->_prev = pos->_prev; delete del; } } } Node* Find(const T& x) { Node* p = _head; while (p) { if (p->_data == x) { cout << "Fint it" << endl; return p; } p = p->_next; } cout << "Not find" << endl; return NULL; } void Reverse() //翻转字符串 { if (_head == NULL || _head->_next == NULL) { return; } else { Node* head = _head; Node* tail = _tail; while (tail->_next != head && head != tail) { swap(head->_data, tail->_data); head = head->_next; tail = tail->_prev; } } } //void Reverse() //{ // if (_head == NULL || _head->_next == NULL) // { // return; // } // else // { // Node* head = _head; // while (head->_prev != NULL) // { // Node* tmp = NULL; // tmp = _head->_next; // _head->_next = _head->_prev; // _head->_prev = tmp; // head = head->_prev; // } // Node* tmp = _head; // _head = _tail; // _tail = _head; // } //} void Print() { if (_head == NULL) { cout << "The List is NULL" << endl; return; } else { Node* p; p = _head; while (p != NULL) { cout << p->_data << " "; p = p->_next; } cout << endl; } } T& Front() { assert(_head); return _head->_data; } T& Back() { assert(_tail); return _tail->_data; } size_t _Size() { size_t count = 0; Node* cur = _head; while (cur) { ++count; cur = cur->_next; } return count; } bool Empty() { return _head == NULL; } }; //适配器模式实现队列 template <class T, class Container = List<T>> class Queue { public: void Push(const T& x) { _con.PushBack(x); } void Pop() { _con.PopFront(); } T& Front() { return _con.Front(); } T& Back() { return _con.Back(); } size_t Size() { return _con._Size; } bool Empty() { return _con.Empty(); } protected: Container _con; };
List.cpp
#include "List.h" void TestSList1() { List<int> L1; L1.PushBack(1); L1.PushBack(2); L1.PushBack(3); L1.PushBack(4); L1.Print(); L1.PopBack(); L1.PopBack(); L1.PopBack(); L1.PopBack(); L1.Print(); } void TestSList2() { List<int> L1; L1.PushFront(1); L1.PushFront(2); L1.PushFront(3); L1.PushFront(4); L1.Print(); L1.PopFront(); L1.PopFront(); L1.PopFront(); L1.PopFront(); L1.Print(); } void TestSList3() { List<int> L1; L1.PushFront(1); L1.PushFront(2); L1.PushFront(3); L1.PushFront(4); L1.Print(); L1.Insert(L1.Find(1), 5); L1.Print(); L1.Insert(L1.Find(3), 6); L1.Print(); L1.Insert(L1.Find(5), 7); L1.Print(); L1.Erase(L1.Find(1)); L1.Erase(L1.Find(3)); L1.Erase(L1.Find(5)); L1.Print(); } void TestSList4() { List<int> L1; L1.PushFront(4); L1.PushFront(3); L1.PushFront(2); L1.PushFront(1); L1.Print(); L1.Reverse(); L1.Print(); } void TestSList5() { List<int> L1; L1.PushBack(1); L1.PushBack(2); L1.PushBack(3); L1.PushBack(4); L1.Print(); List<int> L2(L1); L2.Print(); List<int> L3; L3.PushBack(5); L3.PushBack(6); L3.PushBack(7); L2 = L3; L2.Print(); } void TestQueue() { //Queue<int, List<int>> q; Queue<int> q; q.Push(1); q.Push(2); q.Push(3); while (!q.Empty()) { cout << q.Front() << " "; q.Pop(); } cout << endl; } int main() { //TestSList1(); //TestSList2(); //TestSList3(); //TestSList4(); //TestSList5(); TestQueue(); system("pause"); return 0; }
链表的模板形式与顺序表也类似,需要修改节点指针的类型,把所有关于类型的参数用模板参数替代,同时也实现了模板适配器的 队列 先进先出,用链表用顺序表链表都可以实现,而链表的结构来说更高效,所以适配器的模板第二个参数默认为class Container = List<T> ,给他提供默认的容器参数。
最后有一点要说的是实现顺序表的时候存在一个深拷贝层次的浅拷贝,在模板类型为string时,因为系统默认提供了一段buffer,假如顺序表的一个元素<buffer的长度就直接把这个元素存在buffer中,所以之前的C++顺序表代码并看不出什么错误,因为开辟新空间和释放并不影响,而当所传的元素大于>buffer的时候buffer中存的是这个元素的指针,那么当我们的CheckCapacity()函数重新开辟大小的时候把这个指针考下来的时候就又出现了浅拷贝,两个指针同时指向所传的那个元素,所以当旧的那块空间析构时候就出现了问题,所以我们为了避免这个问题,就使用了for循环这中效率比较低的拷贝方式,而我们想要其他内置类型的时候提高效率依旧使用memcpy这种高效的拷贝,这里有一种辨别模板参数类型的办法---(类型萃取)可以有效的解决这个问题。