看了这篇博主写的关于动态链表的文章,感触颇深,今天我就以c语言来实现双向循环列表并解决约瑟夫环问题。
(4条消息) 数据结构:动态链表(C语言描述)_知道什么是码怪吗?的博客-CSDN博客
一.约瑟夫环问题
已知 n 个人(以编号 1,2,3,…,n 分别表示)围坐在一张圆桌周围,从编号为 k 的人开始顺时针报数,数到 m 的那个人出列;他的下一个人又从 1 还是顺时针开始报数,数到 m 的那个人又出列;依次重复下去,要求找到最后出列的那个人?
例如有 5 个人,要求从编号为 3 的人开始,数到 2 的那个人出列:
出列顺序依次为:
编号为 3 的人开始数 1,然后 4 数 2,所以 4 先出列;
4 出列后,从 5 开始数 1,1 数 2,所以 1 出列;
1 出列后,从 2 开始数 1,3 数 2,所以 3 出列;
3 出列后,从 5 开始数 1,2 数 2,所以 2 出列;
最后只剩下 5 自己,所以 5 出列。
二:解题思路:
1.解决这个问题,我们首先要创建一个双向循环链表,将值1,2,3,4,5分别赋值给5个结点的数据域。
struct rand{
int number;
struct rand *next; // 指向下一个节点的指针
struct rand *up; // 指向上一个节点的指针
};
int length(rand *head); // 获得链表实际长度的函数
typedef struct rand rand;
int main(){
rand *head = (rand*)malloc(sizeof(rand));
head -> number = 1; // 首先赋值头指针,头指针数据域为 1
rand *p = (rand*)malloc(sizeof(rand));
for(int i = 2; i <= 5; ++i){
p -> number = i;
if(i == 2){
// 头插法数据域为 2 的结点需要与数据域 1 的头结点连接,双向循环,建立循环关系
p -> next = head;
head -> up = p;
head -> next = p;
} else {
p -> next = head -> next;
p -> next -> up = p;
p -> up = head;
head -> next = p;
}
p = (rand*)malloc(sizeof(rand));
}
}
我们来检测以下循环链表是否创建成功。
printf("让我们检测以下双向循环链表是否成功创建\n");
rand *test = head;
int k = 5;
printf("顺时针方向:\n");
while( k-- ){
printf("%d \n",test -> number);
test = test -> up;
// 顺时针方向以当前指针指向上一个结点的方式进行循环
}
k = 5;
test = head;
printf("\n逆时针方向:\n");
while( k-- ){
printf("%d \n",test -> number);
test = test -> next;
// 顺时针方向以当前指针指向下一个结点的方式进行循环
}
好的,双向循环链表创建成功!!!
2.开始进行约瑟夫环问题啦,其实思路很简单,话不多少,直接上代码。
rand *st = (rand*)malloc(sizeof(rand));
int start = 3 - 1;
// 编号为 3 开始 ,因为头结点是 1 ,所以走到 3 的结点只需要循环两次。
st = head;
while(start --)
st = st -> up; // 获得编号为 3 的结点
while(length(st) > 1){
// 最后整个链表只剩下一个结点,该结点即最后出列的结点
st = st -> up;
// 顺时针方向获得数到 2 的结点 ,当前结点数 1 ,所以他的上一个(顺时针方向)结点就是数 2 的结点。
rand *st2 = (rand*)malloc(sizeof(rand));
st2 = st -> up; // 获得该数到 2 的节点的顺时针方向的下一个结点 ,便于后面重新赋值 st
st -> up ->next = st -> next; // 将该数到 2 的结点的临近结点更改关系
st ->next -> up = st -> up;
free(st); // 删除该数到 2 的结点
st = st2; // st 重新赋值,指向数到 2 的结点的下一个结点
}
printf("最后出列的是 %d\n",st -> number);
附上一段获取链表长度的代码。
int length(rand *head){
/* 形参就是链表的任意结点都可,不用一定为头结点
思路就是以一个变量记录形参数据域的值,遍历链表,
直到新节点的数据域与形参的数据域相同时,退出
遍历,返回长度。
*/
rand *p = (rand*)malloc(sizeof(rand));
p = head;
int pm = p -> number; // 记录形参数据域
int len = 0; // 记录长度的值
p = p -> next;
while(1){
len ++;
if(pm == p -> number)
break;
p = p -> next;
}
return len;
}
参考资料:https://blog.csdn.net/weixin_41746479/article/details/118468058