【码图】534_约瑟夫生死游戏_循环链表实现

用循环链表实现:N个乘客同乘一艘船,因为严重超载,加上风高浪大,危险万分,因此船长告诉乘客,只有将部分乘客投入海中,其余人才能幸免于难。
无奈,大家只得同意这种办法。于是N个人围成一圈(从1,2,3...N分别编号)。由编号为1的人开始,依次报数,数到第M人,便把他投入大海中,
然后再从他的下一个人数起,数到第M人,再将他扔到大海中,如此循环地进行,直到剩下K个乘客为止。按顺序依次输出被扔下大海的乘客的编号。

提交格式:
实现int * solve(int N,int M,int K)函数。
函数参数为乘客人数N、间隔人数M和剩余乘客人数K,1<=N<=1000,1<=M<=500000,0<=K<N。
函数返回值为按顺序被扔下大海乘客编号的数组。
请不要printf输出任何内容。

输入样例1:
9 3 2 
输出样例1:
3 6 9 4 8 5 2

输入样例2:
12 5 6
输出样例2:
5 10 3 9 4 12

这道题考察的知识点是循环链表的构建,而在编写代码的过程中,我们很容易发现,对“步长”的选择也是一大难点。

考虑扔人下海,不难知道是删除链表的一个节点。删除节点,需要两个指针,一个指向要被删除的节点(current),另一个指向要被删除的节点的前一个节点(pre),于是乎我们考虑“数到第M个人”的问题,很容易想到的实现方式,是把pre和current都往前移若干个单位距离,那么到底是几个呢?

“数到第M个人,便把他投入大海中”, 那么如果是第一个人呢?数到第“1”个人,把他扔到海中,我们不需要移动指针。所以需要移动指针次数是M-1次。

再考虑如何使得每次移动过后,都“轮换对称”的相似于指针移动前的状态。要移动M-1次,使得current移动到要被删除的地方,那么第一次的时候,pre应该在队列的末尾。而移动、删除过后,current指向被删除的节点的后一位,恰好相当于第一次指向第一个节点。(类比整除不难得到)

于是很自然的,我们得到如下代码:

#include<stdio.h>
#include<malloc.h>

typedef struct Node {
	int Num;
	Node* next;
};

int* solve(int N, int M, int K)
{
	int people[1000] = {};
	Node* current = NULL;
	Node* first = (Node*)malloc(sizeof(Node));
	first->Num = 1;
	first->next = NULL;
	Node* pre = first;
	for (int i = 1; i < N; i++)
	{
		current = (Node*)malloc(sizeof(Node));
		current->Num = i + 1;
		pre->next = current;
		pre = current;
		current->next = NULL;
	}
	current->next = first;//形成闭环
	int count = 0;
	int live = N;
	pre = current;
	current = first;
	int j = 0;
	while (1)
	{
		if (N == K) break;
		for (int i = 0; i < M-1; i++)
		{
			pre = pre->next;
			current = current->next;
		}
		people[j++] = current->Num;
		current = current->next;
		free(pre->next);
		pre->next = current;
		count++;
		N--;
	}
	return people;
}

  • 11
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值