环形链表,约瑟夫问题,(7)

来源:互联网 发布:python下载后怎么使用 编辑:程序博客网 时间:2024/06/05 00:57

一个经典的环形链表问题,有1,2,3,,,,n这些数字排成一个圆圈,从1开始每次删除第m个数字,求出这个圆圈中最后的数字。当然也可以说先从数字k开始,数到m删除这个数字,然后就从1开始,数到m删除这个数字,原理是一样。

这里是从1开始,数到m删除这个数字。

其实一个单向非循环链表也是可以模拟这个应用的,就是当指针指到最后一个节点时,让他转到头结点继续遍历。

下面的实例是环形链表,尾节点的下一个节点是头结点,已经指好了。

#include <iostream>#include "ListCommon.h"using namespace std;ListNode* lastRemainListNode(ListNode* pHead, int move){if(pHead == NULL || move <1){return NULL;}
//只有一个节点的情况 pHead->m_pNext == pHead
if(pHead->m_pNext == pHead){cout << "only one node!" <<endl;return pHead;}ListNode* pNode = pHead;ListNode* pLast = pHead;//让pLast指向pNode的前一个节点,实际也是要删除的那个节点的前一个节点while(pLast->m_pNext != pHead){pLast = pLast->m_pNext;}cout<<"pLast="<<pLast->m_nValue<<endl;int count =0;
//仅剩一个节点时循环终止 pNode == pLast
while(pNode != pLast){cout<<"count="<<count<<",move="<<move<<",pNode="<<pNode->m_nValue<<",pLast="<<pLast->m_nValue<<endl;if(++count == move){//到达计数值m,删除pNode,删除前先把指针指好,pLast始终指向pNode的前一个节点,所以pLast的next指向pNode的next就等于把pNode删除了。pLast->m_pNext = pNode->m_pNext;delete pNode;pNode = NULL;count = 0;}else{//继续遍历pLast = pLast->m_pNext;}//使pLast指向pNode的前一个节点pNode = pLast->m_pNext;}return pNode;}int main(int argc, char* argv[]){ListNode* pNode1 = CreateListNode(1);ListNode* pNode2 = CreateListNode(2);ListNode* pNode3 = CreateListNode(3);ListNode* pNode4 = CreateListNode(4);ListNode* pNode5 = CreateListNode(5);        ConnectListNodes(pNode1,pNode2);        ConnectListNodes(pNode2,pNode3);        ConnectListNodes(pNode3,pNode4);        ConnectListNodes(pNode4,pNode5);ConnectListNodes(pNode5,pNode1);//尾节点5指向头结点1PrintList(pNode1);//打印整个链表,PrintListNode(pNode5->m_pNext);//打印输出尾节点的下一个节点,表明这是一个环形链表ListNode* pNode = lastRemainListNode(pNode1,3);//处理删除PrintListNode(pNode);//最后剩余的节点数值return 0;}
测试结果:

粘贴了多个测试结果,其中move就是m的值:

PC:~/algorithm$ g++ ListCommon.cpp CircularList.cpp -o CircularList
PC:~/algorithm$ ./CircularList
print list begin ---
1
2
3
4
5
print list end
The value of node is 1
pLast=5,move=3
count=0,move=3,pNode=1,pLast=5
count=1,move=3,pNode=2,pLast=1
count=2,move=3,pNode=3,pLast=2
count=0,move=3,pNode=4,pLast=2
count=1,move=3,pNode=5,pLast=4
count=2,move=3,pNode=1,pLast=5
count=0,move=3,pNode=2,pLast=5
count=1,move=3,pNode=4,pLast=2
count=2,move=3,pNode=5,pLast=4
count=0,move=3,pNode=2,pLast=4
count=1,move=3,pNode=4,pLast=2
count=2,move=3,pNode=2,pLast=4
The value of node is 4

数到1就删除,剩下的就是5,

PC:~/algorithm$ g++ ListCommon.cpp CircularList.cpp -o CircularList
linjw@linjw-PC:~/algorithm$ ./CircularList
print list begin ---
1
2
3
4
5
print list end
The value of node is 1
pLast=5,move=1
count=0,move=1,pNode=1,pLast=5
count=0,move=1,pNode=2,pLast=5
count=0,move=1,pNode=3,pLast=5
count=0,move=1,pNode=4,pLast=5
The value of node is 5

数到2删除,剩下的就是3:

PC:~/algorithm$ g++ ListCommon.cpp CircularList.cpp -o CircularList
linjw@linjw-PC:~/algorithm$ ./CircularList
print list begin ---
1
2
3
4
5
print list end
The value of node is 1
pLast=5,move=2
count=0,move=2,pNode=1,pLast=5
count=1,move=2,pNode=2,pLast=1
count=0,move=2,pNode=3,pLast=1
count=1,move=2,pNode=4,pLast=3
count=0,move=2,pNode=5,pLast=3
count=1,move=2,pNode=1,pLast=5
count=0,move=2,pNode=3,pLast=5
count=1,move=2,pNode=5,pLast=3
The value of node is 3


这个问题还有一个递归算法,先假定有0,1,2,。。。n-1,这个n个数的环,

我们用f(n,m)表示最后剩余的那个数的编号,

第一轮循环,出列的那个编号肯定是m-1,当m<n时可以这么表示,考虑到m可能大于n,所以表示为(m-1)%n

第一轮出列的是m-1,第二轮循环开始的位置就是m

根据这个映射,m对应0m+1对应1,…,m-1对应了n-1,这样第二轮实际就是总共n-1个数,从0开始计数的环,所以第二轮剩余的那个数的编号表示为f(n-1,m)

那么f(n,m)f(n-1,m)肯定是对应的,只是第二轮的起点实际是m,而不是0

所以有f(n,m)=(f(n-1,m) +m ) % n

根据这个递推,直到f(1)= 0,然后依次回溯f(2),f(3),…,f(n)


比如n=3m=2

f(3,2) =(f(2,2)+2)%3;

f(2,2) =(f(1,2)+2)%2;

f(1,2) =0;

所以有:f(2,2)=(0+2)%2 =0;

f(3,2)=(0+2)%3=2;最后保留的就是2

代码实例:

#include <iostream>#include "ListCommon.h"using namespace std;int josephusRecursion(int length, int count){if(length <1 || count < 1){return -1;}if(length ==1){return 0;}return (josephusRecursion(length-1,count) + count)%length;}int main(int argc, char* argv[]){int remain_1 = josephusRecursion(5,3);cout << "josephusRecursion ,length =5,count=3,ramain =" <<remain_1<<endl;int remain_2 = josephusRecursion(3,2);cout << "josephusRecursion ,length =5,count=2,ramain =" <<remain_2<<endl;return 0;}
测试结果:

PC:~/algorithm$ g++ ListCommon.cpp CircularList.cpp -o CircularList
PC:~/algorithm$ ./CircularList
josephusRecursion ,length =5,count=3,ramain =3
josephusRecursion ,length =5,count=2,ramain =2


原创粉丝点击