剑指 Offer 62. 圆圈中最后剩下的数字

剑指 Offer 62. 圆圈中最后剩下的数字_第1张图片

目录

​编辑

一,问题描述

二,例子

三,题目接口

 四,解题代码

1.模拟大法

2.递归法


 

一,问题描述

0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。

二,例子

剑指 Offer 62. 圆圈中最后剩下的数字_第2张图片

如以上例子,如果画成一个圆圈,那他便是这样的:

剑指 Offer 62. 圆圈中最后剩下的数字_第3张图片

现在便要以数字0的位置为1开始数,数到3。数到3的数字便是要删除的数字。

剑指 Offer 62. 圆圈中最后剩下的数字_第4张图片

第一次要删除的数字便是2。删除以后变成这样子:

剑指 Offer 62. 圆圈中最后剩下的数字_第5张图片

然后新的位置为1开始数,数到3。数到3的位置便是要删除的数字:

剑指 Offer 62. 圆圈中最后剩下的数字_第6张图片

所以这一次要删除的便是数字0。删除后的图像如下:

剑指 Offer 62. 圆圈中最后剩下的数字_第7张图片

 然后再来执行删除操作:
剑指 Offer 62. 圆圈中最后剩下的数字_第8张图片

删除后:

剑指 Offer 62. 圆圈中最后剩下的数字_第9张图片

然后继续走删除节点的步骤:

剑指 Offer 62. 圆圈中最后剩下的数字_第10张图片

在删除掉最后完数字1以后,这个循环就只剩下一个数字3了。这个3便是我们要返回的数字。

三,题目接口

class Solution {
public:
    int lastRemaining(int n, int m) {

    }
};

 四,解题代码

1.模拟大法

按照前面的分析逻辑,这道题便可以使用模拟大法。

注意:使用模拟大法时要用到数组,用到数组时便要使用到坐标。使用坐标便要注意越界问题,为了避免越界便要使用%的操作。

现在写出代码如下:

class Solution {
public:
    int lastRemaining(int n, int m) {
        vectorv(n);//定义一个数组来模拟删除操作
        v.clear();//数组开出来的前n个空格有数字00占着,所以要清理掉

        for(int i = 0;i1)//一直删除到节点只剩一个
       {
        int del = (begin+m-1)%v.size();//要被删除的数的下标
        for(int j = del;j

这段代码的逻辑是对的,但是很遗憾模拟法的时间复杂度太高了,所以这段代码是通关不了的。

2.递归法

首先我们得先来找找规律:

1.当数组种只有一个数字时,它便是唯一的幸存者。就是0。

2.当有两个数字:0,1。幸存者便是1。

3.当有三个数字:0,1,2。唯一的幸存者便是:1

4.当有四个数字:0 ,1,2,3。唯一的幸存者便是:0

5.当有五个数字:0,1,2,3,4。唯一的幸存者便是:2

……

从上面的幸存者下标中其实有一个规律,就是这一层的幸存者的下标是上已成幸存者下标加上m再和这一层的个数的余。也就是:f(n,m) = (f(n-1,m)+m)%n;

在得到这个公式以后便可以开始写递归代码:

class Solution {
public:
    int lastRemaining(int n, int m) {
        return n == 1?0:(lastRemaining(n-1,m)+m)%n;

    }
};

你可能感兴趣的:(剑指offer,算法,数据结构,c++,Cpp,学习笔记,学习)