• (剑指Offer)面试题45:圆圈中最后剩下的数字


    题目:

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

    思路:

    1、环形链表模拟圆圈

    创建一个n个节点的环形链表,然后每次在这个链表中删除第m个节点;

    可以用std::list来模拟环形链表,list本身不是环形结构,因此每当迭代器扫描到链表末尾的时候,需要将迭代器移到链表的头部。

    2、分析每次被删除的数字的规律,动态规划

    假设从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;

    代码:

    1、环形链表

    #include <iostream>
    #include <list>
    using namespace std;
    
    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 main()
    {
        cout << lastRemaining(5,3) << endl;
        return 0;
    }
    

    2、映射规律,动态规划

    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;
    }
    
    
    int lastRemaining_2(unsigned int n,unsigned int m){
        if(n<1 || m<1)
            return  -1;
        if(n==1)
            return 0;
        return (lastRemaining_2(n-1,m)+m)%n;
    }

    在线测试OJ:

    http://www.nowcoder.com/books/coding-interviews/f78a359491e64a50bce2d89cff857eb6?rp=2

    AC代码:

    1、环形链表

    class Solution {
    public:
        int LastRemaining_Solution(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;
        }
    };

    2、序列规律,动态规划

    class Solution {
    public:
        int LastRemaining_Solution(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;
        }
    };

    class Solution {
    public:
        int LastRemaining_Solution(unsigned int n, unsigned int m)
        {
            if(n<1 || m<1)
                return -1;
            if(n==1)
                return 0;
            
            return (LastRemaining_Solution(n-1,m)+m)%n;
        }
    };
  • 相关阅读:
    html——黑体、斜体、下划线及删除线
    <转>DataGridView分页控件
    (转)Log4J 最佳实践之全能配置文件
    C# 单例代码
    MySql基本语句
    .NET中windows服务如何获取自己的安装路径
    web开发网址收藏...
    将字符串转换为json对象_正确语法
    (转)理解矩阵一、二、三
    将数据写入EXCEL多个表
  • 原文地址:https://www.cnblogs.com/AndyJee/p/4687715.html
Copyright © 2020-2023  润新知