约瑟夫环

微软面试题之一,难度系数中

题目描述:

题目:n 个数字(0,1,…,n-1)形成一个圆圈,从数字0 开始, 
每次从这个圆圈中删除第m 个数字(第一个为当前数字本身,第二个为当前数字的下一个
数字)。 
当一个数字删除后,从被删除数字的下一个继续删除第m 个数字。 
求出在这个圆圈中剩下的最后一个数字。


逻辑分析:

前言:赫赫有名的约瑟夫环,接触过算法的朋友想必耳熟能详(如果你学习了算法课程依旧不了解,思过崖3日游不谢),早年常见于大公司面试,且上机编程,尽管常见,难度系数还是有的。

1、一般来说,各大公司都是要求用链表来实现,尽管顺序表显然方便的多。思路也很简单,n个数字,一共需要删除n-1个,而每一次都要遍历m个元素,那么如果用计算机轮询,也就是模拟删除的方法(类似枚举),时间复杂度为O(m*(n-1)),显然是平方级的,效率虽然低,不过也是常规解法。这里提供一份代码。

#include <iostream.h>
#include <malloc.h>
typedef struct LNode
{
	int data;
	struct LNode *next;
}*LinkList;

void CreateList(LinkList &L)
{
	L=(LinkList)malloc(sizeof(LNode));
	L->data=0;
	L->next=NULL;
	LNode *p=L;
	cout<<"请输入数字的个数N:"<<endl;
	int n,i;
	cin>>n;
	LNode *q;
	for(i=0;i<n;i++)
	{
		q=(LinkList)malloc(sizeof(LNode));
		q->data=i;
		p->next=q;
		q->next=NULL;
		p=q;
	}
	q->next=L->next;
}

void Joesf(LinkList L,int m)
{
	LinkList p,q;
	p=L;
	int n;
	while(L->next->next!=L->next)
	{
		q=p->next;
		n=m;
		while(n>1)
		{
			q=q->next;
			p=p->next;
			n--;
		}
		if(q==L->next)
		{
			LNode *k=q;
			while(k->next!=q)
				k=k->next;
			L->next=q->next;
			k->next=q->next;
			//cout<<q->data<<endl;
		}
		else
		{
			p->next=q->next;
			//cout<<q->data<<endl;
			q=q->next;
		}
	}
	cout<<"最后一个元素是:"<<L->next->data<<endl;
}
void main()
{
	LinkList L;
	CreateList(L);
	int m;
	cout<<"请输入m:"<<endl;
	cin>>m;
	Joesf(L,m);
}






2、上述迷你过程效率低下,是因为每次找到要删除的数都要进行m次的查找。那么如何减少查找次数?这里就要借助数学来“化简过程”,通过计算删除的元素,比如第一次删除,(m-1)%n的结果即为要删除的数。删除以后,则首个元素变为(m-1)%n+1,为了方便,我们将(m-1)%n记为k。

3、重点在于如何找到删除前和删除后,各元素的位置关系,图示:

k+1    ->    0
k+2    ->    1

n-1    ->    n-k-2
0       ->    n-k-1

k-1   ->   n-2

现在我们知道了有n-1个数时last的位置,记为f(n-1,m),那么如何来求得f(n,m)关于f(n-1,m)之间的关系?

通过上表可以得到y=( x+k+1) %n,而由于k = (m-1)%n,所以y=(x+m)%n,最终关系如下:

                0                              n=1
f(n,m)={
                [f(n-1,m)+m]%n     n>1

 那么,代码也就呼之欲出了。

int LastRemaining(unsigned int n, unsigned int m)
{
    if(n < 1 || m < 1)
        return -1;
	
    int last = 0;
    for (int i = 2; i <= n; i ++) 
        last = (last + m) % i;
	
    return last;
}
显然,时间复杂度O(n)。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值