第二十九课 循环链表的实现
Posted wanmeishenghuo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第二十九课 循环链表的实现相关的知识,希望对你有一定的参考价值。
循环链表的继承层次结构:
插入位置为0图解:
删除位置为0图解:
添加CircleList.h文件:
1 #ifndef CIRCLELIST_H 2 #define CIRCLELIST_H 3 4 #include "LinkList.h" 5 6 namespace DTLib 7 { 8 9 template < typename T > 10 class CircleList : public LinkList<T> 11 { 12 protected: 13 typedef typename LinkList<T>::Node Node; 14 15 int mod(int i) const // 不能对0取余,需特殊处理 16 { 17 return (this->m_length == 0) ? 0 : (i % this->m_length); 18 } 19 20 // last获取最后一个节点的指针 21 // position返回i-1个节点,再取next可以获取最后一个节点的指针 22 Node* last() const 23 { 24 return this->position(this->m_length - 1)->next; 25 } 26 27 void last_to_first() const //将链表首尾相连 28 { 29 last()->next = this->m_header.next; 30 } 31 32 public: 33 bool insert(const T& e) 34 { 35 return insert(this->m_length, e); 36 } 37 38 bool insert(int i, const T& e) 39 { 40 bool ret = true; 41 42 i = i % (this->m_length + 1); 43 44 ret = LinkList<T>::insert(i, e); 45 46 if(ret && (i == 0)) 47 { 48 last_to_first(); 49 } 50 51 return ret; 52 } 53 54 bool remove(int i) 55 { 56 bool ret = true; 57 58 i = mod(i); 59 60 if(i == 0) 61 { 62 Node* toDel = this->m_header.next; 63 64 if( toDel != NULL ) 65 { 66 this->m_header.next = toDel->next; 67 this->m_length--; 68 69 if( this->m_length > 0 ) 70 { 71 last_to_first(); 72 73 if( this->m_current == toDel ) 74 { 75 this->m_current = toDel->next; 76 } 77 } 78 else 79 { 80 this->m_header.next = NULL; 81 this->m_current = NULL; 82 } 83 84 this->destroy(toDel); 85 } 86 else 87 { 88 ret = false; 89 } 90 } 91 else 92 { 93 ret = LinkList<T>::remove(i); 94 } 95 96 return ret; 97 } 98 99 bool set(int i, const T& e) 100 { 101 return LinkList<T>::set(mod(i), e); 102 } 103 104 T get(int i) const 105 { 106 return LinkList<T>::get(mod(i)); 107 } 108 109 bool get(int i, T& e) const 110 { 111 return LinkList<T>::get(mod(i), e); 112 } 113 114 int find(const T& e) const 115 { 116 int ret = -1; 117 118 //先变成单链表,这样就可以直接调用find了,find过后再恢复为循环链表 119 //但是这是有问题的,因为find中可能抛出异常 120 //last()->next = NULL; 121 //ret = LinkList<T>::find(e); 122 //last_to_first(); 123 124 Node* slider = this->m_header.next; 125 126 for(int i = 0; i < this->m_length; i++) 127 { 128 if( slider->value == e ) 129 { 130 ret = i; 131 break; 132 } 133 134 slider = slider->next; 135 } 136 137 return ret; 138 } 139 140 void clear() 141 { 142 while( this->m_length > 1) 143 { 144 remove(1); // 每次删除第一个元素,提高效率,每次删除第0个元素效率低 145 } 146 147 if( this->m_length == 1) 148 { 149 Node* toDel = this->m_header.next; 150 151 this->m_header.next = NULL; 152 this->m_length = 0; 153 this->m_current = NULL; 154 155 this->destroy(toDel); 156 } 157 } 158 159 bool move(int i, int step) 160 { 161 return LinkList<T>::move(mod(i), step); 162 } 163 164 bool end() 165 { 166 return (this->m_length == 0) || (this->m_current == NULL); 167 } 168 169 ~CircleList() 170 { 171 clear(); 172 } 173 }; 174 175 } 176 177 #endif // CIRCLELIST_H
我们将LinkList中的函数全部做成了virtual虚函数,因为有可能将CircleList当LinkList使用。在CircleList中我们重新实现了很多函数。
在重新实现的find函数中,120--122行也能实现功能,但是这不是异常安全的,先将循环链表变为单链表,然后查找,最后再变为循环链表。在查找的过程中有泛指类型T的比较操作,因此,有可能抛出异常。抛出异常后就无法变为循环链表了,破坏了循环链表,因此,不是异常安全的。为了移植性,我们也不选择使用try catch,而是重新实现了find函数。
mod函数要定义为const的,否则在get成员函数中使用mod会报错。
例如:
CircleList的clear函数也是重新实现,不能直接变为单链表,然后调用父类的clear,因为也存在异常安全性。
我们重新实现了clear,在m_length大于1时,每次删除第一个元素,这样可以提高效率。如果每次删除第0个元素,则还要涉及到 last_to_first操作,这时效率非常低的。
循环链表的应用:
测试程序如下:
1 #include <iostream> 2 #include "CircleList.h" 3 4 using namespace std; 5 using namespace DTLib; 6 7 void josephus(int n, int s, int m) 8 { 9 CircleList<int> cl; 10 11 for(int i=1; i <= n; i++) 12 { 13 cl.insert(i); 14 } 15 16 // 报数到m就自杀,也就是移动m-1次就自杀,因此,下面传入的参数是m-1 17 cl.move(s-1, m - 1); 18 19 while( cl.length() > 0 ) 20 { 21 cl.next(); 22 cout << cl.current() << endl; 23 cl.remove(cl.find(cl.current())); 24 } 25 } 26 27 int main() 28 { 29 //41个人玩游戏, 从1开始数数, 数到3就自杀 30 josephus(41, 1, 3); 31 32 return 0; 33 }
运行结果如下:
小结:
以上是关于第二十九课 循环链表的实现的主要内容,如果未能解决你的问题,请参考以下文章
JAVA学习第二十九课(经常使用对象API)- String类