约瑟夫环问题与进阶

手撕代码----约瑟夫环问题

据说著名犹太历史学家 Josephus 有过以下故事:在罗马人占领乔塔帕特后,39 个犹太人与 Josephus 及他的朋友躲到一个洞中,39 个犹太人决定宁愿死也不要被敌人抓到,于是决定了一种自杀方式,41 个人排成一个圆圈,由第 1 个人开始报数,报数到 3 的人就自杀,然后再由下一个人重新报 1,报数到 3 的人再自杀,这样依次下去,直到剩下最后一个人时,那个人可以自由选择自己的命运。这就是著名的约瑟夫问题。现在请用单向环形链表得出最终存活的人的编号。
题目链接

输入描述

一行两个整数 n 和 m, n 表示环形链表的长度, m 表示每次报数到 m 就自杀。

输出描述

输出最后存活下来的人编号(编号从1开始到n)

基本思路

  1. 考虑特殊情况,比如只有一个节点,m<1等情况
  2. 新建链表结点指针,使得 last.next = head。这样的原因是当head所在节点为需要删除的节点时,可以利用head的前驱节点last进行断链、连接等操作方便删除节点。
  3. 当最后只有一个节点时,即head = last就输出节点的值。
  4. 时间复杂度分析:每次删除一个节点需遍历m次,一共需要删除n-1个节点,时间复杂度为O(m*n).
import java.util.*;
//创建节点类,为了创建环形链表
class Node{
    int val;
    Node next;
    public Node(){}
    public Node(int val){
        this.val  = val;
    }
    public Node(int val, Node next){
        this.val = val;
        this.next = next;
    }
}

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()){
            int num = sc.nextInt();
            int m = sc.nextInt();
            Node head = new Node(1);
            Node p = head;
            //创建单链表
            for(int i=2;i<=num;i++){
                Node node = new Node(i);
                p.next = node;
                p = p.next;
            }
            //成环操作
            p.next = head;
            System.out.println(solution(head, m));
        }
    }
    //解决问题的函数
    public static int solution(Node head, int m){
    	//特殊情况考虑
        if(head==null||head.next==null||m<1) return head.val;
        Node last = head;
        //指定last指针,满足思路2
        while(last.next!=head){
            last = last.next;
        }
        int count = 1;
        while(head!=last){
        	//删除节点操作
            if(count==m){
                head = head.next;
                last.next = head;
                count = 1;
            }else if(count<m){
                head = head.next;
                last = last.next;
                count++;
            }
        }
        return head.val;
    }   
}

进阶思路(时间复杂度O(N))

数学归纳法,递归实现
(1,2,3,…,m-1,m,m+1,…,n)—>(n-m+1,n-m+2,…,n-1,1,…, n-m)
数学归纳公式:F(n) = (F(n-1)+m-1)%n+1(未完待续)

//解决问题的主函数
public static int solution(Node head, int m){
	//特殊情况考虑
	if(head==null||head.next==null||m<1) return head.val;
    Node last = head.next;
    int temp = 1;
    while(last!=head){
    	temp++;
    	last = temp.next;
    }
    temp = getLive(temp, m);
    while(--temp!=0){
    	head = head.next;
    }
    head.next = head;
    return head.val;
}   
public static int getLive(int i, int m){
	if(i==1) return 1;
	return (getLive(i-1, m)+m-1) % i+1;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值