双向循环链表
双向循环链表类似于双链表,但是在实现上有不同:
双向循环链表没有头尾哨兵节点,因此在插入/删除链表时必须考虑特殊情况:往空表中插入第一个元素和删除表中最后一个元素,另外还需要注意的是,如果插入/删除的是表中的第一个元素,将会改变起始指针begin的位置,造成起始指针的左移或者右移,这也是一种特殊情况。
另外,由于循环链表没有尽头,所以遍历链表时必须已知链表长度,这就要求循环链表类必须要有一个成员变量currentLength记录链表长度,这一点在单链表/双链表中并不是必须的。
最后,在获取第i个节点的地址的move函数中,可以稍作改进,当i小于或等于链表长度的一半是按顺序遍历,当i大于长的的一半是逆序遍历,这样move函数的平均查找时间从n/2降低为n/4,n为链表长度。
#include <iostream> using namespace std; // 线性表:顺序表和链表共同的抽象类 template<typename elemType> class list{ public: virtual void clear() = 0; // 删除线性表中的所有数据元素 virtual int length() const = 0; // 求线性表的长度 virtual void insert(int i, const elemType & x) = 0; // 在第i个位置插入一个元素 virtual void remove(int i) = 0; // 删除第i个位置的元素 virtual int search(const elemType & x) const = 0; // 搜索某个元素x在线性表中是否出现 virtual elemType visit(int i) const = 0; // 返回线性表中第i个数据元素的值 virtual void traverse(bool reverse) const = 0; // 按序访问线性表的每一数据元素 virtual ~list(){}; }; // 循环双链表 template<typename elemType> class dcLinkList:public list<elemType>{ public: struct node{ elemType data; node *prev, *next; node(const elemType & x, node* p = NULL, node* n = NULL) {data = x; prev = p; next = n;} node():prev(NULL), next(NULL){} ~node(){} }; node* begin; int currentLength; node* move(int i) const; class NoSuchNodeError{}; public: dcLinkList():begin(NULL), currentLength(0){} ~dcLinkList(){clear();} void clear(); int length() const{return currentLength;} void insert(int i, const elemType & x); void remove(int i); int search(const elemType & x) const; elemType visit(int i) const; void traverse(bool reverse = false) const; }; template<typename elemType> void dcLinkList<elemType>::clear(){ node* p = begin; node* q; for(int i = 0; i < currentLength; ++i){ q = p->next; delete p; p = q; } } template<typename elemType> typename dcLinkList<elemType>::node* dcLinkList<elemType>::move(int i) const{ node* p = begin; if(2 * i > currentLength) for(int j = 0; j < currentLength - i; ++j) p = p->prev; else for(int j = 0; j < i; ++j) p = p->next; return p; } template<typename elemType> void dcLinkList<elemType>::remove(int i){ if(i < 0 || i >= currentLength) throw NoSuchNodeError(); if(currentLength == 1){ delete begin; begin = NULL; } else{ node* p = move(i); if(i == 0) begin = begin->next; p->next->prev = p->prev; p->prev->next = p->next; delete p; } --currentLength; } template<typename elemType> void dcLinkList<elemType>::insert(int i, const elemType & x){ if(i < 0 || i > currentLength) throw NoSuchNodeError(); if(currentLength == 0){ node* p = new node(x); p->next = p; p->prev = p; begin = p; } else{ node* pos = move(i); node* p = new node(x, pos->prev, pos); p->prev->next = p; p->next->prev = p; if(i == 0) begin = p; } ++currentLength; } template<typename elemType> void dcLinkList<elemType>::traverse(bool reverse) const{ if(reverse){ node* p = begin->prev; for(int i = 0; i < currentLength; ++i){ cout << p->data << " "; p = p->prev; } } else{ node* p = begin; for(int i = 0; i < currentLength; ++i){ cout << p->data << " "; p = p->next; } } cout << endl; } template<typename elemType> elemType dcLinkList<elemType>::visit(int i) const{ if(i < 0 || i >= currentLength) throw NoSuchNodeError(); return move(i)->data; } template<typename elemType> int dcLinkList<elemType>::search(const elemType & x) const{ node* p = begin; int i = 0; for(; i < currentLength && p->data != x; ++i) p = p->next; if(i == currentLength) return -1; else return i; }