复习leetcodeLCR 187题:破冰游戏(C语言+约瑟夫环)

写在前面:

本题和汉诺塔问题都被leetcode定为简单难度,笔者认为是有失偏颇的(一杯茶,一包烟,一道题目看一天),因此初学者可以先将函数递归基础过一遍(文章链接:函数递归复习),再来看本题和汉诺塔问题的讲解。

约瑟夫环递归思想:

笔者在解答本题以前,并未学过约瑟夫环递归公式,所以笔者先是用递归的思路思考本题。

思路大致如下(为讲解方便,笔者使用了leetcode中“num=7,target=4”的这种情况):

递归思想即是大事化小,小事化了;因此,我们可以在每次有人出局以后,都重新再调用一次函数,形成递归

num 1 2 3 4 5 6 7
每个人给1个编号 0 1 2 3 4 5 6
第一轮 0 1 2 3 4 5 6
第一轮到第二轮 4 5 6 0 1 2 4
第二轮到第三轮 1 2 4 5 6 1 2
第三轮到第四轮 6 1 2 4 6 1 2
第四轮到第五轮 6 1 2 6 1 2 1

注:红底为报数又一次从头开始,因为7个编号是围成一个圈在报数(即按01234560123456……的顺序在报数),所以从头开始的第一个编号不一定为0。

递归思想:第一轮报了4个数+3编号出局 + 按“4560124……”继续报数;第二轮报了4个数+0编号出局+按“1245612……”继续报数;第二轮报了4个数+5编号出局+按“6124612……”继续报数;以此类推,第三轮报数4编号出局,第四轮报数6编号出局,最后1和2互相报了几轮数(即1 2 1 2),所以2出局,1获胜。

——————————————————分割线——————————————————————

复习leetcodeLCR 187题:破冰游戏(C语言+约瑟夫环)_第1张图片

答案代码如下:

int iceBreakingGame(int num, int target) {
    if(num==1) return 0;
    else
    return (iceBreakingGame(num-1,target)+target)%num;
}  

接下来我们就从答案向iceBreakingGame(1,4)逆向思考,笔者在这里将递归过程中部分情况罗列出来:

1.iceBreakingGame(1,4),return 0;

2.iceBreakingGame(2,4) = (iceBreakingGame(1,4)+target(4))%num(2) = 0,return 0;

3.iceBreakingGame(3,4)  = (iceBreakingGame(2,4)+4)%3 = 0,return 1;

5. .……

6.iceBreakingGame(6,4)  = (iceBreakingGame(5,4)+4)%6 = 4,return 4;

7.iceBreakingGame(7,4)  = (iceBreakingGame(6,4)+4)%7 = 1

每个人给1个编号(数组下标) 0 1 2 3 4 5 6
num 1 2 3 4 5 6 7
(7,4)--return1 1 2 3 4 5 6 7
(6,4)--return4 5 6 7 1 2 3 5
2 3 5 6 7
7 2 3 5
(3,4)--return1 7 2 3 7

递归代码解释:

根据上文约瑟夫环递归思想,不难想到(iceBreakingGame(num-1,target),即人数出局1个,其余玩家作为一个整体继续游戏

本题返回的编号为1,对应了第2个玩家最终获胜,为讲解方便,此时能想到的即是将编号看成是数组下标,即s[0] = 1,s[1] =2(s[]数组是假设出来的)

由上述的表格和递归过程不难看出,每次调用函数时的返回值最终获胜者的编号,例如iceBreakingGame(6,4)的返回值为4,  第二个玩家此时的编号即为4

那么我们在让玩家出局的同时,可以在出局玩家之前让在场玩家依次往前移4位,但请注意,此处玩家是绕成了一个圈,以下是帮助理解的大致过程:

// 1 2 3 4 5 6 7 (1 ……)围成一个圈,即7和1拉手,1和2拉手,这样形成一个闭环
// 5 6 7 1 2 3 4/⬅2移到了上述1的位置,再移到7的位置,再移到6的位置,最终移到5的位置
//但此处是从1开始报数不是从5开始报数!               

注:这里“数字/”表明这个数字对应的玩家出局,第一次胜者2的下标为1,第二次胜者下标为4

按照逆向思维,如果有10个玩家,5号玩家(下标为4)为最终胜利者,target为3,往前移3位,那么胜利者下标变成了1;因此在还有9个玩家时,胜者的下标为1。因此在递归中从(9,3)到(10,3)按照正向思维,就是往后移三位,因此出现了(iceBreakingGame(num-1,target)+target)。 

读者在此可能会不禁疑惑,本例中的(6,4)→(7,4)情况,为什么胜利者2向前移动三位而非向后移动四位递归却能实现呢?

首先要知道,在本例中2移位时出现数组越界,所以才出现了看上去只移动了三位。为了解决这个问题,我们需要对2的下标%num(当num减少时我们可以看成整个数组开辟的地址少了一个,所以取num的模)

因此可以得出(iceBreakingGame(6,4)+ target(4)) % num(7)= (4 + 4)% 7 = 1 = iceBreakingGame(7,4)。那么 (iceBreakingGame(num - 1, target) + target) % num 这个公式也就自然而然得出了。

复习leetcodeLCR 187题:破冰游戏(C语言+约瑟夫环)_第2张图片

约瑟夫环递归公式:

return num == 1 ? 0 : ((iceBreakingGame(num - 1, target) + target) % num)

(是的,本题只需要上面一句代码就能实现,真是旱的旱死,涝的涝死)

简单难度递归相关leetcode题目链接(无链表):

1.leetcode第二百三十一题:2的幂(C语言)

2.leetcode第五百零九题:斐波那契数(C语言)

你可能感兴趣的:(leetcode)