第二十九课 循环链表的实现

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类

Python全栈工程师(0基础到精通)教程 第二十九课(异常(高级)运算符重载)

《链表》之带头双向循环链表

C语言实现双向非循环链表的逆序打印

C语言实现双向非循环链表的清空

Spring入门第二十九课