// 约瑟夫环问题李春葆解法.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include<iostream>
using namespace std;
struct child{
int no;
child* next;
child(int d):no(d),next(NULL){}
};
//小孩节点循环单链表不带头结点,first指向开始报数的小孩节点,初始时指向首节点
class joseph{
int n,m;
child *first;
public:
joseph(int n1,int m1):n(n1),m(m1){}//构造函数
void create()
{
first=new child(1);
child* r=first;
child *p;
for(int i=2;i<=n;i++)
{
p=new child(i);
r->next=p;//将p节点连接到末尾
r=p;//更新尾指针
}
r->next=first;//构成一个首节点为first的循环单链表(重点--------)
}
void jos01()
{
child *p,*q;
//没有利用单链表的循环条件,这里用的计数(因为共n个小孩,数量固定,但是对于链表通常用指针可以参考王红梅老师的做法)
for(int i=1;i<=n;i++)
{
p=first;
int j=1;
while(j!=m)//从first节点开始报数,报到第m个退出循环
{
j++;//报数递增
p=p->next;//移到下一个节点
}
cout<<p->no<<" ";//j==m,该小孩出列
q=p->next;//q节点存储p的后继节点
p->no=q->no;//将节点q的值复制到节点p,所以删除q就行了
p->next=q->next;//删除q节点(重点=======)
delete q;
first=p;//从节点p重新开始
}
cout<<endl;
}
void jos02()
{
child *p,*pre;
//没有利用单链表的循环条件,这里用的计数(因为共n个小孩,数量固定,但是对于链表通常用指针可以参考王红梅老师的做法)
for(int i=1;i<=n;i++)
{
p=first;
int j=1;
while(j!=m)//从first节点开始报数,报到第m个退出循环
{
j++;//报数递增
pre=p;//
p=p->next;//移到下一个节点
}
cout<<p->no<<" ";//j==m,该小孩出列
pre->next=p->next;
//删除p节点(重点=======)
delete p;
first=pre->next;//重新开始
}
cout<<endl;
}
};
int main(int argc, char* argv[])
{
int n=6,m=3;
joseph j(n,m);
printf("出圈顺序如下:\n");
j.create();
//j.jos01();
j.jos02();
return 0;
}
对于jos01():
void jos01()
{
child *p,*q;
//没有利用单链表的循环条件,这里用的计数(因为共n个小孩,数量固定,但是对于链表通常用指针可以参考王红梅老师的做法)
for(int i=1;i<=n;i++)
{
p=first;
int j=1;
while(j!=m)//从first节点开始报数,报到第m个退出循环
{
j++;//报数递增
p=p->next;//移到下一个节点
}
cout<<p->no<<" ";//j==m,该小孩出列
q=p->next;//q节点存储p的后继节点
p->no=q->no;//将节点q的值复制到节点p,所以删除q就行了
p->next=q->next;//删除q节点(重点=======)
delete q;
first=p;//从节点p重新开始
}
cout<<endl;
}
特别注意:
cout<<p->no<<" ";//j==m,该小孩出列
q=p->next;//q节点存储p的后继节点
p->no=q->no;//将节点q的值复制到节点p,所以删除q就行了
p->next=q->next;//删除q节点(重点=======)
delete q;
first=p;//从节点p重新开始
找到第m个节点,指针p指向第m个节点,q为p的后继,将
q=p->next;//q节点存储p的后继节点
p->no=q->no;//将节点q的值复制到节点p,所以删除q就行了
p->next=q->next;//删除q节点(重点=======)精妙之二
delete q;
(p为第m个节点,就会删除p节点,为了下面p好找,将 p->no=q->no;//将节点q的值复制到节点p,所以删除q就行了)
括号里的解释都为了:
first=p;//从节点p重新开始
对于jos2:
void jos02()
{
child *p,*pre;
//没有利用单链表的循环条件,这里用的计数(因为共n个小孩,数量固定,但是对于链表通常用指针可以参考王红梅老师的做法)
for(int i=1;i<=n;i++)
{
p=first;
int j=1;
while(j!=m)//从first节点开始报数,报到第m个退出循环
{
j++;//报数递增
pre=p;//
p=p->next;//移到下一个节点
}
cout<<p->no<<" ";//j==m,该小孩出列
pre->next=p->next;
//删除p节点(重点=======)
delete p;
first=pre->next;//重新开始
}
cout<<endl;
}
沿用一般做法,设置pre为跟踪指针记录p的前驱节点,删除p节点
特别小心:
first=pre->next;//重新开始
这里可以参考:王红梅老师的解法
下面附图(李春葆老师解法的精妙之一first)
我在这张图上红色的表示,可以直到李春葆老师用的是jos01()的做法
循环开始和结束时,first的值设置是本算法的精华
精妙之二上面已提到