一,循环链表的概念
1.什么是循环链表
所谓的循环链表就是让单向链表的首尾相连,组成一个环状。
2.循环链表的典型应用
约瑟夫环问题。
3.实现循环链表的重点
1,循环链表在插入第一个元素的时候,需要我们将第一元素的指针域指向其自身,也就构成了循环链表。
2,循环链表基于单向链表而生,单是比循环链表多了游标这个概念。要想实现循环链表的插入,删除的关键是考虑头结点问题,因为在头插法方式(往链表的头部插入数据)中,需要将末尾数据元素的指针域指向新插入的节点。将新插入的节点的指针域指向头结点的指针域的指针域,还需要将头结点指向新插入的结点。(插入相同)。
二,循环链表新增概念和功能
1,什么是游标
所谓的游标就是在链表中可以移动的指针,游标初始化一般是指向链表的第一个结点。
2,游标的功能
- 初始化游标
- 移动游标:将移动前的游标所对应得结点返回,并将游标指向下一个数据元素。
- 获取游标:获取当前游标所对应得数据元素
- 删除游标:删除当前游标所对应得数据元素,并将游标指向下一个数据元素。
三,循环链表的实现
1,循环链表的功能
# ifndef CIRCLE_LINK_LIST
# define CIRCLE_LINK_LIST
/* 业务节点 */
typedef void Node;
/* 链表节点(被包含) */
typedef struct CircleNode
{
struct CircleNode * next;
}CircleNode;
/* 链表 */
typedef struct CircleLinkList
{
/* 头指针 */
Node * ptr;
/* 头结点 */
CircleNode header;
/* 游标 */
CircleNode slider;
/* 长度 */
int length;
}CircleLinkList;
/* 创建循环链表 */
CircleLinkList * createList();
/* 获取链表长度 */
int length(CircleLinkList * list);
/* 销毁链表 */
void destory(CircleLinkList * list);
/* 清空链表 */
void clear(CircleLinkList * list);
/* 判断链表是否为空 */
int empty(CircleLinkList * list);
/* 插入结点 */
void insert(CircleLinkList * list,int pos, Node * node);
/* 删除结点 */
Node * del(CircleLinkList * list, int pos);
/* 获取结点 */
Node * get(CircleLinkList * list, int pos);
/* 将游标重置指向链表的第一个元素 */
void resetSlider(CircleLinkList * list);
/* 获取当前游标指向的数据元素 */
Node * current(CircleLinkList * list);
/* 将游标指向到链表的下一个数据元素并返回当前游标的数据元素 */
Node * next(CircleLinkList * list);
/* 删除游标,并将游标指向下一个数据元素 */
Node * deleteNode(CircleLinkList * list);
# endif
2,循环链表的实现
# include<stdio.h>
# include<stdlib.h>
# include"CircleLinkList.h"
/* 创建循环链表 */
CircleLinkList * createList()
{
/* 在堆上分配内存 */
CircleLinkList * list = (CircleLinkList *)malloc(sizeof(CircleLinkList));
/* 初始化 */
list->ptr = &list->header;
list->header.next = NULL;
list->slider.next = NULL;
list->length = 0;
return list;
}
/* 获取链表长度 */
int length(CircleLinkList * list)
{
return list->length;
}
/* 销毁链表 */
void destory(CircleLinkList * list)
{
if (list != NULL)
{
free(list);
}
}
/* 清空链表 */
void clear(CircleLinkList * list)
{
if (list != NULL)
{
list->header.next = NULL;
list->slider.next = NULL;
}
}
/* 判断链表是否为空 */
int empty(CircleLinkList * list)
{
if (list->length == 0)
{
return 1;
}
else {
return 0;
}
}
/* 插入结点 */
void insert(CircleLinkList * list, int pos, Node * node)
{
if (list == NULL)
{
printf("链表为NULL\n");
return;
}
/* 判断插入位置是否超过链表长度或小于0 */
pos = pos < 0 ? 0 : pos;
pos = pos > length(list) ? length(list) : pos;
/* 注意:如果在第一个位置插入,则需要遍历到最后一个元素,然后再把最后一个元素的指针域指向第一个 */
/* 将业务节点转换为链表节点 */
CircleNode * _node = (CircleNode *)node;
/* 判断是否第一次插入 */
if (length(list) == 0)
{
list->header.next = _node;
_node->next = _node;
list->length++;
return;
}
/* 定义posNode结点 */
CircleNode * posNode = list->header.next;
/* 判断是否在头部插入 */
if (pos == 0)
{
/* 将posNode移动到尾部 */
for (int i = 0; i < length(list)-1; i++)
{
posNode = posNode->next;
}
/* 将尾部指针指向新节点 */
posNode->next = _node;
/* 将新节点指针指向头节点指向的节点 */
_node->next = list->header.next;
/* 将头节点指向新节点 */
list->header.next = _node;
}
else {
/* 将posNode移动到pos位置上 */
for (int i = 0; i < pos-1; i++)
{
posNode = posNode->next;
}
/* 插入 */
_node->next = posNode->next;
posNode->next = _node;
}
list->length++;
}
/* 删除结点 */
Node * del(CircleLinkList * list, int pos)
{
if (list == NULL)
{
printf("链表为NULL\n");
return NULL;
}
if (length(list) <= 0)
{
printf("链表已空\n");
return NULL;
}
/* 判断插入位置是否超过链表长度或小于0 */
pos = pos < 0 ? 0 : pos;
pos = pos > length(list) ? length(list) : pos;
/* 定义要删除的节点 */
CircleNode * result = NULL;
/* 定义posNode结点 */
CircleNode * posNode = list->header.next;
/* 判断是否删除头结点 */
if (pos == 0)
{
/* 移动posNode到pos位置 */
for (int i = 0; i < length(list) - 1; i++)
{
posNode = posNode->next;
}
/* 保存要删除的节点 */
result = posNode->next;
/* 删除 */
posNode->next = list->header.next->next;
list->header.next = list->header.next->next;
}
else {
/* 定义缓存上一个结点 */
CircleNode * previous = posNode;
/* 移动posNode到pos位置 */
for (int i = 0; i < pos; i++)
{
previous = posNode;
posNode = posNode->next;
}
/* 保存要删除的节点 */
result = posNode;
/* 删除 */
previous->next = posNode->next;
}
list->length--;
return result;
}
/* 获取结点 */
Node * get(CircleLinkList * list, int pos)
{
if (list == NULL)
{
printf("链表为NULL\n");
return NULL;
}
/* 判断插入位置是否超过链表长度或小于0 */
pos = pos < 0 ? 0 : pos;
pos = pos > length(list) ? pos%length(list) : pos;
/* 定义头结点 */
CircleNode * header = list->header.next;
/* 定义pos结点 */
CircleNode * posNode = header;
/* 移动posNode到指定位置 */
for (int i = 0; i < pos; i++)
{
posNode = posNode->next;
}
return posNode;
}
/* 将游标重置指向链表的第一个元素 */
void resetSlider(CircleLinkList * list)
{
list->slider.next = list->header.next;
}
/* 获取当前游标指向的数据元素 */
Node * current(CircleLinkList * list)
{
return list->slider.next;
}
/* 将游标指向到链表的下一个数据元素并返回当前游标的数据元素 */
Node * next(CircleLinkList * list)
{
CircleNode * result = list->slider.next;
list->slider.next = list->slider.next->next;
return result;
}
/* 删除游标,并将游标指向下一个数据元素 */
Node * deleteNode(CircleLinkList * list)
{
if (list == NULL)
{
printf("链表为NULL\n");
return NULL;
}
if (length(list) <= 0)
{
printf("链表已空\n");
return NULL;
}
/* 获取当前游标的数据结点 */
Node * node = current(list);
/* 将当前游标下移 */
next(list);
/* 定义要删除的节点 */
CircleNode * result = NULL;
/* 定义posNode结点 */
CircleNode * posNode = list->header.next;
/* 将业务节点转换为链表节点 */
CircleNode * _node = (CircleNode *)node;
/* 判断是否删除头结点 */
if (_node == list->header.next)
{
/* 移动posNode到pos位置 */
for (int i = 0; i < length(list) - 1; i++)
{
posNode = posNode->next;
}
/* 保存要删除的节点 */
result = posNode->next;
/* 删除 */
posNode->next = list->header.next->next;
list->header.next = list->header.next->next;
}
else {
/* 定义缓存上一个结点 */
CircleNode * previous = posNode;
/* 移动posNode到pos位置 */
while (posNode != _node)
{
previous = posNode;
posNode = posNode->next;
}
/* 保存要删除的节点 */
result = posNode;
/* 删除 */
previous->next = posNode->next;
}
list->length--;
return result;
}
3,循环链表的测试
# include<stdio.h>
# include"CircleLinkList.h"
typedef struct Student
{
CircleNode next;
char name[64];
int age;
}Student;
int main()
{
Student s1 = { NULL,"刘备",56 };
Student s2 = { NULL,"关羽",40 };
Student s3 = { NULL,"张飞",36 };
Student s4 = { NULL,"赵云",34 };
Student s5 = { NULL,"马超",20 };
Student s6 = { NULL,"黄忠",80 };
CircleLinkList * list = createList();
insert(list, length(list), &s1);
insert(list, length(list), &s2);
insert(list, length(list), &s3);
insert(list, length(list), &s4);
insert(list, length(list), &s5);
insert(list, length(list), &s6);
printf("##############遍历################\n");
for (int i = 0; i < length(list); i++)
{
Student * student = (Student *)get(list, i);
printf("name = %s,age = %d\n", student->name, student->age);
}
//printf("##############删除################\n");
//for (int i = 0; i < 6; i++)
//{
// Student * student = (Student *)del(list, length(list)-1);
// printf("name = %s,age = %d\n", student->name, student->age);
//}
resetSlider(list);
printf("##############游标遍历################\n");
for (int i = 0; i < length(list); i++)
{
Student * student = next(list);
printf("name = %s,age = %d\n", student->name, student->age);
}
}
三,约瑟夫环问题
/*
* 约瑟夫环运作如下:
* 1、一群人围在一起坐成环状(如:N)
* 2、从某个编号开始报数(如:K)
* 3、数到某个数(如:M)的时候,此人出列,下一个人重新报数
* 4、一直循环,直到所有人出列,约瑟夫环结束
*/
# include<stdio.h>
# include"CircleLinkList.h"
typedef struct value
{
CircleNode next;
int value;
}value;
int main()
{
/* 创建循环链表 */
CircleLinkList * list = createList();
/* 从0到9十个学生围成环状 */
value v0 = { NULL,0 };
value v1 = { NULL,1 };
value v2 = { NULL,2 };
value v3 = { NULL,3 };
value v4 = { NULL,4 };
value v5 = { NULL,5 };
value v6 = { NULL,6 };
value v7 = { NULL,7 };
value v8 = { NULL,8 };
value v9 = { NULL,9 };
/* 尾插法 */
insert(list, length(list), &v0);
insert(list, length(list), &v1);
insert(list, length(list), &v2);
insert(list, length(list), &v3);
insert(list, length(list), &v4);
insert(list, length(list), &v5);
insert(list, length(list), &v6);
insert(list, length(list), &v7);
insert(list, length(list), &v8);
insert(list, length(list), &v9);
/* 重置游标 */
resetSlider(list);
/* 将游标移动到2位置,从第三个人开始报数 */
for (int i = 0; i < 2; i++)
{
next(list);
}
/* 将数到4的人删除 */
while (empty(list) == 0)
{
/* 循环4次 */
for (int i = 0; i < 3; i++)
{
next(list);
}
/* 删除当前游标 */
value * t = (value *)deleteNode(list);
printf("result = %d\n", t->value);
}
return 0;
}