十四、循環鏈表的實現
阿新 • • 發佈:2018-09-17
但是 this 內部 his 多少 next find函數 ins code
1、循環鏈表簡介
概念上:
- 任意數據元素都有一個前驅和一個後繼
- 所有的數據元素的關系構成一個邏輯上的環
實現上:
- 循環鏈表是一種特殊的單鏈表
- 尾結點的指針域保存了首結點的地址
循環鏈表的繼承層次結構
2、循環鏈表的實現思路
- 通過模板定義
CircleList
類,繼承自LinkList
類 - 定義內部函數
last_to_first()
,用於將單鏈表首尾相連 - 特殊處理:首元素的插入操作和刪除操作
- 重新實現:清空操作和遍歷操作
循環鏈表的實現要點:
- 插入位置為0時:插入的結點成為首結點
- 頭結點和尾結點均指向新結點
- 新節點成為首結點插入鏈表
- 刪除位置為0時:
- 頭結點和尾結點指向位置為1的結點
- 安全銷毀首結點
3、循環鏈表的具體實現
#ifndef CIRCLELIST_H #define CIRCLELIST_H #include "LinkList.h" namespace DTLib { template <typename T> class CircleList : public LinkList<T> { protected: typedef typename LinkList<T>::Node Node; int mod(int i) const { return (this->m_length == 0)? 0 : (i % this->m_length); } Node* last() const // 獲得指向最後一個結點的指針 { return this->position(this->m_length-1)->next; } void last_to_first() { last()->next = this->m_header.next; } public: bool insert(const T& e) { return insert(this->m_length, e); } bool insert(int i, const T& e) { bool ret = true; i = i % (this->m_length + 1); // 對i進行歸一化處理 ret = LinkList<T>::insert(i, e); // 用父類的insert函數來實現 // 註意首尾相連 if(ret && ( i == 0)) { last_to_first(); } return ret; } bool remove(int i) { bool ret = true; i = mod(i); if(i == 0) { Node* toDel = this->m_header.next; if(toDel != NULL) { this->m_header.next = toDel->next; this->m_length--; if(this->m_length > 0) // 在還有元素的時候挪動 { last_to_first(); if(this->m_current == toDel) { this->m_current = toDel->next; } } else { this->m_header.next = NULL; this->m_current = NULL; } this->destory(toDel); } else { ret = false; } } else {// 刪除非首結點 ret = LinkList<T>::remove(i); } return ret; } bool set(int i, const T& e) { return LinkList<T>::set(mod(i), e); } T get(int i) const { return LinkList<T>::get(mod(i)); } bool get(int i, const T& e) const { return LinkList<T>::get(mod(i), e); } int find(const T& e) const { int ret = -1; // last()->next = NULL; // 將尾結點的next指針置空,循環鏈表變成了單鏈表 // ret = LinkList<T>::find(e); // last_to_first(); // 但是這樣就改變了循環鏈表的狀態,不能這樣幹,因為find裏面可能發生異常,循環鏈表就變成單鏈表了 // 不是異常安全的 // 需要重新實現find函數 Node* slider = this->m_header.next; for(int i = 0; i < this->m_length; i++) { if(slider->value == e) { ret = i; break; } slider = slider->next; } // 異常安全,比較的時候就算發生異常,也不會造成循環鏈表的狀態改變 return ret; } void clear() { if(this->m_length > 0) { // last()->next = NULL; // LinkList<T>::clear(); // 同樣的問題,clear裏面如果發生異常,不能保證異常安全性 while( this->m_length > 1) { remove(1); // 只要當前結點的長度大於1,就將結點1刪除,直到所有元素刪除 // 不用remove(0)的原因是:考慮到效率問題 // 每次remove(0)都會調用last_to_first等一大批操作 // 刪除非首結點就快很多 // 所以選擇刪除結點1,這樣最後就只會剩下結點0和首結點,再單獨處理 } if(this->m_length == 1) { Node* toDel = this->m_header.next; this->m_header.next = NULL; this->m_length = 0; this->m_current = NULL; this->destory(toDel); } } } // 重新實現遍歷操作 bool move(int i, int step) { return LinkList<T>::move(mod(i), step); } bool end() { return (this->m_length == 0) || (this->m_current == NULL); } ~CircleList() { clear(); } }; } #endif // CIRCLELIST_H
4、循環鏈表的應用
約瑟夫環問題
已知n個人(以編號1,2,3...n分別表示)圍坐在一張圓桌周圍。從編號為k的人開始報數,數到m的那個人出列;他的下一個人又從1開始報數,數到m的那個人又出列;依此規律重復下去,直到圓桌周圍的人全部出列。
// n 人數, s 第幾號開始報數, m 報多少 void josephus(int n, int s, int m) { CircleList<int> cl; for(int i = 1; i <= n; i++) { cl.insert(i); } cl.move(s-1, m-1); while(cl.length() > 0) { cl.next(); cout << cl.current() << endl; cl.remove(cl.find(cl.current())); } } int main() { josephus(41, 1, 3); return 0; }
5、小結
循環鏈表是一種特殊的單鏈表
尾結點的指針域保存了首結點的地址
特殊處理首元素的插入操作和刪除操作
重新實現清空操作和遍歷操作
十四、循環鏈表的實現