约瑟夫环问题--循环单链表实现(李春葆老师的解法)

// 约瑟夫环问题李春葆解法.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的值设置是本算法的精华

精妙之二上面已提到

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值