孩子们的游戏(圆圈中最后剩下的数)

@TOC

描述

每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

如果没有小朋友,请返回-1

思考1

首先第一种方法就是,循环遍历整个链表,每次找到后,把该节点删除,首先我们需要构建一个链表


class ListNode:

    def __init__(self, x):

        self.val = x

        self.next = None              

然后在开始移动报数,我们移动 m-1步


# 我们找到小朋友

leftHead = None

for i in range(m-1):

    leftHead = head

    head = head.next

最后在把 m -1的那个节点移除链表


leftHead.next = head.next

head = head.next

最后的输出条件,就是链表中只剩下一个结点了


# 表示循环链表中,只剩下一个小朋友了

if head == head.next:

	return head.val

完整的代码如下


#  孩子们的游戏(圆圈中最后剩下的数)

# 每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,

# 自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,

# 让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,

# 并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,

# 可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?

# (注:小朋友的编号是从0到n-1)

# 如果没有小朋友,请返回-1

class ListNode:

    def __init__(self, x):

        self.val = x

        self.next = None



class Solution:

    def LastRemaining_Solution(self, n, m):

        if n == 0 or m == 0:

            return -1



        # 构建一个循环链表

        head = ListNode(0)

        tempHead = head

        for i in range(1, n):

            node = ListNode(i)

            head.next = node

            head = head.next



        head.next = tempHead

        head = tempHead

        while head:

            # 我们找到小朋友

            leftHead = None

            for i in range(m-1):

                leftHead = head

                head = head.next



            leftHead.next = head.next

            head = head.next



            # bakTmp = leftHead.next

            #

            # # while bakTmp.next != head:

            # #     bakTmp = bakTmp.next



            # 表示循环链表中,只剩下一个小朋友了

            if head == head.next:

                return head.val



if __name__ == '__main__':

    print(Solution().LastRemaining_Solution(5, 2))

思考2

在这里插入图片描述

在这里插入图片描述

这个题一开始小朋友们每个人自己的编号是确定的,就相当于我们列表里面的索引是确定的一样,然后让编号为0。

链表1:也就是 列表里面的第一个数开始报数,上图第一个链表蓝色的0,开始报数。报到 m-1 的数的 i小朋友 出列,圈里就少了一个数。定义为 f(n).

链表2:这个时候 从 m-1 的下一个 m 开始 下一轮的循环,开始报数,也就是上图第二个链表的蓝色框。再次报到 m-1 的时候,这个ii小朋友会站出来。(但是这个时候 我们链表的 循环顺序 (m-(m-2)) 发生了变化,不再是从第一个数 【链表的表头 开始循环,而是m 这个数作为起始位置的】,与之前第一个 链表循环的时候的 顺序(0-n)不同了【起始位置为链表的表头】。此时表里少了一个小朋友。这个是题意,让我们这样来找的小朋友。所以定义为 f `(n-1).

这样的话,就出现了 上图中的 链表2 ,链表3. 这样的不同的情况,这两个 找出来的第 m-1 个 小朋友 是同一个小朋友,但是 两个顺序却不相同。

链表3:这个图 是 以 m 为起始位置 来寻找第 m-1 个值的,它 就是 f(n-1)

如果说我们想由 链表3 得到 链表2 的话,那么 我们就需要把作为起始位置的m(下标为0) 移动到 下标为(m) 的位置,那么就是下标值 + m 。如图,我们需要移动的是 每个数值所对应的 下标 index值。让 m 在一个链表中作为起始位置来开始 报数 找 第 m - 1 个iii小朋友。

但是又由于 我们这样直接加上一个m 以后,这个 index 值有可能会大于 这个链表的长度,如果大于这个链表的长度的话,那么就是说移动到了这个链表的前一部分,所以要对我们的 这个数 对 链表的长的的一个取余:

(iii+m)%n 我们一共是 n 个值,从0-(n-1);

f(n-1) = iii

所以 f(n) = f `(n-1) = (iii+m)%n

所以 f(n) = (f(n-1)+m)

那么这个通项表达式我们就找到了,再去编写代码。


# -*- coding:utf-8 -*-

class Solution:

    def LastRemaining_Solution(self, n, m):

        # write code here

        #通过推导公式可得 f(n) = (f(n-1)+m)%n

        #首先判断,当我们这个链表里没有小朋友的时候,或者找到的小朋友报的数小于1 的时候,这个时候返回一个-1,题中表示 如果测试的是0个小朋友,数0个站出来,那么返回的值应为-1.

        if n < 1 or m < 1:

            return -1

        #只有一个人的时候,说明要找的就是这一个人。那么就返回下标0 编号。

        if n==1:

            return 0

        value = 0

        #时间复杂度 o(n)

        #从 2 开始 一直到 n 个小朋友 来循环,n 个数,所以为 n+1 

        for index in range(2,n+1):

            #现在数到的 m-1 这个值 的索引。对应上上面的公式。

            currentValue = (value+m) % index

            #把找到的这个下标值 赋值给 value

            value = currentValue

        #返回编号

        return value

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值