剑指offer面试题目:圆圈中最后剩下的数字(约瑟夫环)

题目

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


分析

  • 环形链表模拟圆圈
    创建一个n个节点的环形链表,然后每次在这个链表中删除第m个节点;
    可以用std::list来模拟环形链表,list本身不是环形结构,因此每当迭代器扫描到链表末尾的时候,需要将迭代器移到链表的头部。
  • 分析每次被删除的数字的规律,动态规划
    假设从0-n-1中删除了第m个数字,则下一轮的数字排列为m,m+1,…..n,1,2,3…m-2,将该数字排列重新映射为0~n-2,则为
  • m    0
    m+1   1  
    ….    ….
    n-1   n-1-m

    0    n-m
    1    n-m+1
    …    ….
    m-2   n-2

    可以看出从右往左的映射关系为left=(right+m)%n,即0~n-1序列中最后剩下的数字等于(0~n-2序列中最后剩下的数字+m)%n,很明显当n=1时,只有一个数,那么剩下的数字就是0.
    问题转化为动态规划问题,关系表示为:

    f(n)=(f(n-1)+m)%n; 当n=1,f(1)=0;


代码

方法一

int lastRemaining(unsigned int n,unsigned int m){
    if(n<1 || m<1)
        return  -1;
    std::list<int> numbers;
    for(unsigned int i=0;i<n;i++)
        numbers.push_back(i);
    std::list<int>::iterator current=numbers.begin();
    std::list<int>::iterator next;
    while(numbers.size()>1){
        for(unsigned int i=1;i<m;i++){
            current++;
            if(current==numbers.end())
                current=numbers.begin();
        }
        next=++current;
        if(next==numbers.end())
            next=numbers.begin();
        --current;
        numbers.erase(current);
        current=next;
    }
    return *(current);
}

方法二

int lastRemaining_1(unsigned int n,unsigned int m){
    if(n<1 || m<1)
        return  -1;
    int last=0;
    for(int i=2;i<=n;i++){
        last=(last+m)%i;
    }
    return last;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值