循环链表解决约瑟夫问题

约瑟夫问题:

据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

通俗的说一下规则:

  • 所有的人(包括你自己)围成一个圈并编号
  • 从第一个人开始顺时针报数,报到数字k的人被杀掉并移除圈
  • 从被杀死的下一个人继续报数,报到数字k的人再被杀掉并移除
  • 直到剩下最后一个人

这个问题的解决方法有很多种,可以用递归,用数学推导,用数组完成,下面介绍一种用链表模拟的方法解决这个问题。

提供两段实现的代码供参考:

第一段代码:

///在一个主函数中实现
#include
#include

typedef struct List
{
    int  data;
    struct List *next;
}LinkList;


int main()
{
    LinkList *L,*r,*s;
    L = (LinkList *)malloc(sizeof(LinkList));
    r =L;
    int n,i;
    int k;
    printf("Please input the number of people:\n");
    scanf("%d",&n);
    printf("The number to count each round:\n");
    scanf("%d",&k);

    for(i = 1;i<=n;i++)               ///尾插法建立循环链表
    {
        s = (LinkList *)malloc(sizeof(LinkList));
        s->data = i;
        r->next = s;
        r= s;
    }
    r->next =L->next;                  //让最后一个链表的下一个节点指向开头
    
	
    LinkList *p,*q;
    p = L->next;

    while(p->next != p)                            //开始模拟(判断条件要注意:因为最后肯定剩下一个人, 所以最后一个数据的next还是他本身)
    {
        for(i = 1;inext;                         //从第一个人开始数k-2次
        }
        q = p->next;
        p->next = p->next->next;    //将该节点从链表上删除,第k个人死去
        free(q);
        p = p->next;                  //p指针指向死去的下一个人
    }
    printf("The last one :%d",p->data);
    return 0;
}

结果展示:

第二段代码:

///用函数实现
#include 
#include 
#define LEN sizeof (LinkList)  //定义struct List这个类型的长度

typedef struct List  //定义结构体
{
    int num;
    struct List *next;
}LinkList;

LinkList *create(int m)  //创建循环链表
{
    LinkList *head,*p1,*p2;
    int i;
    head=p1=(LinkList*)malloc(LEN);
    head->num=1;
    for(i=1,p1->num=1;inum=i+1;
        p1->next=p2;
        p1=p2;
    }
    p2->next=head;
    return head;
}

LinkList *findout(LinkList *start,int n)  //找到需要删除结点的前一个结点
{
    int i;
    LinkList *p;
    p=start;
    for(i=1;inext;
    return p;
}

LinkList *letout(LinkList *last)  //删除下一个结点并返回下下个结点
{
    LinkList *out,*next;
    out=last->next;
    last->next=out->next;
    next=out->next;
    printf("%d-->",out->num);    //打印死亡顺序
    free(out);
    return next;
}

int main()
{
    int m,n,i,king;
    LinkList *p;
    printf("Please input the number of people:\n");
    scanf("%d",&m);
    printf("The number to count each round:\n");
    scanf("%d",&n);
    if(n==1)
    {
        king=m;
    }
    else
    {
        p=create(m);
        for(i=1;inum;
        free(p);
    }
    printf("\n");
    printf("The last one is:%d\n",king);
    return 0;
}

结果展示:

循环链表解决约瑟夫问题_第1张图片

 ===============================================================

约瑟夫问题的另一种解法:参考 约瑟夫环问题的两种解法(详解):https://blog.csdn.net/okasy/article/details/79503398

递归实现:参考 https://www.cnblogs.com/Bravewtz/p/10089717.html

循环链表实现C++代码:

#include
 
using namespace std;
 
/************约瑟夫问题****************/
 
typedef struct CLinkList
{
    int data;
    struct CLinkList *next;
}node;
 
 
int main()
{
///建立循环链表
    node *L,*r,*s;
    L = new node;
    r =L;
    int n = 41,i;
    int k = 3;
    for(i = 1;i<=n;i++)       //尾插法建立链表
    {
        s = new node;
        s->data = i;
        r->next = s;
        r= s;
    }
    r->next =L->next;       //让最后一个结点指向第一个有数据结点
    node *p;
    p = L->next;
    delete L;   //删除第一个空的结点
 
///模拟解决约瑟夫问题
    while(p->next != p)                            //判断条件:因为最后肯定剩下一个人, 循环链表的最后一个数据的next还是他本身
    {
        for(i = 1;inext;                         //每k个数死一个人
        }
        cout<next->data<<"->";
        p->next = p->next->next;                   //将该节点从链表上删除。
        p = p->next;
    }
    cout<data<

运行结果:

 


附上华为2016研发工程师编程题

这道题就是一个约瑟夫问题。

循环链表解决约瑟夫问题_第2张图片

#include 

using namespace std;

typedef struct CLinList
{
	int data;
	struct CLinList *next;
}node, *Pnode;

int main()
{
	int n;
	while (cin >> n)
	{
		Pnode L, r, s;
		L = new node;
		r = L;
		int i;
		int k = 3;
		for (i = 0; i < n; i++)
		{
			s = new node;
			s->data = i;
			r->next = s;
			r = s;
		}
		r->next = L->next;
		Pnode P;
		P = L->next;
		delete L;

		while (P->next != P)
		{
			for (i = 1; i < k - 1; i++)
			{
				P = P->next;
			}
			//cout << P->next->data << "->";
			P->next = P->next->next;
			P = P->next;
		}
		cout << P->data << endl;
		
	}
	
	return 0;
}

 

你可能感兴趣的:(数据结构与算法)