1、双向循环链表的实现
课程目标
- 使用Linux内核链表实现DTLib中的双向循环链表
- template < typename T > class DualCircleList;
DTLib中双向循环链表的设计思路
数据结点之间在逻辑上构成双向循环
链表,头结点仅用于结点的定位。
实现思路
-通过模板定义DualCircleList类,维承自DualLinkList类
-在DualCircleList内部使用Linux内核链表进行实现
-使用struct list_head定义DualCircleList的头结点
- 特殊处理:循环遍历时忽略头结点实现要点
-通过list_head进行目标结点定位( position(i) )
-通过list_entry将list_head指针转换为目标结点指针
-通过list_for_each实现int find(const T& e)函数
-遍历函数中的 next() 和 pre() 需要考虑跳过头结点
2、编程实验
基于Linux内核链表的双向循环链表 DualCircleList.h
#ifndef DUALCIRCLELIST_H #define DUALCIRCLELIST_H #include"LinuxList.h" #include"DualLinkList.h" namespace DTLib { template <typename T> class DualCircleList : public DualLinkList<T> { 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); //const成员函数内对成员变量取地址 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); } public: DualCircleList() { this->m_length = 0; this->m_step = 1; m_current = NULL; INIT_LIST_HEAD(&m_header); } bool insert(const T& e) { return insert(this->m_length,e); } bool insert(int i,const T& e) { bool ret = true; Node* node = new Node(); i = i % (this->m_length + 1); if(node) { node->value = e; list_add_tail(&node->head,position(i)->next); this->m_length++; } else { THROW_EXCEPTION(NoEnoughMemoryException,"No memory to create a new element ..."); } return ret; } bool remove(int i) { bool ret = true; i = mod(i); ret = (0 <= i) && (i < this->m_length); if(ret) { list_head* toDel = position(i)->next; if(m_current == toDel) { 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) { 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)) { return ret; } else { THROW_EXCEPTION(IndexOutOfBoundsExpception,"Invalid parameter i to get element ..."); } return ret; } bool get(int i,T& e) const { 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 { int ret = -1; int i =0; list_head* slider = NULL; list_for_each(slider,&m_header) { if(list_entry(slider,Node,head)->value == e) { ret = i; break; } i++; } return ret; } int length() const { return this->m_length; } void clear() { while(this->m_length > 0) { remove(0); } } bool move(int i, int step=1) { bool ret = (step > 0); i = mod(i); ret = ret && (0 <= i) && (i < this->m_length); if(ret) { m_current = position(i)->next; this->m_step = step; } return ret; } bool end() { return ( m_current == NULL)||(this->m_length == 0); } T current() { if(!end()) { return list_entry(m_current,Node,head)->value; } else { THROW_EXCEPTION(InvalidParameterException,"No value at current position ..."); } } bool next() { 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() { 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() { clear(); } }; } #endif // DUALCIRCLELIST_H
main.cpp
#include <iostream> #include"DualCircleList.h" using namespace std; using namespace DTLib; int main() { DualCircleList<int> d1; for(int i=0;i<5;i++) { d1.insert(0,i); d1.insert(0,5); } for(int i=0;i<d1.length();i++) { cout<<d1.get(i)<<" "; } cout<<endl; d1.move(d1.length()-1); while(d1.find(5) != -1) { if(d1.current() == 5) { cout<<d1.current()<<" "; d1.remove(d1.find(d1.current())); } else { d1.pre(); } } cout<<endl; for(int i=0;i<10;i++) { cout<<d1.get(i)<<" "; } cout<<endl; int i =0; for(d1.move(d1.length()-1);(i<d1.length())&&(!d1.end());d1.pre(),i++) { cout<<d1.current()<<" "; } return 0; }
3、小结
Linux内核链表是带头结点的双向循环链表
DualCircleList使用Linux内核链表进行内部实现
DualCircleList在循环遍历时需要跳过头结点
将 list_head指针转换为目标结点指针时, 使用list_entry宏4、思考题
下面代码中的pn1和pn2是否相等?为什么?