1. 什么是循环链表
1.1概念
- 任意数据元素都有一个前驱(地址)和一个后继(地址)
- 所有的数据元素的关系构成一个逻辑上的环
1.2实现
- 循环链表是一种特殊的单链表
- 尾节点的指针保存了首节点的地址
2. 循环链表的逻辑构成
继承层次结构
3. 循环链表的实现思路
(1)通过模板定义CircleList类,继承自LinkList类
(2)定义内部函数makeCircle(),用于将单链表首尾相连
(3)特殊处理:首元素的插入操作和删除操作
①插入位置为0时:头结点和尾结点均指向新结点;新结点成为首结点。
②删除位置为0时:头结点和尾结点指向位置为1的结点;安全销毁首结点。
(4)重新实现:清空操作和遍历操作
【编程实验】循环链表的实现
//LinkList.h
1 #ifndef LINKLIST_H
2 #define LINKLIST_H
3 #include "List.h"
4 namespace DataStructureLib
5 {
6 template <typename T>
7
8 class LinkList:public List<T>
9 {
10 protected:
11 struct Node{
12 T value;
13 Node* next;
14 };
15
16 mutable Node m_header;//头结点 、mutable为了让get函数中的const属性导致的&m_header(编译器认为是要修改成员变量)mutable就允许const成员函数取地址
17 int m_length;//链表的长度
18
19 Node* position(int i) const//返回第i和元素的指针
20 {
21 Node* ret=&m_header;
22
23 for(int p=0;p<i;p++)
24 {
25 ret=ret->next;
26 }
27
28 return ret;//元素地址保存在该节点的next指针域中
29 }
30
31 //游标
32 int m_step;
33 Node* m_current ;
34 public:
35 LinkList()
36 {
37 m_header.next=NULL;
38 m_length=0;
39 m_step=1;
40 m_current=NULL;
41 }
42 bool insert(const T &elem) //O(n)
43 {
44 return insert(m_length, elem);
45 }
46
47 bool insert(int index, const T& elem)//思路:1.找到index位置处的元素;2.在该元素尾部insert新元素
48 {
49 bool ret=(index<=m_length)&&(index>=0);
50
51 if (ret)
52 {
53 Node* NewNode=createNode() ;
54
55 if (NULL!=NewNode)
56 {
57 NewNode->value=elem;
58
59 Node* currentNode=position(index);
60 NewNode->next=currentNode->next;
61 currentNode->next=NewNode;
62
63 m_length++;
64 }
65 else{
66 throw("has Not enougth memory to insert new element ...");
67 }
68
69 }
70 return ret;
71 }
72
73 bool remove(int index)
74 {
75 bool ret=((index<=m_length)&&(index>=0));
76
77 if (ret)
78 {
79 Node* CurrentNode=position(index);
80 Node* toDelNode=CurrentNode->next;
81 if(this->m_current==toDelNode)
82 {
83 this->m_current=toDelNode->next;
84 }
85 CurrentNode->next=toDelNode->next;
86
87 delete toDelNode ;
88 m_length--;
89 }
90
91 return ret;
92 }
93
94 bool set(int index,const T& e)
95 {
96 bool ret=((0<=index)&&(index<=m_length));
97
98 if (ret)
99 {
100 Node* CurrentNode=position(index);
101 CurrentNode->next->value=e;
102 }
103
104 return ret;
105 }
106
107 bool get(int index, T& elem) const
108 {
109 bool ret=((index<=m_length)&&(index>=0));
110
111 if (ret)
112 {
113 Node* CurrentNode=position(index);
114 elem= CurrentNode->next->value;
115 }
116
117 return ret;
118 }
119
120 T get(int index)
121 {
122 T ret;
123 if((index<m_length)&&(0<=index))
124 {
125 Node* CurrentNode=position(index);
126 ret= CurrentNode->next->value;
127 }
128
129 return ret;
130 }
131 int getlength() const
132 {
133 return m_length;
134
135 }
136
137 void clear()
138 {
139
140 while (m_header.next)
141 {
142 Node* toDelNode=m_header.next;
143 m_header.next=toDelNode->next;
144 delete toDelNode;
145 }
146 m_length=0;
147 }
148
149 //寻找e元素所在的位置,
150 //返回值 失败:-1 成功:e元素所在的位置的id
151 int find(T& e)
152 {
153 int ret = -1;
154 for (int i=0;i<m_length;i++)
155 {
156 if (e==get(i))
157 {
158 ret=i;
159 }
160 }
161
162 return ret;
163 }
164
165 bool move(int i,int step=1)//将游标定位到目标位置,//i代表目标位置 step游标每次移动的节点的数目
166 {
167 bool ret= (0<=i)&&(i<m_length)&&(step>0);
168
169 if (ret)
170 {
171 m_current=position(i)->next;
172 m_step=step;
173 }
174
175 return ret;
176
177 }
178
179 bool end()//游标有无到达链表尾部
180 {
181 return (m_current==NULL);
182 }
183
184 T current()//获取游标所指向的数据元素
185 {
186 if(!end())
187 {
188 return m_current->value ;
189 }
190 else
191 {
192 throw("No Value at current position...");
193 }
194 }
195
196 //移动游标 相当于每次移动的大小m_step
197 bool next()
198 {
199
200 int i=0;
201
202 while ((m_step>i)&&(!end()))
203 {
204 m_current=m_current->next;
205 i++;
206 }
207
208 return (i==m_step);
209 }
210
211 //封装节点创建和销毁函数,以便子类可以重载这个函数。
212 virtual Node* createNode()
213 {
214 return new Node();
215 }
216
217 virtual void destroyNode(Node* pn)
218 {
219 delete pn;
220 }
221
222 ~LinkList()
223 {
224 clear();
225 }
226 };
227 }
228 #endif
//CircleList.h
1 #ifndef CIRCLELIST_H
2 #define CIRCLELIST_H
3
4 #include "LinkList.h"
5
6 namespace DataStructureLib
7 {
8 template <typename T>
9
10 class CircleList:public LinkList<T>
11 {
12 protected:
13 typedef typename LinkList<T>::Node Node ;//这种情况下要加上typename,因为不加编译器有可能将LinkList<T>::Node Node 试图
14 //解释为变量,你在typedef 就会有语法错误。加上以后就是让编译器认为LinkList<T>::Node Node 它是一个类型
15
16 Node* last() const //
17 {
18 return this->position(m_length-1)->next;////返回指向尾结点指针
19 }
20
21 void makeCircleList() const {//尾节点的指针指向第一个节点(即首节点的指针域)
22 if(this->m_header.next)
23 last()->next=this->m_header.next;
24 }
25
26 int mod(int i)//判断循环链表中的第i个元素在链表的位置
27 {
28 return (this->m_length==0)?0:(i %this->m_length);
29 }
30
31 public:
32 bool insert(int index, const T& elem)
33 {
34 bool ret=true;
35
36 index=index%(this->m_length+1);
37
38 ret=LinkList<T>::insert(index,elem);
39 if (ret && (index==0))
40 {
41 makeCircleList(); //首尾相连
42 }
43
44 return ret;
45 }
46
47 bool insert(const T& elem)
48 {
49 return insert(this->m_length,elem);
50
51 }
52
53 bool remove(int index)
54 {
55 bool ret = true;
56 index=mod(index);
57
58 if (index==0) //删除0位置时
59 {
60 Node* toDel=this->m_header.next;
61 if (toDel!=NULL)
62 {
63 this->m_header.next=toDel->next;
64 this->m_length--;
65
66 if(this->m_length>0)
67 {
68 makeCircleList();
69 if(this->m_current==toDel)
70 {
71 this->m_current=toDel->next;
72 }
73 }
74 else
75 {
76 this->m_header.next=NULL;
77
78 this->m_current=NULL;
79 }
80
81 this->destroyNode(toDel);
82 }
83 else
84 {
85 ret=false;
86 }
87 }
88
89 else
90 {
91 LinkList<T>::remove(index);
92 }
93
94
95
96 return ret;
97 }
98
99 bool set(int index, const T& elem)
100 {
101 return LinkList<T>::set(mod(index), elem);
102 }
103
104 T get(int index) const
105 {
106 return LinkList<T>::get(mod(index));
107 }
108
109 bool get(int index, const T& elem) const
110 {
111 return LinkList<T>::get(mod(index), elem);
112 }
113
114 int find(const T& elem) const
115 {
116 int ret = -1;
117 Node* slider = this->m_header.next;
118
119 for(int i=0; i<this->m_length; i++){
120 if(slider->value == elem){
121 ret = i;
122 break;
123 }
124
125 slider = slider->next;
126 }
127
128 return ret;
129 }
130
131 void clear()
132 {
133 if (this->m_length>1)
134 {
135 //remove(1)而不remove(0)是因为避开删除首结点
136 //而导致效率低下的问题。
137 remove(1);
138 }
139 if (this->m_length==1)
140 {
141 Node* toDel = this->m_header.next;
142 this->m_header.next = NULL;
143 this->m_length = 0;
144 this->m_current=NULL;
145 this->destroyNode(toDel);
146 }
147
148 }
149
150 bool move(int i,int step) //O(n)
151 {
152 return LinkList<T>::move(mod(i),step);
153 }
154
155 bool end() //O(1)
156 {
157 return ( (this->m_length == 0) || (this->m_current == NULL) );
158 }
159
160 ~CircleList()
161 {
162 clear();
163 }
164
165 };
166
167 }
168 #endif
约瑟夫环问题
小故事
在罗马人占领乔塔帕特后,39个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus和他的朋友并不想遵从。那么,一开始要站在什么地方才能避免被处决?
约瑟夫环
己知n个人(以编号0,1,2,3,…,n-1分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
【编程实验】约瑟夫问题
1 #include <iostream>
2 #include "CircleList.h"
3
4 using namespace std;
5 using namespace DataStructureLib;
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 cl.move(s-1,m-1); //游标指向0处,步长2
17
18 int i=0;
19
20 while( cl.getlength() > 0 )
21 {
22 cl.next();
23
24 if (i%8==0)
25 {
26 cout<<'\n';
27 }
28
29 cout <<"("<<++i<<")"<< cl.current() << " "; //当前要自杀的
30
31 int index=cl.find(cl.current());
32 cl.remove(index);
33 }
34 }
35
36 int main()
37 {
38 josephus(41,1,3); //41个人,从1号开始数,数到第三个人开始自杀
39
40 return 0;
41 }
(1)3 (2)6 (3)9 (4)12 (5)15 (6)18 (7)21 (8)24
(9)27 (10)30 (11)33 (12)36 (13)39 (14)1 (15)5 (16)10
(17)14 (18)19 (19)23 (20)28 (21)32 (22)37 (23)41 (24)7
(25)13 (26)20 (27)26 (28)34 (29)40 (30)8 (31)17 (32)29
(33)38 (34)11 (35)25 (36)2 (37)22 (38)4 (39)35 (40)16
(41)31
4. 小结
(1)循环链表是一种特殊的单链表
(2)尾结点的指针域保存了首结点的地址
(3)特殊处理首元素的插入操作和删除操作
(4)重新实现清空操作和遍历操作