使用C语言的循环链表解决约瑟夫问题
** 第一次做CSDN的博客,所以很简陋,但很朴实,嘿嘿。代码其实很low,该优化的地方很多,但是我觉得特别适合初学者学习,因为这正是初学者的我一步步笨手笨脚写出来的。**
下面让我细细的分析这段长长的代码,希望有人会耐心看完。
1.首先我们用Cycle_list initList_Sq()函数建立一个最简单的循环链表。(Cycle_list 这个是我命名的结构体指针,应该很好懂的啦。)这里我巧妙的把头结点 head->date=1; 为什么呢?我后面解答。
Cycle_list initList_Sq()
{
Cycle_list head;
head = (Cycle_list)malloc(sizeof(LNode));
head->next = head;
head->date = 1;
return head;
}
2.然后使用Bool inputList(Cycle_list head, ElemType mydate)函数实现数据的存入。首先我们要用while (P->next != head)来判断结点是否到了“终点”,如果结点的下一个是头结点了,那么我们就在这个结点的下一个位置加上我们新建立的NewNode。然后让我们拼接起来,让我们原先的“终点”结点P接到我们建立的NewNode上,然后NewNode接到我们的头结点位置上,这样一个循环链表就建立成功了(还是带数据的哦)。
Bool inputList(Cycle_list head, ElemType mydate)
{
Cycle_list P, NewNode;
P = head;
NewNode = (Cycle_list)malloc(sizeof(LNode));
while (P->next != head)
{
P = P->next;
}
NewNode->date = mydate;
P->next = NewNode;
NewNode->next = head;
return OK;
}
3.接下来按顺序介绍这个函数void outputReply(Cycle_list head, ElemType Distance, Cycle_list Rear_Pointer);
写到这里,如果你和我一样是自己写过这个问题的代码时候,想必你也和我一样有一个这样的问题:“如果我选择的间距是1呢?也就是说每一个节点都要打印”,一般情况,如果我们随便选择距离为3的时候,你可能会觉得,我们只要使用一个循环语句,然后间隔3个就跳过一个结点(或删除这个结点),是的,我当时自己写的时候也是这样考虑的,但是我发现,如果当我们的距离设置为1的时候,那问题就来了!所以我的解决方法是记住上一个节点的指针!让我们来看第四步!
void outputReply(Cycle_list head, ElemType Distance,Cycle_list Rear_Pointer)
{
Cycle_list P, R;
int i;
P = head;
while (P->next != P)
{
for (i = 1;i < Distance;i++)
{
P = P->next;
Rear_Pointer = Rear_Pointer->next;
}
printf("The rid of %d\n", P->date);
Rear_Pointer->next = P->next;
P = P->next;
}
printf("The left element is %d\n", P->date);
}
4.保留“终点”结点的地址!这样一来,看似的问题就迎刃而解了,我们想跳过(删除)哪个结点都可以很方便的做到,
Cycle_list Find_RearPointer(Cycle_list head)
{
Cycle_list P;
P = head;
while (P->next != head)
{
P = P->next;
}
return P;
}
5…最后我们来说说为什么要把头结点的date初始化为1;其实这也是我代码的缺陷,只能解决按顺序存储的循环链表。看主函数:我使用了for语句:
for (i = 2;i <= length;i++)
{
inputList(la, i);
}
因为头结点也是结点嘛,所以他也可以自己存储数据,如果你还没理解,我们就在回头看看inputList函数,当我们第一次调用inputList函数的时候,此时我们的链表就一个结点,而且该节点date未初始化,但是我们却还是创建了一个新的NewNode结点来存储一个新的数据,这就导致了头结点没有发挥他自己的作用,但是在我写这个博客的时候,我发现其实是我自己局限了,这个地方的代码可以很好的优化,也就是说并不必要自己去初始化头结点的date,相反,该头结点和往常单链表一样发挥引入下面结点的作用就可以了。(就当我废话了。。。。)
下面是整体代码:
#include<stdlib.h>
#include <stdio.h>
typedef int Bool;
typedef int ElemType;
#define OK 1;
typedef struct LNode {
int date;
struct LNode* next;
}LNode, * Cycle_list;
Cycle_list initList_Sq();
Bool inputList(Cycle_list head, ElemType mydate);
void outputReply(Cycle_list head, ElemType Distance, Cycle_list Rear_Pointer);
Cycle_list Find_RearPointer(Cycle_list head);
int main()
{
int length, i, Distance;
Cycle_list la, Rear_Pointer;
la = initList_Sq();
printf("请输入链表长度:");
scanf("%d", &length);
for (i = 2;i <= length;i++)
{
inputList(la, i);
}
Rear_Pointer=Find_RearPointer(la);
printf("请输入间隔距离:");
scanf("%d", &Distance);
outputReply(la, Distance,Rear_Pointer);
return OK;
}
Cycle_list initList_Sq()
{
Cycle_list head;
head = (Cycle_list)malloc(sizeof(LNode));
head->next = head;
head->date = 1;
return head;
}
Bool inputList(Cycle_list head, ElemType mydate)
{
Cycle_list P, NewNode;
P = head;
NewNode = (Cycle_list)malloc(sizeof(LNode));
while (P->next != head)
{
P = P->next;
}
NewNode->date = mydate;
P->next = NewNode;
NewNode->next = head;
return OK;
}
Cycle_list Find_RearPointer(Cycle_list head)
{
Cycle_list P;
P = head;
while (P->next != head)
{
P = P->next;
}
return P;
}
void outputReply(Cycle_list head, ElemType Distance,Cycle_list Rear_Pointer)
{
Cycle_list P, R;
int i;
P = head;
while (P->next != P)
{
for (i = 1;i < Distance;i++)
{
P = P->next;
Rear_Pointer = Rear_Pointer->next;
}
printf("The rid of %d\n", P->date);
Rear_Pointer->next = P->next;
P = P->next;
}
printf("The left element is %d\n", P->date);
}
最后在废话一句啦:其实这代码真的很臭,希望你们只是借鉴,如果能启发到你,那我真的太开心了,毕竟作为一名大一计算机小白的我也能帮助到你,太好了呢。加油加油!