循环链表的引入:
有 10 个老年朋友按编号顺序 1,2,。。。,10 顺时针方向围成一圈。从 1 号开 始顺时针方向 1,2,。。。,9 报数,凡报数 9 者出列(显然,第一个出圈为 编号 9 者)。 最后一个出圈者的编号是多少?第 5 个出圈者编号是多少?
为了解决以上问题特地使用循环链表。
1:循环链表和单链表不同循环链表首尾相连,而单链表有头有尾,所以循环链表只是在单链表的基础上把单链表的头和尾相连起来(前提:在了解单链表的基础上才能对循环链表进行操作)。
2:首先先定义一个结构体来表示一个链表,定义的形式与单链表相同
代码实现:
//定义一个结构体来表示循环链表
typedef struct circularLinkList {
int date;
struct circularLinkList *next;
}linkList, linkNode;
3:链表定义好以后对链表进行初始化,循环链表的初始化和单链表不同,循环链表初始化定义好头节点后要将头节点的指针域指向头节点的地址以达到循环的效果。
代码实现:
//初始化循环链表
bool initcircularLinkList(linkList*& list) {
if (!list)return false; //检测循环链表是否为空
list = new linkNode; //分配一块一个节点大小的空间
list->next = list; //将头节点的指针域指向自己
return true;
}
4:循环链表前插法实现元素的插入
//前插法实现循环链表元素的插入
bool insertFrontLinkList(linkList*& list, linkNode* l) {
if (!list || !l) return false; //检测传入的链表和要插入的节点是否为空
linkNode* p;
p = list;
if (p->next == list) { //判断链表头节点下一个节点后面是否还有节点没有则进行以下操作
p->next = l;
l->next = p;
}
else { //如果头节点后面还有下一个节点则进行以下操作
l->next = p->next;
p->next = l;
}
return true;
}
5:循环链表尾插法实现链表元素插入
//循环链表尾插法
bool insertBackLinkList(linkList*& list, linkNode* l) {
if (!list)return false;
linkNode* p;
p = list;
if (p->next == list) { //判断链表头节点下一个节点后面是否还有节点没有则进行以下操作
p->next = l;
l->next = p;
}
else {
while (p->next != list)p = p->next; //如果链表头节点后面还有节点则遍历链表找到链表则后一个指向头节点的节点
p->next = l; //让最后一个节点的指针域指向要插入的节点
l->next = list; //要插入的节点指向头节点
}
return true;
}
6:打印循环链表中的值,打印的方法和单链表相似,只是循环链表是首尾相连的而单链表不是,所以在打印的时候只需要找到循环链表的最后一个节点就可以了
代码实现:
//打印循环链表的值
bool printLinkList(linkList*& list) {
if (!list)return true;
linkList* p;
p = list->next; //指向第一个节点
while (p!= list) { //判断是否为最后一个节点
cout << p->date << " "; //打印当前节点的值
p = p->next; //指向下一个节点
}
return true;
}
7:在指定位置删除元素的值
代码实现:
//删除循环链表中的元素
bool deleteLinkList(linkList*& list, int i) {
int index = 0;
if (!list)return true;
linkList* p, * s;
p = list;
while (p->next != list && index < i - 1 ) {
index++;
p = p->next;
}
if (p->next == list||index > i || i < 0)return false;
s = p->next;
p->next = s->next;
delete s;
return true;
}
8:循环链表的基本操作已经大部分实现所以现在可以进行解决刚才的问题
问题回顾:有 10 个老年朋友按编号顺序 1,2,。。。,10 顺时针方向围成一圈。从 1 号开 始顺时针方向 1,2,。。。,9 报数,凡报数 9 者出列(显然,第一个出圈为 编号 9 者)。 最后一个出圈者的编号是多少?第 5 个出圈者编号是多少?
解决方案:先初始化一个循环链表,后用尾插法对循环链表进行元素的插入插入10个元素,元素插入完成后对链表进行遍历遍历一个节点计数加一,每遍历9个节点删除一个节点,由于在主函数中操作不方便所以自己封装一个函数
代码实现:
//从1到9开始数数到9将9删除从10开始数数到9再把该数删除
void deleteLinkList1(linkList*& list, int i) {
int index = 0, j = 0;
int number = 0, times = 0;
if (!list) {
cout << "链表为空!" << endl;
exit(1);
}
linkNode* p, * s;
p = list;
do {
index += i; //当前要遍历链表节点的次数
while (p->next) { //因为p指向头节点所以p->nex依然在头节点中,所以p依然指向要寻找节点的上一个节点
if (p->next != list)j++;
if (j >= index)break;
p = p->next;
}
times++;
s = p->next;
p->next = s->next;
number = s->date;
delete s;
printLinkList(list);
cout << endl;
if (times == 5)cout << "第五次删除的元素为:" << number << endl;
if (p->next == list)cout << "最后一次次删除的元素为:" << times << endl;
} while (list->next != list); //元素删除到只剩一个元素位置
}
while (p->next) { //因为p指向头节点所以p->nex依然在头节点中,所以p依然指向要寻找节点的上一个节点
if (p->next != list)j++;
if (j >= index)break;
p = p->next;
}
理解:因为p指向头节点所以p->nex依然在头节点中,所以p依然指向要寻找节点的上一个节点
(因为指向头节点,进入循环时j+1,所以当p指向头节点时j=1,计数的次数永远比遍历的节点多1,所以p一直指向要寻找节点的上一个节点)。
9:具体代码实现
头文件编写:
#pragma once
#include<iostream>
using namespace std;
//定义一个结构体来表示循环链表
typedef struct circularLinkList {
int date;
struct circularLinkList *next;
}linkList, linkNode;
//初始化循环链表
bool initcircularLinkList(linkList*& list) {
if (!list)return false;
list = new linkNode;
list->next = list;
return true;
}
//前插法实现循环链表元素的插入
bool insertFrontLinkList(linkList*& list, linkNode* l) {
if (!list || !l) return false;
linkNode* p;
p = list;
if (p->next == list) {
p->next = l;
l->next = p;
}
else {
l->next = p->next;
p->next = l;
}
return true;
}
//打印循环链表的值
bool printLinkList(linkList*& list) {
if (!list)return true;
linkList* p;
p = list->next;
while (p!= list) {
cout << p->date << " ";
p = p->next;
}
return true;
}
//循环链表尾插法
bool insertBackLinkList(linkList*& list, linkNode* l) {
if (!list)return false;
linkNode* p;
p = list;
if (p->next == list) {
p->next = l;
l->next = p;
}
else {
while (p->next != list)p = p->next;
p->next = l;
l->next = list;
}
return true;
}
//删除循环链表中的元素
bool deleteLinkList(linkList*& list, int i) {
int index = 0;
if (!list)return true;
linkList* p, * s;
p = list;
while (p->next != list && index < i - 1 ) {
index++;
p = p->next;
}
if (p->next == list||index > i || i < 0)return false;
s = p->next;
p->next = s->next;
delete s;
return true;
}
主函数编写:
#include<iostream>
#include<stdlib.h>
#include"circularLinkList.h"
using namespace std;
//从1到9开始数数到9将9删除从10开始数数到9再把该数删除
void deleteLinkList1(linkList*& list, int i) {
int index = 0, j = 0;
int number = 0, times = 0;
if (!list) {
cout << "链表为空!" << endl;
exit(1);
}
linkNode* p, * s;
p = list;
do {
index += i;
while (p->next) {
if (p->next != list)j++;
if (j >= index)break;
p = p->next;
}
times++;
s = p->next;
p->next = s->next;
number = s->date;
delete s;
printLinkList(list);
cout << endl;
if (times == 5)cout << "第五次删除的元素为:" << number << endl;
if (p->next == list)cout << "最后一次次删除的元素为:" << times << endl;
} while (list->next != list);
}
int main(void) {
linkList* list;
initcircularLinkList(list);
//尾插法实现链表元素插入
for (int i = 1;i <= 10;i++) {
linkNode* w;
w = new linkNode;
w->date = i;
insertBackLinkList(list, w);
}
printLinkList(list);
cout << endl;
cout << "---------------" << endl;
cout << "输入删除到几号元素:";
int i;
cin >> i;
deleteLinkList1(list, i);
system("pause");
return 0;
}