图示为双链表结构
观察循环链表的结构,不难发现单循环链表仅仅在单向链表的基础上,把最后一个节点的Next指针指向链表头节点。而双循环链表仅在双向链表的基础上,同样把最后一个节点和链表头节点建立联系。
由此:在了解单向链表和双向链表后,单向循环和双向循环就比较简单了。
循环链表结构实现:
typedef int DataType; // 数据类型
// 循环链表节点 形同 普通链表节点
// 单向循环链表节点 ---> 单链表节点
// 双向循环链表节点 ---> 双链表节点
class CNode {
public:
// 此处 this指针 赋值给 Pre 和 Next 让它们指向该节点本身
CNode(DataType data) :Data(data), Pre(this), Next(this) {}
DataType Data; // 数据域
CNode* Pre; // 前驱指针
CNode* Next; // 后继指针
};
// 循环链表
class CList {
public:
CNode* Head = NULL;
};
常规操作实现
增 :
// 头插 (会改变链表头)
void headPush(CList &CL, DataType data) {
if (CL.Head == NULL) {
CL.Head = new CNode(data);
return;
}
CNode* T = new CNode(data);
// !-- 先修改 后继 再修改 前驱 ,顺序不能乱 --!
// 修改后继指针
T->Next = CL.Head;
CL.Head->Pre->Next = T;
// 修改前驱指针
T->Pre = CL.Head->Pre;
CL.Head->Pre = T;
CL.Head = T;
}
// 尾插 (不会改变链表头) // 跟头插无明显区别 ,仅不更改 链表头
void endPush(CList &CL, DataType data) {
if (CL.Head == NULL) {
CL.Head = new CNode(data);
return;
}
CNode* T = new CNode(data);
// 先改后继,再改前驱
// 修改后继
T->Next = CL.Head;
CL.Head->Pre->Next = T;
// 修改前驱
T->Pre = CL.Head->Pre;
CL.Head->Pre = T;
}
删 :
// 头删
void headPop(CList &CL) {
if (CL.Head == NULL) {
return;
}
CNode* T = CL.Head->Next;
if (T == CL.Head) { // 仅一个节点
CL.Head = NULL;
}
else {
T->Pre = CL.Head->Pre;
CL.Head->Pre->Next = T;
CL.Head = T; // 更新链表头
}
}
// 尾删
void endPop(CList &CL) {
if (CL.Head == NULL) {
return;
}
CNode* T = CL.Head->Pre;
// 同头插方法相同,不过无需更改链表头
if (T == CL.Head) { // 仅一个节点
CL.Head = NULL;
}
else {
CL.Head->Pre = T->Pre;
T->Pre->Next = CL.Head;
// 无需更改链表头
}
}
// 删除指定节点
bool DelCNode(CList &CL, DataType data) {
if (CL.Head == NULL) {
return false;
}
CNode* T = CL.Head;
int flat = false; // 是否存在data节点的标志
do {
if (T->Data != data) {
T = T->Next;
}
else {
flat = true; // 更改标志为真
break;
}
} while (T != CL.Head);
// 没找到data节点
if (flat != true) {
return false;
}
CNode* N = T->Next;
if (N == T) { // 仅剩一个节点
CL.Head = NULL;
}
else {
N->Pre = T->Pre;
T->Pre->Next = N;
if (T == CL.Head) { // 如果待删节点在链表头,更改 N 为新链表头
CL.Head = N;
}
}
return true;
}
查 :
// 查找某节点是否存在
bool FindCNode(CList &CL, DataType data) {
if (CL.Head == NULL) {
return false;
}
CNode* T = CL.Head;
do {
if (T->Data == data) {
return true;
}
T = T->Next;
} while (T != CL.Head);
return false;
}
改 :
// 改 (data1-->data2)
bool ChangeNode(CList &CL, DataType data1, DataType data2) {
if (CL.Head == NULL) {
return false;
}
CNode* T = CL.Head;
do {
if (T->Data == data1) {
T->Data = data2;
return true;
}
T = T->Next;
} while (T != CL.Head);
return false;
}
遍历:
// 顺序遍历
void Traverse(CList &CL) {
if (CL.Head == NULL) {
return;
}
CNode* T = CL.Head;
// 巧用 do-while 循环 再一次回到链表头停止
do {
cout << T->Data << " ";
T = T->Next;
} while (T!=CL.Head);
cout << endl;
}
// 逆序遍历
void ReTravese(CList &CL) {
if (CL.Head == NULL) {
return;
}
CNode* T = CL.Head->Pre;
do {
cout << T->Data << " ";
T = T->Pre;
} while (T != CL.Head->Pre);
cout << endl;
}
获取链表长度:
小结:此处仅实现双向循环链表,而单向循环链表比双向循环链表更容易,不再赘述,实现基本思想大同小异。