剛剛接觸C++以及數據結構,今天做了第一次嘗試用C++和數據結構解決問題,問題是基於約瑟夫環問題的簡單版。
- 先來看看約瑟夫環問題的介紹:
約瑟夫環是一個數學的應用問題:已知n個人(以編號1,2,3...n分別表示)圍坐在一張圓桌周圍。從編號為k的人開始報數,數到m的那個人出列;他的下一個人又從1開始 報數,數到m的那個人又出列;依此規律重復下去,直到圓桌周圍的人全部出列。(摘自百度百科)
程序的輸入應該由三個值組成(n, k, m),接下來程序將會自動運行,本文假設每次都從第一個人開始報數,所以是做了一些簡化,但是換湯不換葯。
- 接下來將分部介紹程序的組成
首先是頭文件,命名為circle_single_linked_list.h
基本結構:
template <typename Object> class circleSingleLinkedList{ private:
/*循環單鏈表的每個的節點定義
*結構簡單,所以使用struct
*具體實現請看下文
*/
struct Node{ }; public:
/*內嵌類iterator
*是鏈表中的一個位置的抽象(abtracts the notion of a position)
*類比vector<>的iterator
*在類外的調用方法為:circleSingleLinkedList<int>::iterator ....
*本文省去了對於const_iterator類型的定義
*/ class iterator{ }; public:
/*循環鏈表實現
*由構造函數(含默認實參),big three都沒有...(程序比較簡陋)
*begin()返回表頭位置
*insert以及erase是實現約瑟夫環的主要手段
*循環鏈表只有2個數據成員, theSize存儲鏈表節點個數, head為頭指針, 指向鏈表中的第一個節點
*具體實現見下文
*/ circleSingleLinkedList(const Object& x = Object()){ } iterator begin()const{ } int size(){ return theSize; } bool empty(){ return size() == 0; } iterator insert(iterator itr, const Object& x){ } iterator erase(iterator itr){ } void print(ostream& out){ } private: int theSize; Node* head; };
由於編寫的是模板類,所以將類的定義,成員函數的實現,以及內嵌類的實現都放在了一個頭文件中,本人剛剛接觸C++,目前了解的情況是:模板類最好不要用 separate compilation,因為可能會造成complicated looking syntax,具體是什么我目前也不清楚,需要繼續學習。
- 接下來是各部分的具體實現
1 struct Node{ 2 Object data; //用於存放各個節點的數值,在約瑟夫環問題中,存放不同人的編號 3 Node* next; //每個單鏈表節點的指向下一個節點的指針 4 5 Node(const Object& d = Object(), Node* n=NULL) //默認構造函數 6 : data(d), next(n) {} 7 };
內嵌類iterator的實現
//iterator類主要包含了運算符重載:解引用,prefix++,postfix++,==,!=,具體的使用可以類比vector<>::iterator的使用方法.
1 class iterator{ 2 public: 3 iterator(): current(NULL){} 4 5 Object & operator*()const{ 6 return current->data; 7 } 8 9 iterator & operator++(){ 10 current = current->next; 11 return *this; 12 } 13 14 iterator operator++(int){ 15 iterator old = *this; 16 ++(*this); 17 return old; 18 } 19 20 bool operator==(const iterator& rhs)const{ 21 return current == rhs.current; 22 } 23 24 bool operator!=(const iterator& rhs)const{ 25 return !(*this == rhs); 26 } 27 28 protected: //protected一般在有繼承的情況下使用,這個地方我就懶得改了 29 Node* current; //iterator是鏈表中一個具體位置的抽象, current指針用於指向所選定的節點 30 31 iterator(Node* p): current(p){} //iterator類型主要使用的構造函數 32 33 friend class circleSingleLinkedList<Object>;//使外層包含類也可以訪問iterator的非public成員 34 };
剩下的就是循環單鏈表的定義,在這里我就想着重講一下insert以及erase方法,begin()函數,默認構造函數(初始化頭結點)以及print函數(輸出鏈表各節點的data數據)這里就不提了。
insert(位置,數值),因為這是單鏈表,不能通過一個位置來確定它之前的位置(沒有指向前一個節點的指針),所以insert是在itr所確定的元素的后面插入一個新節點,函數返回新節點處的位置.
需要注意的是,insert與erase都是的返回類型都是copy passing的,具體原因我暫時還搞不清楚.
1 iterator insert(iterator itr, const Object& x){ 2 Node* p = itr.current; 3 Node* newNode = new Node(x, p->next); 4 p->next = newNode; 5 theSize++; 6 return iterator(newNode); 7 }
erase函數
erase(位置),刪除itr位置之后的元素,原理與insert相同,如果刪除的元素師頭節點,則將頭節點分配給原頭節點的下一個元素
1 iterator erase(iterator itr){ 2 Node* p = itr.current; 3 if(p->next == head) 4 head = head->next; 5 Node* old = p->next->next; 6 delete p->next; 7 p->next = old; 8 theSize--; 9 return itr; 10 }
- 接下來我們來看一下main函數中的實際應用情況,也就是約瑟夫環問題。
1 int main() 2 { 3 circleSingleLinkedList<int> iList(1); //初始化循環鏈表,其包含一個頭節點,由head指針指向,頭指針指向的元素就是約瑟夫環問題中的第一個人,編號為1 4 5 int n; 6 cout << "n=" << endl; //輸入約瑟夫環問題中的人數n 7 cin >> n; 8 9 int i=2; 10 circleSingleLinkedList<int>::iterator iter = iList.begin(); //初始化iter指向頭節點 11 while(i <= n){ 12 iter = iList.insert(iter,i); //為鏈表添加節點元素,每一次都添加到iter所抽象的元素的下一個,其數值為i 13 ++i; 14 } 15 16 iter = iList.begin(); //iter重新指向表頭 17 18 int m; 19 cout << "m=" << endl; //輸入約瑟夫環問題中的步值m,也就是每做一次刪除所數的一個步長 20 cin >> m; 21 22 i = 1; 23 while(iList.empty() != true){ 24 while(i < m){ //由於erase是刪除iter所指向的下一個元素,而不是刪除iter,所以iter需要代表被刪除元素之前的一個元素. 25 ++iter; 26 ++i; 27 } 28 iter = iList.erase(iter); 29 iList.print(cout); //每做一次刪除就輸出顯示整個list. 30 i = 1; 31 } 32 33 cout << endl; 34 35 return 0; 36 }
- 最后結果:

輸入n=10, m=3, 由於我默認程序從第一個節點(1)開始數,所以第一個刪除的節點是4(也就是編號為4的人),刪除之后,從4的下一個節點(5)開始數3,第二個刪除的元素為8,以此類推.
- 總結:這是第一次用數據結構和C++解決實際問題,也是第一次發博文,目前對數據結構的理解是,其是對內存空間進行某種特定規律的管理的一種手段,以特殊的策略實現功能程序的優化。
希望繼續練習,熟悉更多的結構。
2013-11-08 11:12:23
