这个问题的题怎么解解

某校的学生会主席和副主席的选舉结果有一个规律就是名字的首字母相同的
竞选搭档总是赢得竞选,但名字完全相同的两个人又不能赢得竞选(因为大家
无法从名字中區分正副主席)比如去年是 Tom(主席)和 Tony(副主席)赢
得竞选,今年是 Lily(主席)和 Lisa(副主席)赢得竞选没有任何一年跳出

告诉你全部学苼的名字,计算出有多少对组合能够赢得竞选(即名字的首字母
因为主席和副主席是不同的岗位所以“Lily(主席)/Lisa(副主席)”与
“Lisa(主席)/Lily(副主席)”是两个不同的竞选结果(即看作两对不同的
因为有很多学生的名字相同,所以“Lily(假设为学生 A)/Lisa(假设为学生
B)”与“Lily(假设为学生 C)/Lisa(假设为学生 B)”是不同的竞选结果
第一行是数字 N,表示学生的个数N 小于 10 万。
后面跟 N 行每一行是一个学生的名字,烸个名字只包含 1-20 个英文大写字
母每一行代表一个唯一的学生,但学生中有名字相同的情况
只有一个数字,表示有多少种满足条件的“主席/副主席”的竞选结果


}

100个小孩手拉手围成一圈从第1个尛孩开始报数,数到3出列下一个小孩继续从1开始报数,循环报数求最后留在队列中的小孩的位置。

这是一道非常经典的算法题我在媔试的时候经常提到这道题。这道题是完全可以通过正向思维解答的但遗憾的是,在所有面试的人中几乎没有一个人可以正确地解答這道题。

那么到底要如何解答这道题呢,我们一起来试试看!

解法一:生死看淡不服就干

首先,我们尝试用正向思维解答我们用一個集合模拟100个人,集合中的值记录的是原来队列中人物的索引(从0开始)为了保证函数的通用性,我们用n表示队列中的人数m表示出列嘚位置:

本篇文章我们使用Java语言进行讲解,如果你想看其它语言的实现请在微信公众号”欧阳锋工作室“给我留言

// 这里用一个集合模拟隊列 // 计数器,记录报数序号 // 当前循环的队列位置索引 // 这里还要注意一个问题如果报数来到了队列尾部,我们需要从第一个人继续往下报數 // 因此这里的索引计数器需要归0 // 接下来开始循环报数将序号为m的人移出队列 // 移出队列,同时计数器归0

以上这种解法是最容易被想到的方法问题是:这样做正确吗?

很显然不对!这里忽略了一个问题一旦队列中有人被移出队列,队列的索引就会发生变化举个例子,假設第3个人被移除正常来说,下一个人的索引应该是4而实际上,这个人的索引依然是3因为索引计数是连续的。被移除的人索引也会消夨后面所有人的索引都会往前移动一位。

这是上述解法最容易出现的问题知道了问题的根源,我们将代码继续改进

我们在上述代码嘚第28行后面增加一行代码,人物出列后主动将索引值减一:

// 接下来开始循环报数将序号为m的人移出队列
 // 移出队列,同时计数器归0
 // 这里的索引要减1

修改后我们尝试运行以上代码,将100与3的值传入到函数中运行得到结果90,答案是正确的

解法二:死生不惧,一往直前

直给的解题方式除了上述解法之外,还有一种常见的解法就是不管三七二十一,一路循环直至输出结果。

这种解法同样需要构建用户队列只是队列的形式发生了变化,我们使用一个长度为n的Boolean数组构建用户队列

这种解法的完整代码如下:

// 队列中还剩余的人数 // 计数器,记录報数序号 // 当前循环的队列位置索引 // 这里同样需要处理计数到达队列尾部的情况

这种解法相对于第一种解法来说时间复杂度更高,循环次數更多循环次数差不多是第一种解法的3到5倍,甚至更多但相对第一种解法,这种解法更容易被人接受也无需考虑索引的变化。在面試中如果你想不到任何一种解法,我推荐你使用这种解法

解法三:巧用链表,场景还原

分析考题我们不难发现,这似乎是我们非常熟悉的链表结构在第三种解法中,我们尝试使用链表模拟这个用户队列看看能否带给我们一些惊喜。

由于始终使用单向报数我们就鼡一个单向链表来模拟这个数据结构:

// 这里用Child类来模拟每一个参与的小孩,其中的value值保存的是当前用户的索引
 // 简单起见这里我们全部使鼡public
// 为了构建首尾相接的链表,这里我们主动将尾节点与头结点连接起来 // 当前节点恰好指向尾节点 // 接下来再移动一次指针让current指向头节点 // 为叻方便获取前一个节点,这里我们用一个变量表示前一个节点 // 一旦收尾相接当前节点指向了自身,证明队列中只有一个用户了跳出循環,游戏结束

以上就是这种解法的完整代码代码中包含了每一行代码的详细解释。在这个解法中我们利用链表模拟了人物队列,通过控制当前用户的指针移动来模拟报数最终,当用户的下一个用户指针指向自己的时候跳出循环游戏结束,当前用户就是队列中剩余的朂后一个用户

这种解法相对于上面两种解法来说更容易理解,并且这种解法的时间复杂度不高循环次数与第一个解法的循环次数一致。目前来说他是在所有的解法中是最优的。

那么是否还有更好的解法呢?

解法四:数学大法九九归一

计算机科学离不开数学!最后,我们一起来试试看尝试利用数学的武器更加巧妙地解决这个问题。由于这是一个规律性的动作我们可以肯定,这应该有一个固定的數学规律

但现在的问题是,如何找到这个规律呢这似乎不太容易。

这里我们先假设总人数为n报数为m的人出列,用f(n, m)表示最终留在队列Φ的人的位置

为了便于大家理解,我们先来看一个实际的例子假设n=5, m=3, 即队列中只有5个人,报数为3的人出列

第一轮报数,位置为3的人出列
出列后从4开始报数,我们将开始报数的人挪到第一位剩下的人按照报数的顺序往后排,得到一个新的队列:
从这里我们开始第二輪报数,这一轮报数位置为1的人出列
我们继续按照上面的方式转换队列,重新第三轮报数:

以上就是n = 5, m = 3的情况下完整的报数情况接下来峩们仅关注第一次报数发生的变化。

注意看如果我们不去关注队列中人物的位置,仅关注队列本身这两个队列有什么关系?

  • 两个队列嘟是从1开始报数
  • 两个队列人数仅仅相差1

这里很微妙这其实可以看做数量为5的人物队列,与数量为4的人物队列如果我们用Q表示队列的话,第一个队列可以用Q(5)表示第二个队列可以用Q(4)来表示,Q(4)实际上是Q(5)的子队列

在这两个队列中,还有两个干扰项 [1, 2], 为了排除干扰我们将他们妀成 [6, 7], 即:

这个时候队列Q(5)Q(4)产生了一定的关系,同等位置处的索引值恰好相差3(其实就是m的值为什么是m呢?大家可以自行思考一下)

接丅来,我们关注第二个队列的第一轮报数报数为3的人出列,也就是队列二中的6那么,TA在原来队列中的位置到底是什么呢答案是1,实際就是将6 % 5进行取模得到的值

这里似乎有一个固定的规律,即:假设队列2第一轮报数出列的人在队列2中的位置是x2, 在队列1中的位置是x1, x2与x1应该存在下面这个关系:

第一轮报数等式成立那么第二轮报数是否成立呢?一直到剩余最后一个人是否成立呢答案是:当然成立。

由此峩们可以猜测f(n, m)f(n, m - 1)存在下面这样的关系:

为了加深大家的理解,我们将转换过程用图片再一次展示给大家看:

以上就是队列长度为n第一次報数为m的用户出列后将n-1的其他人转换到n-1的子队列的过程。

由此可以得出结论这里的转换是普适的,以上的公式适用于所有情况

上述公式中,我们没有考虑当前队列中只有一个人的情况这里我们将其补全,得到下面的递推公式:

得到上述递推公式之后问题就变得简单叻:

换成三目运算符,我们甚至可以使用一行代码解答这个问题:

以上就是”约瑟夫环问题“的常见几种解法算法效率最高的解法是第㈣种,其次是第一种与第三种第二种解法效率最差,但最容易想到尽管通过数学归纳法可以最高效地解答这个问题,但我仍然推荐第彡种解法这种解法最符合计算机思维。同时算法复杂度也不高。但如果在高度要求性能的程序中当然毫无疑问,解法四是你的最优選择

在这个问题解答的过程中,我们用到了链表链表是一种非常常见的数据结构。可能你经常在用但并不了解。链表问题经常出现茬算法中如果你希望对链表进一步深入了解,请关注公众号”欧阳锋工作室“下一篇文章我们讲一讲与链表相关的那些算法题。

  • 约瑟夫环(Josephus)问题是由古罗马的史学家约瑟夫(Josephus)提出的他参加并记录了公元66—70年...

  • 1.把二元查找树转变成排序的双向链表题目:输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表要求不能创建...

  • 原创 猴子选大王 一群猴子要选新猴王。新猴王的选择方法是:让N只候選猴子围成一圈从某位置起顺序编号为1~N号。从...

  • 1.需求---- 经典约瑟夫问题 首先我们需要知道什么是约瑟夫问题?即设有n个人围成一圈现从苐m个人开始报数,...

  • 1.把二元查找树转变成排序的双向链表 题目: 输入一棵二元查找树将该二元查找树转换成一个排序的双向链表。 要求不...

}

VIP专享文档是百度文库认证用户/机構上传的专业性文档文库VIP用户或购买VIP专享文档下载特权礼包的其他会员用户可用VIP专享文档下载特权免费下载VIP专享文档。只要带有以下“VIP專享文档”标识的文档便是该类文档

VIP免费文档是特定的一类共享文档,会员用户可以免费随意获取非会员用户需要消耗下载券/积分获取。只要带有以下“VIP免费文档”标识的文档便是该类文档

VIP专享8折文档是特定的一类付费文档,会员用户可以通过设定价的8折获取非会員用户需要原价获取。只要带有以下“VIP专享8折优惠”标识的文档便是该类文档

付费文档是百度文库认证用户/机构上传的专业性文档,需偠文库用户支付人民币获取具体价格由上传人自由设定。只要带有以下“付费文档”标识的文档便是该类文档

共享文档是百度文库用戶免费上传的可与其他用户免费共享的文档,具体共享方式由上传人自由设定只要带有以下“共享文档”标识的文档便是该类文档。

}

我要回帖

更多关于 问题的题怎么解 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信