约瑟夫环方法小结
2015-08-25 22:14
387 查看
题目:0,1,……,n-1这n个数排成一个圈,从数字0开始每次从这个圈里删除第m个数字。求这个圈里剩下的最后一个数。
解一:从题意可以看出这是一个循环链表的题目,我们可以建立一个循环链表,然后从头开始遍历,遍历到第m-1个节点时,删除其后一个节点,直到只含有一个节点,输出该节点的值即可。
代码:
解法二:思想如同解一,但是可以利用STL中的list,但是注意遍历list的时候如果遍历到结尾处,则需要回到ist的头部,list.end()中没有存储数据。
代码:
解法三:剑指offer上提供的方法。定义关于n和m的方程f(n,m),表示每次在n个数字0,1,……,n-1中每次删除第m个数字最后剩下的数字。在n个数中第一次被删除的是(m-1)%n,记为k。把剩余的n-1个数字重新排列,为k+1,……n-1,0,1,……k-1。接着做一个映射
k+1->0
k+2->1
……
n-1->n--2
0->n-k-1
1->n-k;
……
k-1->n-2
把该映射定义为p,则x->p(x)可表示成p(x)=(x-k-1)%n。其逆映射p‘(x)=(x+k+1)%n。 根据映射规则,映射之前的序列中最后剩下的数字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。根据该递推公式我们可以求出f[n,m],注意当n=1时,f[n,m]=0。
代码:
解一:从题意可以看出这是一个循环链表的题目,我们可以建立一个循环链表,然后从头开始遍历,遍历到第m-1个节点时,删除其后一个节点,直到只含有一个节点,输出该节点的值即可。
代码:
typedef struct node//建立链表 { node *next; int data; node() :data(-1), next(NULL){}; }node; node* Createlist(int n)//建立循环链表 { node *ret = NULL; if (n > 1) { int i = 0; node *p = new node; p->data = i; ret = p; i++; while (i <= n-1) { node *q = new node; q->data = i; p->next = q; p = p->next; i++; } p->next = ret; } else { ret = new node; ret->data = 0; ret->next = ret; } return ret; } int LastRemaining(unsigned int n, unsigned m) { if (n < 1 || m < 1) return -1; node *head = Createlist(n); node *h = head; m %= n; while (h!=h->next) { int i = 1; for (int i = 1; i < m-1; i++) { h = h->next; } cout << h->next->data<<" "; node *temp = h->next; h->next = h->next->next; h = h->next; delete temp; } //cout << h->data<<endl; return h->data; }
解法二:思想如同解一,但是可以利用STL中的list,但是注意遍历list的时候如果遍历到结尾处,则需要回到ist的头部,list.end()中没有存储数据。
代码:
int LastRemaining(unsigned int n, unsigned m) { if (n < 1 || m < 1) return -1; list<int> numbers; for (int i = 0; i <n; i++) { numbers.push_back(i); } auto iter = numbers.begin(); while (numbers.size()>1) { for (int i = 1; i < m; i++) { iter++; if (iter==numbers.end())//如果遍历到链表末尾,则需返回到头部 { iter = numbers.begin(); } } auto next = ++iter; if (next==numbers.end()) { next = numbers.begin(); } --iter; numbers.erase(iter); iter = next; } return *iter; }
解法三:剑指offer上提供的方法。定义关于n和m的方程f(n,m),表示每次在n个数字0,1,……,n-1中每次删除第m个数字最后剩下的数字。在n个数中第一次被删除的是(m-1)%n,记为k。把剩余的n-1个数字重新排列,为k+1,……n-1,0,1,……k-1。接着做一个映射
k+1->0
k+2->1
……
n-1->n--2
0->n-k-1
1->n-k;
……
k-1->n-2
把该映射定义为p,则x->p(x)可表示成p(x)=(x-k-1)%n。其逆映射p‘(x)=(x+k+1)%n。 根据映射规则,映射之前的序列中最后剩下的数字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。根据该递推公式我们可以求出f[n,m],注意当n=1时,f[n,m]=0。
代码:
int LastRemaining(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; }
相关文章推荐
- 【leetcode】Flatten Binary Tree to Linked List,初次使用指针的指针的引用的方法
- UVA 1225 Digit Counting 【ACM/ICPC Danang 2007】
- 蚂蚁碰撞概率
- css hack技巧
- POJ2676 2918 Sudoku 暴搜
- javascript+dom
- iOS自定义警告框 OC 如何写可变参数的函数
- Perl 学习(十)
- 【python】python读写excel
- 21款网页版html5小游戏源码
- 多线程访问共享内存的不加锁实现方式
- centos7无线网络连接
- 基本排序算法--归并排序
- HDOJ 题目2763 Housewife Wind(Link Cut Tree修改边权,查询两点间距离)
- 《黑天鹅》让我恐惧的一本书
- linux下rsync配置
- Http 专题一:概述
- 新手路过
- mysql 架构分层
- 庆祝 Linux 24 岁生日!