NC132 环形链表的约瑟夫问题
- 题目描述
- 思路一(链表直通版)
- 思路二(数组巧解版)
- 思路三(变态秒杀版)
- Thanks♪(・ω・)ノ谢谢阅读
- 下一篇文章见!!!
题目描述
根据描述,该题思路类似于报数,第一想法就是构建环形链表。
思路一(链表直通版)
- 构建环形链表,赋予对应序号
- 进行约瑟夫问题
- 报到对应数,删除节点
- 一直到只剩一个节点。
typedef struct listnode{int val ;struct listnode* next;
} Listnode;Listnode* buynode(int n ){//开辟空间Listnode* node = (Listnode*)malloc(sizeof(Listnode));//序号赋值node->val = n;//next 指针赋值node->next = NULL;//返回节点return node;
}Listnode* createlist(int n){//序号int i = 1;//创建头尾Listnode* phead ,*ptail;//先创建第一个节点phead = ptail = buynode(i++);//循环构建链表while(i<=n){ptail->next = buynode(i++);ptail = ptail->next;}//将头尾相连,构成循环链表ptail->next = phead;//返回头节点return phead;}
int ysf(int n, int m ) {// 创建环形链表Listnode * head = createlist(n);//约瑟夫问题//计数int count = 1 ;//定义 前指针 当前指针Listnode* cur ,*prev;cur = head;prev = NULL;while(cur->next != cur){//报到指定数 删除节点if(count == m){//前节点 的next指向 当前节点的下一个节点即可删除prev->next = cur->next;cur = cur->next;//报数重置count = 1;}else{//没有报到指定数 则向后移动。prev = cur;cur = cur->next;count++;}}//返回序号return cur->val;
}
来看运行效果:
运行很顺利
思路二(数组巧解版)
链表的实现虽然简洁,但是遇到较大数据时难免会开辟较大内存空间,所以我们可以使用数组模拟循环链表的过程。
- 创建数组,并赋予对应序号值
- 开始遍历计数,报到m 将数组前移覆盖删除即可
- 一直反复进行到只剩一个元素。
#define max_num 100001
int ysf(int n, int m ) {//初始化数组int man[100001];for(int i = 0;i<n;i++){man[i] = i + 1;}//计数int count = 1;//控制下标int i =0;while(n > 1 ){//如果报到对应数if(count == m){//向前覆盖删除元素for(int j = i;j < n-1;j++){man[j] = man[j + 1];}//计数重置count = 1;//人数减 1n--;//注意不需要将 i++ 因为删除过程 i 已经指向了后一个元素。}else{//不是对应数 i++ 计数加1i++;count++;}//保证不超出 n 范围i %= n;}//返回对应序号return man[i];
}
思路三(变态秒杀版)
该思路使用数学公式,进行快速计算
F(N,M)=(F(N-1,M)+M)%N
即我们可以通过目前幸存者逆推其一开始的序号。
而根据刚才的数组思路,可以知道最后的幸存者数组下标是0,所以我们便可以开始逆推。
int ysf(int n, int m ) {//F(N,M)=(F(N-1,M)+M)%N//最后幸存者下标为 0int p = 0;//从人数为2开始逆推,直到人数为nfor (int i = 2; i <= n; i++) {//依次移动p = (p + m) % i;}//记得将序号加一return p + 1;}