约瑟夫环问题(单链表实现)

本文通过分析和示例介绍了约瑟夫环问题,利用单链表解决此问题,包括构建链表结构体、链表功能函数以及主函数的详细步骤,最后展示运行结果。

约瑟夫环问题是一个很经典的问题:一个圈共有N个人(N为不确定的数字),第一个人的编号为0或者1(两个都可以,看你的程序如何编写),假设这边我将第一个人的编号设置为1号,那么第二个人的编号就为2号,第三个人的编号就为3号,第N个人的编号就为N号,现在提供一个数字M,第一个人开始从1报数,第二个人报的数就是2,依次类推,报到M这个数字的人出局,紧接着从出局的这个人的下一个人重新开始从1报数,和上面过程类似,报到M的人出局,直到N个人全部出局,请问,这个出局的顺序是什么?
举一个简单的例子:假设现在N的值为10,代表有10个人,M的值为3,代表报数报到3的人出局,那么出局的顺序就为:3 6 9 2 7 1 8 5 10 4

 

分析:

如上图所示,圈内矩形格子中的数字代表每个人的编号,从1开始编号到10。圈外半椭圆中的数字代表10个人的出局顺序。

注意:已经出局的人无需报数,报数的都是未出局的人。

从第一个人开始报数,报到3的人出局,因此,第一个出局的人为3号,3号出局之后,要从出局的这个人(3号)的下一个未出局的人(4号)重新开始从1开始报数,所以4号从1开始继续报数,那么,第二个出局的人就是6号,6号出局之后,要从出局的这个人(6号)的下一个未出局的人(7号)重新开始从1开始报数,所以7号从1开始继续报数,那么,第三个出局的人就是9号,9号出局之后,要从出局的这个人(9号)的下一个未出局的人(10号)重新开始从1开始报数,所以10号从1开始继续报数,那么第四个出局的人就是2号(10号报1,1号报2,2号报3,2号出局),2号出局之后,要从出局的这个人(2号)的下一个未出局的人(4号,这边3号已经出局了,不能报数,所以直接跳到4号)重新开始从1开始报数,那么第五个出局的人就是7号,7号出局之后,要从出局的这个人(7号)的下一个未出局的人(8号)重新开始从1开始报数,那么第六个出局的人就是1号,1号出局之后,要从出局的这个人的下一个未出局的人(4号)重新开始从1开始报数,那么第七个出局的人就是8号,8号出局之后,要从出局的这个人(8号)的下一个未出局的人(10号)重新开始从1 开始报数,那么第八个出局的人就是5号,5号出局之后,要从出局的这个人(5号)的下一个未出局的人(10号)重新开始从1开始报数,那么第九个出局的就是10号,10号出局之后,要从出局的这个人(10号)的下一个未出局的人(4号)重新开始从1开始报数,此时,N个人从只剩下4号还未出局,4号自己从1开始报数,自己数到3,那么它也出局了,4号是第十个出局的人。


思路:构建单向循环链表

1.构建链表结构体
typedef struct node
{
data_t data;
struct node *next;
}linknode,*linklist;
2.构建链表功能函数
//创建链表(无头结点)

linklist list_create(int indata){
    linklist H=(linklist)malloc(sizeof(linknode));
    if(NULL==H){
          printf("create fail\n");
          return NULL;
    }
        H->data=indata;
        H->next=H;
       return H;
}
//尾插法插入链表

int list_tail_insert(linklist H,data_t value){
     if(NULL == H){
		puts("入参非法!");
		return -1;
	}
    linklist New=(linklist)malloc(sizeof(linknode));
	if(NULL == New){
		puts("创建新节点失败!");
		return -1;
	}
        New->data = value;
        New->next=NULL;
       linklist q=H;
        while(q->next!=H){
             
            q=q->next;
        }
           q->next=New;
           New->next=H;

           return 0;

}


//链表打印
int list_show(linklist H){  
    if(NULL==H){
     puts("empty\n");
         return -1;
    }
    if(NULL==(H->next)){
           puts("empty\n");
           return -1;
    }
    linklist q=H;
       while(q->next!=H){
          printf("%d ",q->data);
            q=q->next;
       }
        
       printf("%d ",q->data);
              puts("");
                return 0;
}




//链表删除 pos是删除的位置,sum是链表的节点个数

linklist list_delete(linklist H,int pos,int sum){
    if(NULL==H){
     puts("empty\n");
         return NULL; 
    } 
    linklist p=(linklist)malloc(sizeof(linknode));
    linklist q=(linklist)malloc(sizeof(linknode));
    q=H;    
     for(int i=0;i<(pos+sum-1)%sum;i++){//此处完成指针的循环移动,移动到删除节点上一个位置
     q=q->next;
     }

    if(NULL==q){
     puts("empty\n");
         return NULL;
    }
    if((q->next)==NULL){
    printf("delete pos is invalid\n");
     return NULL;
    }
    p=q->next;//完成删除
    q->next=q->next->next;
    
//如果删除的节点为表尾,将删除后最后一个节点的next指向链表头
    if(p->next==H){
      q->next=H;
    } 
//如果删除位置是表头,将表头指向下一个节点
     if(p==H){
        H=p->next;
     }

    printf("free:%d\n",p->data);
    free(p);
    return H;
}
3.主函数
int main(int argc, char *argv[])
{
   int sum=0;
    printf("input sum\n");
   scanf("%d",&sum);
   linklist H=list_create(1);
    for(int i =2;i<=sum;i++){
    list_tail_insert(H,i);
    }
    list_show(H);
    int kill;
    printf("input kill\n");
    scanf("%d",&kill);
   
    int flag=1;//用来标志杀死的对象,并且移动指针
    int step=0;//用来传递杀死的位置
    while(H->next!=H){ 
    linklist k=(linklist)malloc(sizeof(linknode));
     k=H;

    if(flag==kill){
    H=list_delete(H,step,sum);//杀死目标
     sum--;
     flag=1;
    }
     if((step+1)==sum){ //位置置为0;
       step=0;
     }else{
     step++;
     }
     flag++;
     k=k->next;
  list_show(H);
    
    }
    printf("survival is %d",H->data);
    return 0;
} 

运行结果

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值