使用C语言的循环链表解决约瑟夫问题

使用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);
	
}

最后在废话一句啦:其实这代码真的很臭,希望你们只是借鉴,如果能启发到你,那我真的太开心了,毕竟作为一名大一计算机小白的我也能帮助到你,太好了呢。加油加油!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小司卫理理参上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值