45 - 圆圈中最后剩下的数字

题目要求:

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



本题是约瑟夫环问题。

除了以下给出的2中解法,更高级的算法:约瑟夫问题的两个O(log n)解法

1. 环形链表

由于要不断地从圈里删除一个数字,容易想到用链表实现。用链表将这 n 个数字存储,头尾相连,每隔 m 个删除一个,最后剩下的即为结果。

由于数字从 0 开始,待删除的数字: k = (m-1) % n;

每删除 1 个都要遍历 m 个节点,需要删除 n-1 个,时间复杂度O(m*n), 空间复杂度 O(n)

#include <iostream>
#include <list>
using namespace std;
// 环形链表
// 时间复杂度O(m*n),空间复杂度O(n)
int LastRemain(int n, int m) {
    if (n < 1 || m < 1)
        return -1;
    list<int> circle;
    for (int i = 0; i < n; i++)
        circle.push_back(i);
    list<int>::iterator iter = circle.begin(), delete_iter;
    while (circle.size() > 1) {
        int k = (m-1) % circle.size(); // 如 k = 1, 则删除第0个
        while (k > 0 ) {
            k--;
            iter++;
            if (iter == circle.end())
                iter = circle.begin();
        }
        delete_iter = iter;
        ++iter;
        if (iter == circle.end())
            iter = circle.begin();
        circle.erase(delete_iter);
    }
    return *iter;
}
int main() {
    int n = 4;
    int m = 3;
    cout << LastRemain(n, m) << endl;
    cout << LastRemain(2, 1) << endl;
}

2. 直接计算最后一个数字

以下分析来源:《剑指offer》p231

能否不重复遍历数字,而直接计算出最后一个数字是什么呢?

待删除的数字 k = (m-1)%n

删除后:0, 1, …, k-1, k+1, …, n-2, n-1 , 而下一次开始计数是从 k+1 开始,在剩下的序列中,

数组为:k+1, k+2, …, n-1, 0, …, k-1

剩下的序列最后剩下的数字,也应该是 n 和 m 的函数,而且最后剩下的数字,一定和原始序列最后剩下的数字是相同的。

由于剩下的序列是从 k+1 开始,设为f′(n?1,m),而原始序列设为f(n,m)

然后将剩下的 n-1 个数字,做如下映射:

    x                        p(x) 

    k+1            ->           0
    k+2            ->           1
    ...
    n-1            ->         n-k-2
    0              ->         n-k-1
    1              ->          n-k
    ...
    k-1            ->          n-2

x->p(x)的映射关系 p(x)=[x?(k+1)]%n,

p(x)->x的映射关系则为 p?1(x)=[x+(k+1)]%n,

这个映射,使得映射队列和初始队列的形式相同:

f′(n?1,m)=p?1[f(n?1,m)] 相当于从p(x)映射到x (从变形序列:0开始 映射回之前的 : k+1 开始)

f′(n?1,m)=p?1[f(n?1,m)]=[f(n?1,m)+(k+1)]%n,带入 k = (m-1)%n

得到最终递推式:f(n,m)=f′(n?1,m)=[f(n?1,m)+m]%n

当 n = 1时,即为最后一个数字。

算法时间复杂度O(n),空间O(1)

// 算法时间复杂度O(n),空间O(1)
int ComputeLastRemain(int n, int m) {
    if (n < 1 || m < 1)
        return -1;
    int last;
    for (int i = 2; i <= n; i++) {
        last = (last+m)%i;
    }
    return last;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-04 03:18:17

45 - 圆圈中最后剩下的数字的相关文章

编程算法 - 圆圈中最后剩下的数字(递推公式) 代码(C++)

圆圈中最后剩下的数字(递推公式) 代码(C++) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 0,1...,n-1这n个数字排成一个圆圈, 从数字0開始每次从这个圆圈里删除第m个数字. 求出这个圆圈里最后剩下的数字. 能够推导出约瑟夫环的递推公式, 使用循环进行求解,  时间复杂度O(n), 空间复杂度O(1). 代码: /* * main.cpp * * Created on: 2014.7.12 * Author: spike */ #incl

编程算法 - 圆圈中最后剩下的数字(循环链表) 代码(C++)

圆圈中最后剩下的数字(循环链表) 代码(C++) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 0,1...,n-1这n个数字排成一个圆圈, 从数字0开始每次从这个圆圈里删除第m个数字. 求出这个圆圈里最后剩下的数字. 使用循环链表, 依次遍历删除, 时间复杂度O(mn), 空间复杂度O(n). 代码: /* * main.cpp * * Created on: 2014.7.13 * Author: Spike */ #include <iostr

剑指Offer对答如流系列 - 圆圈中最后剩下的数字

面试题62:圆圈中最后剩下的数字 题目描述 0, 1, -, n-1这n个数字排成一个圆圈,从数字0开始每次从这个圆圈里删除第m个数字.求出这个圆圈里剩下的最后一个数字. 例如,从数字0开始每次删除第3个数字,则删除的前四个数字是2 0 4 1 因此最后剩下的数字是3 问题分析 思路一: 既然涉及到数据的频繁删除,可以考虑使用链表来存放数据,每次对长度取余数可以实现循环操作. 思路二: 这种问题规律性非常强,其实已经有对这一规律背后的数学模型的探究,即约瑟夫环 举一个具体的场景: 据说著名犹太历

LeetCode1579题——圆圈中最后剩下的数字

1.题目描述:0,1,,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字.求出这个圆圈里剩下的最后一个数字.例如,0.1.2.3.4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2.0.4.1,因此最后剩下的数字是3. 2.示例:示例 1:输入: n = 5, m = 3输出: 3 示例 2:输入: n = 10, m = 17输出: 2 限制:1 <= n <= 10^51 <= m <= 10^6 3.解题思路(1)将

LeetCode 62. 圆圈中最后剩下的数字

面试题62. 圆圈中最后剩下的数字 难度简单 0,1,,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字.求出这个圆圈里剩下的最后一个数字. 例如,0.1.2.3.4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2.0.4.1,因此最后剩下的数字是3. 示例 1: 输入: n = 5, m = 3 输出: 3 示例 2: 输入: n = 10, m = 17 输出: 2 限制: 1 <= n <= 10^5 1 <= m <

LeetCode 面试题62. 圆圈中最后剩下的数字

我的LeetCode:https://leetcode-cn.com/u/ituring/ 我的LeetCode刷题源码[GitHub]:https://github.com/izhoujie/Algorithmcii LeetCode 面试题62. 圆圈中最后剩下的数字 题目 0,1,,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字.求出这个圆圈里剩下的最后一个数字. 例如,0.1.2.3.4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字

求解圆圈中最后剩下的数字

一,问题描述 将 0,1,2....n-1 一共n个数字排成一个圆圈.从数字0开始每次从这个圆圈里面删除第m个数字( 1=<m<=n, 比如第1个数字为0).求出这个圆圈里面最后剩下的那个数字. 二,问题分析 使用java.util.LinkedList类保存这N个数字,并调用 remove(int index)方法来执行删除链表中指定位置的元素. 问题的关键是:如何找到链表中 每次待删除的元素的位置.一共有下面三种方法: 第一种方式是对链表进行遍历,第二种方式是模拟链表的遍历(但是不对链表进

剑指offer圆圈中最后剩下的数字 和 迭代器总结

迭代器只有++ ,--,==,!=四种运算方法,不能将iter = iteration+ 1,因为迭代器是指针类型,1是整数类型,不能直接相加赋值给一个指针. 题目描述 每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此.HF作为牛客的资深元老,自然也准备了一些小游戏.其中,有个游戏是这样的:首先,让小朋友们围成一个大圈.然后,他随机指定一个数m,让编号为0的小朋友开始报数.每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下

面试题62:圆圈中最后剩下的数字(C++)

题目地址:https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/ 题目描述 0,1,,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字.求出这个圆圈里剩下的最后一个数字. 例如,0.1.2.3.4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2.0.4.1,因此最后剩下的数字是3. 题目示例 示例 1: 输入: n = 5, m