约瑟夫问题c、Java语言实现

本文详细介绍了约瑟夫问题的两种实现方法:使用链表和数组循环,通过C语言和Java代码实例展示了从第k个人开始报数直到剩余最后一个人的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

n个人围成一圈,从第k人个开始报数,假设数到m的人出圈,最后剩下一个,求这个人的编号?

一、链表实现

 c 语言实现:

#include<stdio.h>
#include<stdlib.h>
typedef struct child
{
    int num;
    struct child * next;
}child;

child * init_child(child * head, int n)
{
    int i;
    child * temp, * cur;

    //构造一个环形链表
    for (i = 1; i <= n; i++)
    {
        temp = (child *)malloc(sizeof(child));
        temp->num = i;
        if (i == 1)   //只有一个人的时候
        {
            head = temp; //让头指针指向分配的内存空间
            head->next = temp; //指向自己
            cur = head;        //记录当前,方便下面的操作
        }
        else
        {
            cur->next = temp;
            temp->next = head;
            cur = temp;
        }
    }
    return head;
}

void show_nu(child * head)
{
    child * cur;
    cur = head;
    printf("孩子们的编号为:\n");
    while (cur->next != head)       //在打印循环链表最后一个数时,判断成立,所以需要下面的printf()语句
    {
        printf("%d\t", cur->num);
        cur = cur->next;
    }
    printf("%d\n", cur->num);
}

/*
target 从第几个人开始数
num 数到编号为几的出列
*/ 
void start_game(child * head, int target, int num)
{
    child * tail; //跳到循环链表的最后一节点个位置, tail 的下一个节点就是head
    tail = head;
    int i;
    while (tail->next != head)
    {
        tail = tail->next;
    }
    
    //处理从第几个人开始数,让头指针head指向那个人
    for (i = 1; i < target; i++)  //头指针只需移动(target - 1)次
    {
        head = head->next;
        tail = tail->next;
    }
    
    //下面就是移动head和tail指针了,将不幸的孩子从循环链表中踢去
    //tail和head每移动一次相当于数2下,自己数不用移动;移动2次相当于数3下
    printf("被踢掉的编号为:\n");
    while (head != tail)
    {
        for (i = 1; i < num; i++)
        {
            head = head->next;
            tail = tail->next;
        }
        //移动完后,将head指向的那个删掉
       printf("%d\t", head->num);
       head = head->next;
       tail->next = head;
    }
    printf("\n最后留下的编号为: %d\n", head->num);
}

int main()
{
    child * head;  
    int total; //小孩的总数
    int target; //出列的数字
    int num;    //数到哪个编号出列
    printf("约瑟夫问题。\n");
    printf("请输入小孩的个数?孩子会从1到n进行编号:");
    scanf("%d", &total);
    printf("从编号几开始数:");
    scanf("%d", &target);
    printf("数到哪个编号出列:");
    scanf("%d", &num);

    //初始化,构造一个循环链表
    head = init_child(head, total);
    //打印小孩的编号
    show_nu(head);
    //开始最重要的操作了,要出列的小孩就遭殃了
    start_game(head, target, num);
    return 0;
}

运行结果为:

 

 

类似的思路,Java 实现:

public class MyJoseph{
	// 成员内部类 
	static class Node{
		int num;
		Node next;
		Node(int v){
			num = v;
		}
	}

	public static void main(String[] args) {
		int total = 7; //共有10个人
		int start = 3; // 从3开始数
		int target = 4; // 数到四出列

		//构造循环链表
		Node head = new Node(1);  // 创建第一个结点
		Node cur = head;          //设另一个指针指向第一个结点
		for (int i = 2; i <= total; i++){
			cur = (cur.next = new Node(i));
			cur.next = head;
		}

		//从 3 开始数 让head指针指向3
		for (int i = 0; i < start - 1; i++){
			head = head.next;
			cur = cur.next;
		}

		// head指针移动n次,就数到了 n+1
		System.out.println("out of the queue is ");
		while (head != cur){
			for (int i = 1; i < target; i++){
				head = head.next;
				cur = cur.next;
			}
			System.out.println(head.num);
			head = head.next;
			cur.next = head;
		}
		
		System.out.println("last num is " + head.num);


	}
}

二、数组循环实现

public class Joseph {
    public static void main(String[] args) {
        int total = 10;
        int start = 4;
        int target = 3;
        System.out.println("Last person: " + solution(total, start, target));
    }

    /**
     * @param total 10 个人
     * @param start 从 2 开始数
     * @param target  数到 3 出列
     * @return 返回最后出列的那个人
     */
    public static int solution(int total, int start, int target) {
        boolean[] circle = new boolean[total];
        for (int i = 0; i < total; i++) {
            circle[i] = true;
        }
        int last = -1;
        // 剩下的人数
        int remain = total;
        int index = start - 1;
        // 计数
        int count = 0;
        System.out.println("Already out: ");
        while (remain >= 1) {
            // 开始数
            if (circle[index]) {
                count++;
                if (count == target) {
                    // 此人出列 
                    circle[index] = false;
                    System.out.print(index + " ");
                    // 剩下的人数减 1
                    remain--;
                    if (remain == 0) {
                        last = index;
                    }
                    // 计数器归 0
                    count = 0;
                }

            }
            index++;

            if (index == circle.length) {
                index = 0;
            }
        }
        System.out.println();
        return last;
    }
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值