用循环链表实现: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;
}