循环链表API函数头文件
circlelist.h
#pragma once
typedef void CircleList;
typedef struct _tag_CircleListNode
{
struct _tag_CircleListNode* next;
}CircleListNode;
CircleList* CircleList_Create();
void CircleList_Destroy(CircleList* list);
void CircleList_Clear(CircleList* list);
int CircleList_Length(CircleList* list);
CircleListNode* CircleList_Get(CircleList* list, int pos);
int CircleList_Insert(CircleList* list, CircleListNode* node, int pos);
CircleListNode* CircleList_Delete(CircleList* list, int pos);
//将游标重置指向链表中的第一个数据元素
CircleListNode* CircleList_Reset(CircleList* list);
//获取当前游标指向的数据元素
CircleListNode* CircleList_Current(CircleList* list);
//将游标移动指向到链表中的下一个数据元素
CircleListNode* CircleList_Next(CircleList* list);
//直接指定删除链表中的某个数据元素
CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node);
// 根据元素的值 删除 元素 pk根据元素的位置 删除 元素
circlelist.cpp
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "circlelist.h"
typedef struct _tag_CircleList
{
int length;
CircleListNode header; //链表的头部
CircleListNode* slider; //循环链表新增功能:游标
}TCircleList; //定义一个底层的句柄
CircleList* CircleList_Create()
{
TCircleList* ret = (TCircleList*)malloc(sizeof(TCircleList));
memset(ret, 0, sizeof(TCircleList));
ret->length = 0;
ret->header.next = NULL; //链表头部指向的下一个为NULL
ret->slider = NULL;
return ret;
}
void CircleList_Destroy(CircleList* list) //释放内存
{
if (list != NULL)
{
free(list);
list = NULL;
}
}
void CircleList_Clear(CircleList* list) //初始化
{
TCircleList* sList = NULL;
if (list == NULL)
{
return;
}
sList = (TCircleList*)list;
sList->length = 0;
sList->header.next = NULL;
sList->slider = NULL;
}
int CircleList_Length(CircleList* list)
{
int ret = 0;
TCircleList* sList = NULL;
if (list == NULL)
{
ret = -1;
printf("func CircleList_Length() err: %d\n", ret);
return ret;
}
sList = (TCircleList*)list;
ret = sList->length;
return ret;
}
CircleListNode* CircleList_Get(CircleList* list, int pos)
{
int i = 0;
CircleListNode* ret = NULL;
TCircleList* sList = NULL;
CircleListNode* current;
if (list == NULL || pos < 0)
{
printf("func CircleList_Get() err: %d\n", ret);
return ret;
}
sList = (TCircleList*)list;
current = &(sList->header);
for (i = 0; i < pos && (current->next != NULL); i++)
{
current = current->next; //跳pos次,让current指向插入位置前节点
}
ret = current->next;
return ret;
}
int CircleList_Insert(CircleList* list, CircleListNode* node, int pos)
{
int ret = 0, i = 0;
TCircleList* sList = NULL;
CircleListNode* current;
CircleListNode* last = NULL; //第二个辅助指针变量
if (list == NULL || node == NULL || pos < 0)
{
ret = -2;
printf("func CircleList_Insert() err: %d\n", ret);
return ret;
}
sList = (TCircleList*)list;
current = &(sList->header);
//和CircleListNode* current=(CircleListNode*)sList一样,因为函数入口地址重叠
for (i = 0; i < pos && (current->next != NULL); i++)
{
current = current->next;
}
//1 让node连接后续链表
node->next = current->next;
//2 让前面的节点连接到新的node结点
current->next = node;
//如果插入的是第一个结点,让游标指向它
if (sList->length == 0)
{
sList->slider = node;
}
sList->length++;
//若是头插法,需求出尾部结点;判断是头插法
//if (current == (CircleListNode*)sList) //一开始是这里出现了问题,尾结点和头结点没有连起来
if (current == &(sList->header))
{
last = CircleList_Get(sList, sList->length - 1);
last->next = current->next; //3 尾结点连接头结点
}
return ret;
}
CircleListNode* CircleList_Delete(CircleList* list, int pos)
{
int i = 0;
CircleListNode* ret = NULL;
TCircleList* sList = NULL;
CircleListNode* current;
CircleListNode* last = NULL; //第二个辅助指针变量
if (list == NULL || pos < 0)
{
printf("func CircleList_Delete() err: %d\n", ret);
return ret;
}
sList = (TCircleList*)list;
current = &(sList->header);
for (i = 0; i < pos && (current->next != NULL); i++)
{
current = current->next;
}
//若删除头结点,需求出尾部结点;判断
//if (current == (CircleListNode*)sList)
if (current == &(sList->header))
{
last = CircleList_Get(list, sList->length - 1); //求的是删除前的最后结点
}
//1 缓存被删除的节点位置
ret = current->next;
//2 连接
current->next = ret->next;
sList->length--;
若删除头结点,需求出尾部结点;判断
//if (current == (CircleListNode*)sList)
//{
// last = CircleList_Get(list, sList->length - 1);
//}
//判断没有全部删除完
if (last != NULL)
{
sList->header.next = ret->next; //这个应该是和2重复的了
last->next = ret->next;
}
//若删除的是游标指向的元素,让游标下移
if (sList->slider == ret)
{
sList->slider = ret->next;
}
//全部删除完
if (sList->length == 0)
{
sList->header.next = NULL;
sList->slider = NULL;
}
return ret;
}
//将游标重置指向链表中的第一个数据元素
CircleListNode* CircleList_Reset(CircleList* list)
{
CircleListNode* ret = NULL;
TCircleList* sList = NULL;
if (list == NULL)
{
printf("func CircleList_Reset() err: %d\n", ret);
return ret;
}
sList = (TCircleList*)list;
sList->slider = sList->header.next;
ret = sList->slider;
return ret;
}
//获取当前游标指向的数据元素
CircleListNode* CircleList_Current(CircleList* list)
{
CircleListNode* ret = NULL;
TCircleList* sList = NULL;
if (list == NULL)
{
printf("func CircleList_Current() err: %d\n", ret);
return ret;
}
sList = (TCircleList*)list;
ret = sList->slider; //直接返回游标
return ret;
}
//将游标移动指向到链表中的下一个数据元素
CircleListNode* CircleList_Next(CircleList* list)
{
CircleListNode* ret = NULL;
TCircleList* sList = NULL;
if (list == NULL)
{
printf("func CircleList_Next() err: %d\n", ret);
return ret;
}
sList = (TCircleList*)list;
//ret = sList->slider; //直接返回游标
//sList->slider = ret->next;
if (sList->slider != NULL)
{
ret = sList->slider; //直接返回游标
sList->slider = ret->next;
}
return ret;
}
//直接指定删除链表中的某个数据元素
CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node)
{
int i = 0;
CircleListNode* ret = NULL;
TCircleList* sList = NULL;
CircleListNode* current;
CircleListNode* last; //第二个辅助指针变量
if (list == NULL || node == NULL)
{
printf("func CircleList_DeleteNode() err: %d\n", ret);
return ret;
}
sList = (TCircleList*)list;
current = &(sList->header);
//遍历
for (i = 0; i < sList->length; i++)
{
if (current->next == node)
{
ret = current->next;
break;
}
current = current->next;
}
if (ret != NULL)
{
CircleList_Delete(sList, i); //根据位置删除
}
return ret;
}
测试框架与约瑟夫问题(m=3)
约瑟夫问题-循环链表典型应用
n 个人围成一个圆圈,首先第 1 个人从 1 开始一个人一个人顺时针报数,报到第 m 个人,令其出列。然后再从下一 个人开始从 1 顺时针报数,报到第 m 个人,再令其出列,…,如此下去,求出列顺序。
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "circlelist.cpp"
typedef struct _Value
{
CircleListNode circlenode;
int v;
}Value;
void main()
{
int ret = 0, i = 0;
CircleList* list = NULL;
Value v1, v2, v3, v4, v5, v6, v7, v8;
v1.v = 1, v2.v = 2, v3.v = 3, v4.v = 4;
v5.v = 5, v6.v = 6, v7.v = 7, v8.v = 8;
list = CircleList_Create();//创建线性表
if (list == NULL)
{
ret = -3;
printf("func CircleList_Create() ret:%d \n", ret);
return;
}
ret = CircleList_Insert(list, (CircleListNode*)&v1, CircleList_Length(list)); //尾插法
ret = CircleList_Insert(list, (CircleListNode*)&v2, CircleList_Length(list)); //尾插法
ret = CircleList_Insert(list, (CircleListNode*)&v3, CircleList_Length(list)); //尾插法
ret = CircleList_Insert(list, (CircleListNode*)&v4, CircleList_Length(list)); //尾插法
ret = CircleList_Insert(list, (CircleListNode*)&v5, CircleList_Length(list)); //尾插法
ret = CircleList_Insert(list, (CircleListNode*)&v6, CircleList_Length(list)); //尾插法
ret = CircleList_Insert(list, (CircleListNode*)&v7, CircleList_Length(list)); //尾插法
ret = CircleList_Insert(list, (CircleListNode*)&v8, CircleList_Length(list)); //尾插法
//使用游标来遍历
for (i = 0; i < CircleList_Length(list); i++)
{
Value* pv = (Value*)CircleList_Next(list); //一开始的游标值不应该为NULL?
printf("pv->v: %d\n", pv->v);
}
CircleList_Reset(list); //重置游标
//此处用来判断出现 pv 是 nullptr 现象的原因,所以上面一开始应该遍历2遍看是不是循环链表
//Value* pv = NULL;
//for (i = 0; i < 8; i++)
//{
// CircleList_Next(list);
//}
//pv = (Value*)CircleList_Current(list);
//printf("pv->v: %d\n", pv->v); //pv 是 nullptr,说明尾端的next没有指向头部
//删除元素
printf("\n约瑟夫问题输出:\n");
while (CircleList_Length(list) > 0)
{
Value* pv = NULL;
for (i = 1; i < 3; i++) //实现约瑟夫问题 从1数到3,令3出列
{
CircleList_Next(list);
}
pv = (Value*)CircleList_Current(list);
printf("pv->v: %d\n", pv->v); //pv 是 nullptr
CircleList_DeleteNode(list, (CircleListNode*)pv);
}
CircleList_Destroy(list);
system("pause");
}
经验教训:
- 一开始测试时应该先遍历2遍证明其是否是循环链表。
- 教程里虽然说 current = &(sList->header) 和 CircleListNode* current=(CircleListNode*)sList 一样,因为函数入口地址重叠;但是程序上使用时应该是不要交叉混用,开始赋值和作判断条件使用时应该统一用 current = &(sList->header) 或另一个。
- 调试的时候真的要耐心,一步一步的来,不要着急,细心地去发现问题、理解问题并解决问题。