第三十三课 双向循环链表的实现

头结点不位于链表里面,只是用于定位,和内核链表不同。

将LinuxList.h添加到我们的工程中。

再添加一个DualCircleList.h文件:

  1 #ifndef DUALCIRCLELIST_H
  2 #define DUALCIRCLELIST_H
  3
  4 #include "LinuxList.h"
  5 #include "DualLinkList.h"
  6
  7 namespace DTLib
  8 {
  9
 10 template < typename T >
 11 class DualCircleList : public DualLinkList<T>
 12 {
 13 protected:
 14     struct Node : public Object
 15     {
 16         list_head head;
 17         T value;
 18     };
 19
 20     list_head m_header;
 21     list_head* m_current;
 22
 23     list_head* position(int i) const
 24     {
 25         list_head* ret = const_cast<list_head*>(&m_header); //这里需要进行const_cast转换,否则无法在const函数里面取m_header的地址
 26
 27         for(int p=0; p<i; p++)
 28         {
 29             ret = ret->next;
 30         }
 31
 32         return ret;
 33     }
 34
 35     int mod(int i) const
 36     {
 37         return (this->m_length == 0) ? 0 : (i % this->m_length);;
 38     }
 39
 40 public:
 41     DualCircleList()
 42     {
 43         this->m_length = 0;
 44         this->m_step = 1;
 45
 46         m_current = NULL;
 47
 48         INIT_LIST_HEAD(&m_header);
 49     }
 50
 51     bool insert(const T& e)
 52     {
 53         return insert(this->m_length, e);
 54     }
 55
 56     bool insert(int i, const T& e)
 57     {
 58         bool ret = true;
 59         Node* node = new Node();
 60
 61         i = i % (this->m_length + 1);
 62
 63         if(node != NULL)
 64         {
 65             node->value = e;
 66
 67             list_add_tail(&node->head, position(i)->next);
 68
 69             this->m_length++;
 70         }
 71         else
 72         {
 73             THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create Node object ...");
 74         }
 75
 76         return ret;
 77     }
 78
 79     bool remove(int i)
 80     {
 81         bool ret = true;
 82
 83         i = mod(i);
 84
 85         ret = ((0 <= i) &&(i<this->m_length));
 86
 87         if( ret )
 88         {
 89             list_head* toDel = position(i)->next;
 90
 91             if( m_current == toDel )
 92             {
 93                 m_current = toDel->next;
 94             }
 95
 96             list_del(toDel);
 97
 98             this->m_length--;
 99
100             delete list_entry(toDel, Node, head);
101         }
102
103         return ret;
104     }
105
106     bool set(int i, const T& e)   //  O(n)
107     {
108         bool ret = true;
109         i = mod(i);
110         ret = ((0 <= i) && (i < this->m_length));
111
112         if( ret )
113         {
114             list_entry(position(i)->next, Node, head)->value = e;
115         }
116
117         return ret;
118     }
119
120     virtual T get(int i) const   // O(n)
121     {
122         T ret;
123
124         if( get(i, ret) )
125         {
126             return ret;
127         }
128         else
129         {
130             THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ...");
131         }
132
133         return ret;
134     }
135
136     bool get(int i, T& e) const    // O(n)
137     {
138         bool ret = true;
139
140         i = mod(i);
141
142         ret = ((0 <= i) && (i < this->m_length));
143
144         if( ret )
145         {
146             e = list_entry(position(i)->next, Node, head)->value;
147         }
148
149         return ret;
150     }
151
152     int find(const T& e) const
153     {
154         int ret = -1;
155
156         int i = 0;
157
158         list_head* slider = NULL;
159
160         list_for_each(slider, &m_header)
161         {
162             if( list_entry(slider, Node, head )->value == e)
163             {
164                 ret = i;
165                 break;
166             }
167
168             i++;
169         }
170
171         return ret;
172     }
173
174     int length() const
175     {
176         return this->m_length;
177     }
178
179     void clear()
180     {
181         while( this->m_length > 0 )
182         {
183             remove(0);
184         }
185     }
186
187     bool move(int i, int step = 1)
188     {
189         bool ret = (step > 0);
190
191         i = mod(i);
192
193         ret = ret && ((0 <= i) && (i < this->m_length));
194
195         if( ret )
196         {
197             m_current = position(i)->next;
198
199             this->m_step = step;
200         }
201
202         return ret;
203     }
204
205     bool end()
206     {
207         return (m_current == NULL) || (this->m_length == 0);
208     }
209
210     T current()
211     {
212         if( !end() )
213         {
214             return list_entry(m_current, Node, head)->value;
215         }
216         else
217         {
218             THROW_EXCEPTION(InvalidOperationException, "No value at current position ...");
219         }
220     }
221
222     bool next()
223     {
224         int i = 0;
225
226         while((i < this->m_step) && !end())
227         {
228             if( m_current != &m_header )
229             {
230                 m_current = m_current->next;
231                 i++;
232             }
233             else
234             {
235                 m_current = m_current->next;
236             }
237         }
238
239         if( m_current == &m_header )
240         {
241             m_current = m_current->next;
242         }
243
244         return (i == this->m_step);
245     }
246
247     bool pre()
248     {
249         int i = 0;
250
251         while((i < this->m_step) && !end())
252         {
253             if( m_current != &m_header )
254             {
255                 m_current = m_current->prev;
256                 i++;
257             }
258             else
259             {
260                 m_current = m_current->prev;
261             }
262         }
263
264         if( m_current == &m_header )
265         {
266             m_current = m_current->prev;
267         }
268
269         return (i == this->m_step);
270     }
271
272     ~DualCircleList()
273     {
274         clear();
275     }
276 };
277
278 }
279
280 #endif // DUALCIRCLELIST_H

我们的实现中,头结点只为了做定位用,不属于链表中的一个元素,因此,next和pre函数要跳过头结点。

测试程序如下:

 1 #include <iostream>
 2 #include "DualCircleList.h"
 3
 4 using namespace std;
 5 using namespace DTLib;
 6
 7
 8 int main()
 9 {
10     DualCircleList<int> dl;
11
12     for(int i = 0; i < 5; i++)
13     {
14         dl.insert(0, i);
15         dl.insert(0, 5);
16     }
17
18     cout << "begin" << endl;
19
20     dl.move(dl.length() - 1);
21
22     while(dl.find(5) != -1)   //所有的5全部被删除后停止循环
23     {
24         if( dl.current() == 5 )
25         {
26             cout << dl.current() << endl;
27
28             dl.remove(dl.find(dl.current()));
29         }
30         else
31         {
32             dl.pre();
33         }
34     }
35
36     cout << "end" << endl;
37
38     for(int i = 0; i < dl.length(); i++)
39     {
40         cout << dl.get(i) << endl;
41     }
42
43     return 0;
44 }

运行结果如下:

使用以下程序访问会得到死循环:

因为当前是一个双向循环链表。

小结:

思考题:

原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9656853.html

时间: 2024-08-15 14:30:47

第三十三课 双向循环链表的实现的相关文章

NeHe OpenGL教程 第三十三课:TGA文件

转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢. NeHe OpenGL第三十三课:TGA文件 加载压缩和未压缩的TGA文件: 在这一课里,你将学会如何加载压缩和为压缩的TGA文件,由于它使用RLE压缩,所以非常的简单,你能很快地熟悉它的. 我见过很多人在游戏开发论坛或其它地方询问关于TGA读取的问题.

第33课 双向循环链表的实现

1. DTLib中双向链表的设计思路 (1)数据结点之间在逻辑上构成双向循环,这有别于Linux内核链表的实现. (2)头结点仅用于结点的定位,而Linux内核链表是将头结点作为循环的一部分. 2. 实现思路 (1)通过模板定义DualCircleList类,继承自DualLinkList类 (2)在DualCircleList内部使用Linux内核链表进行实现(另类实现) (3)使用struct list_head定义DualCircleList的头结点 (4)特殊处理:循环遍历时忽略头结点

第三十三课:jQuery Deferred

之前我们讲了Mochikit Deferred,JSDeferred,现在讲jQuery Deferred.首先,我们先来讲下他们的区别: 在保存回调函数时,Mochikit Deferred(dojo Deferred)是用一个2维数组保存的,里面的小数组只有两项,一个是成功回调的函数,一个是失败回调的函数. JSDeferred则每个实例都必有ng,ok这两个回调函数. jQuery Deferred则一个_Deferred负责添加成功回调,一个负责添加错误回调. 它们的API区别如下图:

JAVA学习第三十三课(常用对象API)- 集合框架(一)

数字有很多用数组存,对象有很多就要用集合存 但是数组是固定长度的,集合是可变长度的 集合的由来: 对象用来封装特有数据,对象多了需要存储,如果对象个数不确定,就需要使用集合容器来存储 集合的特点: 1.用于存储对象的容器 2.长度可变 3.集合中不可存储基本数据类型 对于集合体系,的最顶层存储的是该体系中所有的共性内容,Collection,同继承一样,看顶层,用底层 java.uitil包中的Cellection 集合容器因为内部的数据不同,有多种具体容器,不断向上抽取,就形成了集合框架 整个

AGG第三十三课 line_profile_aa 参数分析说明

1 前言 agg::line_profile_aa是agg::renderer_outline_aa渲染线段的属性设置类,aa就是anti-aliased的意思,具有抗锯齿功能. 2 函数功能说明 如下简单介绍一下对于线段的属性设置,主要是调用agg::line_profile_aa对象的成员函数: agg::line_profile_aa::min_width() 字面上理解就是设置最小线宽.如果指定的线宽小于min_width函数的设置值,线段将会被化成透明色(brightness fadi

基于双向循环链表的学生管理系统

基于双向循环链表实现的学生管理系统,包括初始化,插入,删除,查抄,保存,自动按照姓名排序功能,退出并保存功能. 实现思想是将程序的各个部分划分为三个层次.主函数为界面层,即客户端层:其中后缀为Student的一般是某个功能的调度函数,属于逻辑层的内容:在调度函数之下有相应的被调度的函数,也就是相应功能的实现函数,一般后缀名为Node,意思就是这个函数直接操作链表中的结点,可以简单的划分为实现层: 这样分层实现呢有利于代码维护和个功能之间对包含或者重叠功能的直接调用,从而提高代码重用度,而降低代码

学生管理系统——基于双向循环链表

基于双向循环链表实现的学生管理系统,包括初始化,插入,删除,查抄,保存,自动按照姓名排序功能,退出并保存功能. 实现思想是将程序的各个部分划分为三个层次.主函数为界面层,即客户端层:其中后缀为Student的一般是某个功能的调度函数,属于逻辑层的内容:在调度函数之下有相应的被调度的函数,也就是相应功能的实现函数,一般后缀名为Node,意思就是这个函数直接操作链表中的结点,可以简单的划分为实现层: 这样分层实现呢有利于代码维护和个功能之间对包含或者重叠功能的直接调用,从而提高代码重用度,而降低代码

双向循环链表(C++实现,兼具Boost单元测试)

本文双链表介绍部分参考自博文数组.单链表和双链表介绍 以及 双向链表的C/C++/Java实现. 1 双链表介绍 双向链表(双链表)是链表的一种.和单链表一样,双链表也是由节点组成,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点.一般我们都构造双向循环链表. 双链表的示意图如下: 表头为空,表头的后继节点为"节点10"(数据为10的节点):"节点10"的后继节点是"

数据结构之双向链表(包含双向循环链表)

双向(循环)链表是线性表的链式存储结构的又一种形式. 在之前已经讲述了单向链表和循环链表.相比于单向链表只能从头结点出发遍历整个链表的局限性,循环链表使得可以从任意一个结点遍历整个链表. 但是,不管单向链表也好,循环链表也罢,都只能从一个方向遍历链表,即只能查找结点的下一个结点(后继结点),而不能查找结点的上一个结点(前驱结点).鉴于上述问题,引入了双向链表.由于双向循环链表包含双向链表的所有功能操作.因此,我们只讲述双向循环链表. 与单向链表不同,双向链表的结点构造如下图所示.即一个结点由三个