课程目标
- 使用 Linux 内核链表实现 DTLib 中的双向循环链表
- template <typename T> class DualCircleList;
DTLib 中双向链表的设计思路
数据结点之间在逻辑上构成双向循环链表,头结点仅用于结点定位
实现思路
- 通过模板定义 DualCircleList类,继承自 DualLinkList 类
- 在 DualCircleList 内部使用 Linux 内核链表实现
- 使用 struct list_head 定义 DualCircleList 的头结点
- 特殊处理:循环遍历时忽略头结点
实现要点
- 通过 list_head 进行目标结点定位 (posotion(i))
- 通过 list_entry 将 list_head 指针转换为目标结点指针
- 通过 list_for_each 实现 int find(const T &e) 函数
- 遍历函数中的 next() 和 pre () 需要考虑跳过头结点
编程实验:基于 Linux 内核链表的双向循环链表
DualCircleList.h
#ifndef DUALCIRCLELIST_LL_H
#define DUALCIRCLELIST_LL_H
#include "LinuxList.h"
#include "DualLinkList.h"
namespace DTLib
{
template <typename T>
class DualCircleList_LL : public DualLinkList<T>
{
public:
DualCircleList_LL()
{
this->m_length = 0;
this->m_step = 1;
m_current = nullptr;
INIT_LIST_HEAD(&m_header);
}
bool insert(const T &e)
{
return insert(this->m_length, e);
}
bool insert(int i, const T &e) override
{
bool ret = true;
Node *node = new Node();
i = i % (this->m_length + 1);
if (node != nullptr)
{
node->value = e;
list_add_tail(&node->head, position(i)->next);
++this->m_length;
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No enough memeory to create node object ...");
}
return ret;
}
bool remove(int i) override
{
bool ret = true;
i = mod(i);
ret = ((0 <= i) && (i < this->m_length));
if (ret)
{
list_head *toDel = position(i)->next;
if (toDel == m_current)
{
m_current = toDel->next;
}
list_del(toDel);
--this->m_length;
delete list_entry(toDel, Node, head);
}
return ret;
}
bool set(int i, const T &e) override
{
bool ret = true;
i = mod(i);
ret = ((0 <= i) && (i < this->m_length));
if (ret)
{
list_entry(position(i)->next, Node, head)->value = e;
}
return ret;
}
T get(int i) const
{
T ret;
if (!get(i, ret))
{
THROW_EXCEPTION(InvalidParameterExcetion, "Invalid parameter i to get element ...");
}
return ret;
}
bool get(int i, T &e) const override
{
bool ret = true;
i = mod(i);
ret = ((0 <= i) && (i < this->m_length));
if (ret)
{
e = list_entry(position(i)->next, Node, head)->value;
}
return ret;
}
int find(const T &e) const override
{
int ret = -1;
int i = 0;
list_head *slider = nullptr;
list_for_each(slider, &m_header)
{
if (list_entry(slider, Node, head)->value == e)
{
ret = i;
break;
}
++i;
}
return ret;
}
int length() const override
{
return this->m_length;
}
void clear() override // O(n)
{
while (this->m_length > 0)
{
remove(0);
}
m_current = nullptr;
}
bool move(int i, int step = 1) override // O(n)
{
bool ret = (step > 0);
i = mod(i);
ret = ret && ((0 <= i) && (i < this->m_length));
if (ret)
{
this->m_current = position(i)->next;
this->m_step = step;
}
return ret;
}
bool end() override // O(1)
{
return ((m_current == nullptr) || (this->m_length == 0));
}
T current() override // O(1)
{
if (!end())
{
return list_entry(m_current, Node, head)->value;
}
else
{
THROW_EXCEPTION(InvalidOpertionExcetion, " No value at current posotion ...");
}
}
bool next() override // O(n)
{
int i = 0;
while ((i < this->m_step) && !end())
{
if (m_current != &m_header)
{
m_current = m_current->next;
++i;
}
else
{
m_current = m_current->next;
}
}
if (m_current == &m_header)
{
m_current = m_current->next;
}
return (i == this->m_step);
}
bool pre() override
{
int i = 0;
while ((i < this->m_step) && !end())
{
if (m_current != &m_header)
{
m_current = m_current->prev;
++i;
}
else
{
m_current = m_current->prev;
}
}
if (m_current == &m_header)
{
m_current = m_current->prev;
}
return (i == this->m_step);
}
~DualCircleList_LL()
{
clear();
}
protected:
struct Node : public Object
{
list_head head;
T value;
};
list_head m_header;
list_head *m_current;
list_head *position(int i) const
{
list_head *ret = const_cast<list_head*>(&m_header);
for (int p=0; p<i; ++p)
{
ret = ret->next;
}
return ret;
}
int mod(int i) const
{
return (this->m_length == 0) ? 0 : (i % this->m_length);
}
};
}
#endif // DUALCIRCLELIST_LL_H
文件:main.cpp
#include <iostream>
#include "DualCircleList_LL.h"
using namespace std;
using namespace DTLib;
int main()
{
DualCircleList_LL<int> dl;
for (int i=0; i<5; ++i)
{
dl.insert(0, i);
dl.insert(0, 5);
}
cout << "begin" << endl;
dl.move(dl.length() -1);
while (dl.find(5) != -1)
{
if (dl.current() == 5)
{
cout << dl.current() << endl;
dl.remove(dl.find(dl.current()));
}
else
{
dl.pre();
}
}
cout << "end" << endl;
for (int i=0; i<dl.length(); ++i)
{
cout << dl.get(i) << endl;
}
return 0;
}
输出:
begin
5
5
5
5
5
end
4
3
2
1
0
小结
- Linux 内核链表是带头结点的双向循环链表
- DualCircleList 使用 Linux 内核链表进行内部实现
- DualCircleList 在循环遍历时需要跳过头结点
- 将 list_head 指针转换为目标结点指针时,使用 list_entry 宏
以上内容整理于狄泰软件学院系列课程,请大家保护原创!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。