(剑指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; } };