【概述】
循环链表分为循环单链表和循环双链表,两者分别是在单链表和双链表的基础上进行的改进,其最大的特点就是在遍历时,从一个结点出发总能到达链表的任一结点
对于循环单链表来说,其结点类型与单链表类型相同,只是表尾结点 p
的指针 next
指向头结点,此外,基本操作与单链表相似
对于循环双链表来说,其结点类型与双链表类型相同,只是表尾结点 p
的后继指针 next
指向头结点,头结点的前驱指针 prior
指向表尾结点 p
,此外,基本操作与双链表相似
为便于实现,两者均采用带头结点的方式,此外,由于对链表的操作很多都在头部或尾部进行,而从头结点找到尾结点时间复杂度为 $O(n)$,为便于操作,可以令头指针 L
指向表尾元素,这样无论对头部还是尾部进行操作时,时间复杂度都为 $O(1)$
【循环单链表】
初始化表
在初始化时,令头结点 first
的后继指针 next
指向其自身
1 2 3 4 5 6 7 8 9 10 11 12 13
| template <typename T> CirSingleLinkList<T>::CirSingleLinkList() { first = (CNode<T> *)malloc(sizeof(CNode<T>)); first->next = first; } template <typename T> CirSingleLinkList<T>::CirSingleLinkList(T data[], int len) { first = (CNode<T> *)malloc(sizeof(CNode<T>)); first->next = first;
this->headInsert(data, len); }
|
销毁表
由于循环链表的尾结点的后继结点指向头结点,在逐个销毁结点空间时,会导致指向错误
因此,对于循环单链表来说,要一开始对表进行遍历,寻找到尾结点,然后将其与头结点的连接断开
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| template <typename T> CirSingleLinkList<T>::~CirSingleLinkList() { CNode<T> *p = first; while (p->next != first) { p = p->next; if (p->next = first) { p->next = NULL; break; } }
while (first != NULL) { CNode<T> *p = first; first = first->next; free(p); } }
|
基本操作
求表长
在求表长时,只要头指针 first
所指向的结点不为其自身,就继续枚举,统计表长
1 2 3 4 5 6 7 8 9 10
| template <typename T> int CirSingleLinkList<T>::getLen() { int len = 0; CNode<T> *p = first; while (p->next != first) { p = p->next; len++; } return len; }
|
判空
在判空时,只需判断头指针 first
是否指向他自己
1 2 3 4 5 6
| template <typename T> bool CirSingleLinkList<T>::empty() { if (first->next == first) return true; return false; }
|
输出
在输出时,只要头指针 first
所指向的结点不为其自身,就循环输出
1 2 3 4 5 6 7 8 9
| template <typename T> void CirSingleLinkList<T>::print() { CNode<T> *p = first->next; while (p != first) { cout << p->data << " "; p = p->next; } cout << endl; }
|
查找
按值查找
在按值查找时,只要工作指针 p
不为头结点,就循环查找
1 2 3 4 5 6 7
| template <typename T> CNode<T> *CirSingleLinkList<T>::getNodeByValue(T elem) { CNode<T> *p = first->next; while (p != first && p->data != elem) p = p->next; return p; }
|
按位查找
在按位查找时,从头结点指向的结点开始计数,同时,只要工作指针 p
不为头结点,就循环查找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| template <typename T> CNode<T> *CirSingleLinkList<T>::getNodeByLocate(int pos) { if (pos < 0) return NULL; if (pos == 0) return first;
CNode<T> *p; p = first->next; int now_pos = 1;
while (p != first && now_pos < pos) { p = p->next; now_pos++; } return p; }
|
删除
与单链表不同,循环单链表在任意一个位置上的删除是等价的,无须判断是否为表尾元素
1 2 3 4 5 6 7 8 9 10 11 12
| template <typename T> bool CirSingleLinkList<T>::deleteNodeByLocate(CNode<T> *p, T &elem) { if (p == NULL) return false; CNode<T> *s = p->next; elem = p->data; p->data = s->data; p->next = s->next; free(s); return true; }
|
【循环双链表】
初始化表
在初始化时,令头结点 first
的后继指针 next
指向其自身,同时令头结点的前驱指针 prior
也指向其自身
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| template <typename T> CirDoubleLinkList<T>::CirDoubleLinkList() { first = (CDNode<T> *)malloc(sizeof(CDNode<T>)); first->prior = first; first->next = first; } template <typename T> CirDoubleLinkList<T>::CirDoubleLinkList(T data[], int len) { first = (CDNode<T> *)malloc(sizeof(CDNode<T>)); first->prior = first; first->next = first;
this->headInsert(data, len); }
|
销毁表
由于循环链表的尾结点的后继结点指向头结点,在逐个销毁结点空间时,会导致指向错误
因此对于循环双链表来说,要在一开始将尾结点与头结点的连接断开
1 2 3 4 5 6 7 8 9
| template <typename T> CirDoubleLinkList<T>::~CirDoubleLinkList() { first->prior->next = NULL; while (first != NULL) { CDNode<T> *p = first; first = first->next; free(p); } }
|
基本操作
求表长
在求表长时,只要头指针 first
所指向的结点不为其自身,就继续枚举,统计表长
1 2 3 4 5 6 7 8 9 10
| template <typename T> int CirDoubleLinkList<T>::getLen() { int len = 0; CDNode<T> *p = first; while (p->next != first) { p = p->next; len++; } return len; }
|
判空
在判空时,只需判断头指针 first
是否指向他自己
1 2 3 4 5 6
| template <typename T> bool CirDoubleLinkList<T>::empty() { if (first->next == first) return true; return false; }
|
输出
在输出时,只要头指针 first
所指向的结点不为其自身,就循环输出
1 2 3 4 5 6 7 8 9
| template <typename T> void CirDoubleLinkList<T>::print() { CDNode<T> *p = first->next; while (p != first) { cout << p->data << " "; p = p->next; } cout << endl; }
|
查找
按值查找
在按值查找时,只要工作指针 p
不为头结点,就循环查找
1 2 3 4 5 6 7
| template <typename T> CDNode<T> *CirDoubleLinkList<T>::getNodeByValue(T elem) { CDNode<T> *p = first->next; while (p != first && p->data != elem) p = p->next; return p; }
|
按位查找
在按位查找时,从头结点指向的结点开始计数,同时,只要工作指针 p
不为头结点,就循环查找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| template <typename T> CDNode<T> *CirDoubleLinkList<T>::getNodeByLocate(int pos) { if (pos < 0) return NULL; if (pos == 0) return first;
CDNode<T> *p; p = first->next; int now_pos = 1;
while (p != first && now_pos < pos) { p = p->next; now_pos++; } return p; }
|
删除
对于循环双链表来说,在删除指定结点时,无需考虑结点位置,直接断开连接进行删除即可
1 2 3 4 5 6 7 8 9 10
| template <typename T> bool CirDoubleLinkList<T>::deleteNodeByLocate(CDNode<T> *p, T &elem) { if (p == NULL) return false; elem = p->data; p->next->prior = p->prior; p->prior->next = p->next; free(p); return true; }
|