【经典算法】LCR187:破冰游戏(约瑟夫问题,Java/C/Python3/JavaScript实现含注释说明,Easy)

目录

  • 题目
  • 思路及实现
    • 方式一:迭代模拟(用链表模拟这个游戏)
      • 思路
      • 代码实现
        • Java版本
        • C语言版本
        • Python3版本
      • 复杂度分析
    • 方式二:数学+迭代
      • 思路
      • 代码实现
        • Java版本
        • C语言版本
        • Python3版本
      • 复杂度分析
    • 方式三:递归
      • 思路
      • 代码实现
        • Java版本
        • C语言版本
        • Python3版本
      • 复杂度分析
  • 总结
  • 相似题目

  • 标签:递归 | 数学

题目

社团共有 num 位成员参与破冰游戏,编号为 0 ~ num-1。成员们按照编号顺序围绕圆桌而坐。社长抽取一个数字 target,
从 0 号成员起开始计数,排在第 target 位的成员离开圆桌,且成员离开后从下一个成员开始计数。
请返回游戏结束时最后一位成员的编号。示例 1:输入:num = 7, target = 4
输出:1
示例 2:输入:num = 12, target = 5
输出:0
提示:1 <= num <= 10^5
1 <= target <= 10^6

原题:LeetCode LCR187
在这里插入图片描述

思路及实现

约瑟夫问题:

这个问题是以弗拉维奥·约瑟夫命名的,他是1世纪的一名犹太历史学家。他在自己的日记中写道,他和他的40个战友被罗马军队包围在洞中。他们讨论是自杀还是被俘,最终决定自杀,并以抽签的方式决定谁杀掉谁。约瑟夫斯和另外一个人是最后两个留下的人。约瑟夫斯说服了那个人,他们将向罗马军队投降,不再自杀。约瑟夫斯把他的存活归因于运气或天意,他不知道是哪一个。
—— 【约瑟夫问题】

详见:约瑟夫问题

方式一:迭代模拟(用链表模拟这个游戏)

思路

这是经典的约瑟夫问题(Josephus Problem)。我们可以模拟这个过程,使用一个列表来存储成员编号,每次计数到 target 时,将当前成员移除列表,然后计数到下一个成员。重复此过程,直到列表里只剩下一个成员,返回该成员的编号。

代码实现

Java版本
public int lastRemaining(int num, int target) {List<Integer> members = new ArrayList<>();for (int i = 0; i < num; i++) {members.add(i);}int index = 0;while (num > 1) {index = (index + target - 1) % num; // 减1因为从0开始计数,取余是因为是圆桌members.remove(index);num--;}return members.get(0);
}

说明:
迭代地模拟成员被移出的过程,index 表示每次需要移除成员的位置。

C语言版本
#include <stdio.h>
#include <stdlib.h>int lastRemaining(int num, int target) {// 创建一个动态数组来模拟成员围坐一圈的情况int *members = (int *)malloc(num * sizeof(int));// 初始化成员编号for (int i = 0; i < num; i++) {members[i] = i;}int current = 0; // 当前计数开始的位置int remaining = num; // 剩余成员数while (remaining > 1) {// 计算要移除成员的索引位置int removeIndex = (current + target - 1) % remaining;// 从数组中移除成员for (int j = removeIndex; j < remaining - 1; j++) {members[j] = members[j + 1];}// 更新当前计数开始的位置current = removeIndex % (remaining - 1);// 更新剩余成员数remaining--;}// 记录最后剩下的成员编号int lastMember = members[0];// 释放动态数组所占用的内存free(members);return lastMember;
}// 测试程序
int main() {int num = 7, target = 4;printf("The last remaining member is: %d\n", lastRemaining(num, target));return 0;
}

说明:
代码实现了迭代模拟方式来解决约瑟夫环问题。首先初始化成员编号,然后根据游戏规则逐一模拟计数与成员被移除的过程。注意,由于成员编号是从0开始,所以移除成员的索引位置需要进行 target - 1 处理。每次有成员移除后,都需要更新计数的起始位置以及剩余的成员数量。最终剩下的成员的编号即为所求。
此外,代码还处理了动态分配内存的释放,以避免内存泄漏问题。

Python3版本
def last_remaining(num, target):members = list(range(num))index = 0while num > 1:index = (index + target - 1) % num # 减1因为从0开始计数,取余是因为是圆桌members.pop(index)num -= 1return members[0]

说明:
Python版本的实现思路与Java版本相同,使用列表和迭代的方式模拟约瑟夫环的过程。

复杂度分析

  • 时间复杂度:O(num^2),因为每次删除操作都需要 O(num) 的时间
  • 空间复杂度:O(num),存储成员编号需要的空间

方式二:数学+迭代

思路

在约瑟夫问题中,可以找到递归的关系f(n, m) = (f(n-1, m) + m) % n,其中f(n, m)表示第n轮中以m开始计数的最后胜利者的位置。

代码实现

Java版本
public int lastRemaining(int num, int target) {int res = 0; // num=1时最后剩下的成员编号for (int i = 2; i <= num; i++) {res = (res + target) % i;}return res;
}

说明:
基于递归关系迭代地求解最后剩下成员的编号,避免了昂贵的数组删除操作。

C语言版本
#include <stdio.h>int lastRemaining(int num, int target) {int res = 0; // 最开始,编号为0的成员肯定会留下// 从第二位成员开始迭代,直到num位成员for(int i = 2; i <= num; i++) {res = (res + target) % i;}return res;
}int main() {int num = 7, target = 4;printf("The last remaining member is: %d\n", lastRemaining(num, target));return 0;
}

说明
从1计数到 num,代表每一轮的成员数。在每轮计算中,
res 的值为上一轮中剩下成员的位置,将其与 target 相加后对当前轮的成员数取余数,得到新一轮中剩余成员的位置。
最后返回 res,即为最后剩下成员的编号。

Python3版本
def last_remaining(num, target):res = 0  # num=1时最后剩下的成员编号for i in range(2, num + 1):res = (res + target) % ireturn res

说明:
利用递归关系进行迭代求解

复杂度分析

  • 时间复杂度:O(num),只需迭代 num-1 次
  • 空间复杂度:O(1),仅需常数个变量存储中间结果

方式三:递归

思路

约瑟夫问题还可以采用递归的思路来解决。对于 num 个人的情况,如果我们知道了 num-1 个人的情况下的胜利者的索引,那么我们可以通过递归关系得到 num 个人时的最终胜利者。
递归关系如下:

f(n, m) = (f(n-1, m) + m) % n

其中 f(1, m) = 0,f(n, m) 表示总数为 n,计数为 m的情况下最后胜利者的索引。

代码实现

Java版本
public int lastRemaining(int num, int target) {return lastRemainingRec(num, target);
}private int lastRemainingRec(int num, int target) {if (num == 1) {// 只有一个成员时,他肯定是胜利者return 0;} else {// 递归计算 num-1 个成员时的胜利者的索引,并应用递归关系return (lastRemainingRec(num - 1, target) + target) % num;}
}

说明:递归在每次调用中计算 num-1 的情况,并将结果使用到 num 个成员的情况。

C语言版本
#include <stdio.h>int lastRemainingRec(int num, int target) {if (num == 1) {// 只有一个成员时,他肯定是胜利者return 0;} else {// 递归计算 num-1 个成员时的胜利者的索引,并应用递归关系return (lastRemainingRec(num - 1, target) + target) % num;}
}int lastRemaining(int num, int target) {return lastRemainingRec(num, target);
}int main() {int num = 7, target = 4;printf("The last remaining member is: %d\n", lastRemaining(num, target));return 0;
}

说明:采用递归方式,递归的边界情况是只剩一个成员时,其编号为0。非边界情况使用递归函数计算。

Python3版本
def last_remaining_rec(num, target):if num == 1:# 只有一个成员时,他肯定是胜利者return 0else:# 递归计算 num-1 个成员时的胜利者的索引,并应用递归关系return (last_remaining_rec(num - 1, target) + target) % numdef last_remaining(num, target):return last_remaining_rec(num, target)# 示例
print(last_remaining(7, 4))  # 输出: 1
print(last_remaining(12, 5)) # 输出: 0

说明:Python 版本的实现中同样使用递归,直观地展示了解法的递归逻辑结构。

复杂度分析

  • 时间复杂度:O(num),因为递归函数将被调用 num 次。
  • 空间复杂度:O(num),递归需要使用栈空间,其大小取决于递归的深度,最大为 num。

总结

方式描述优点缺点时间复杂度空间复杂度
迭代模拟直接根据规则模拟整个游戏过程,依次淘汰成员直观和易理解当成员数目较大时,效率较低O(num^2)O(num)
数学+迭代通过数学公式递推最终结果,逐步缩小问题规模时间效率高,不需要昂贵的删除操作需要数学知识,公式推导可能不够直观O(num)O(1)
递归通过递归函数,从基础情况逐步返回最终答案代码简洁,易编写栈空间开销大,可能会栈溢出O(num)O(num)
迭代改进递归方法的迭代版本,避免了栈溢出的问题避免了递归引起的栈溢出相对于直接递归,可能理解起来稍微复杂O(num)O(1)

相似题目

题号名称难度相似点
LeetCode-141Linked List CycleEasy使用快慢指针判断链表是否有环
LeetCode-142Linked List Cycle IIMedium寻找链表中环的入口点
LeetCode-202Happy NumberEasy利用快慢指针寻找循环
LeetCode-287Find the Duplicate NumberMedium数组可以视为链表,寻找环的入口
LeetCode-206Reverse Linked ListEasy链表的基本操作
LeetCode-234Palindrome Linked ListEasy链表操作和快慢指针
LeetCode-160Intersection of Two Linked ListsEasy寻找两个链表的交点

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/801906.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【CSS疑难点汇总】1.bor-box失效情况总结以及高宽设置为auto的情况

1. box-sizing box-sizing是改变盒子宽高的计算方式&#xff0c;一般使用bor-box&#xff0c;消除padding和border对整个盒子的影响&#xff0c;但在没有明确给出宽高的情况下&#xff0c;box-sizing是没有效果的 1.1 box-sizing不生效的情况 1.1.1块级盒子嵌套 ​ 宽度继承…

(一)基于IDEA的JAVA基础12

一维数组 为什么使用数组: 当我们需要存储一系列数据的时候&#xff0c;就需要用到数组&#xff0c;如果不使用数组&#xff0c;我们就要需要一个一个的去声明变量&#xff0c;这样浪费内存空间&#xff0c;同时效率低下。 什么是数组: 数组本身就是一个变量&#xff0c;只…

(3)(3.1) 英特尔Realsense深度摄像头(二)

文章目录 前言 5 配置ArduPilot 6 地面测试&#xff1a;飞行前验证 7 飞行测试 8 实验 9 数据闪存记录 前言 本文介绍如何将英特尔 Realsense 深度摄像头(Intel Realsense Depth Camera)与 ArduPilot 配合使用&#xff0c;以实现避障(obstacle avoidance)。该方法使用在…

javaScript手写专题——实现instanceof/call/apply/bind/new的过程/继承方式

目录 原型链相关 手写instanceof 实现一个_instance方法&#xff0c;判断对象obj是否是target的实例 测试 手写new的过程 实现一个myNew方法&#xff0c;接收一个构造函数以及构造函数的参数&#xff0c;返回构造函数创建的实例对象 测试myNew方法 手写类的继承 ES6&…

照顾宝贝的双眼:新生儿大小眼关爱手册

引言&#xff1a; 宝宝的眼睛是他们认知世界的窗户&#xff0c;眼睛的健康至关重要。大小眼&#xff0c;即一只眼睛比另一只眼睛大或小&#xff0c;是新生儿时期可能出现的一种眼部异常。尽管在新生儿中相对较少见&#xff0c;但家长们仍然需要了解如何正确照顾宝宝的大小眼&am…

计算机视觉之三维重建(7)---多视图几何(下)

文章目录 一、透视结构恢复问题1.1 概述1.2 透视结构恢复歧义1.3 代数方法1.4 捆绑调整 二、P3P问题三、随机采样一致性 一、透视结构恢复问题 1.1 概述 1. 透视结构恢复问题&#xff1a;摄像机为透视相机&#xff0c;内外参数均未知。 2. 问题&#xff1a;已知 n n n 个三维…

蓝桥杯每日一题:n-皇后问题(回溯)

n−皇后问题是指将 n 个皇后放在 nn 的国际象棋棋盘上&#xff0c;使得皇后不能相互攻击到&#xff0c;即任意两个皇后都不能处于同一行、同一列或同一斜线上。 现在给定整数 n&#xff0c;请你输出所有的满足条件的棋子摆法。 输入格式 共一行&#xff0c;包含整数 n。 输出…

主流电商平台开放接口:电商数据采集API获取淘宝商品详情描述主图SKU价格订单物流

API接口 返回值说明 item_get-获得淘宝商品详情 API 注册开通 taobao.item_get 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09…

矩阵链乘法问题

描述 输入 输入共n1行 第一行输入矩阵的总个数n[2,1000] 后n行分别输入矩阵的维数[1,100] 输出 最后一行输出少乘法次数 输入样例 1 6 30 35 35 15 15 5 5 10 10 20 20 25 输出样例1 15125 代码实现 #include<iostream> #include<vector> #include<…

基于ssm的星空游戏购买下载平台的设计与实现论文

摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff0c;商品交易当然也不能排除在外&#xff0c;随着商品交易管理的不断成熟&#xff0c;它彻底改变了过去传统的经营管理方式&#xff0c;不仅使商品…

ES6中 Promise的详细讲解

文章目录 一、介绍状态特点流程 二、用法实例方法then()catchfinally() 构造函数方法all()race()allSettled()resolve()reject() 三、使用场景# 参考文献 一、介绍 Promise&#xff0c;译为承诺&#xff0c;是异步编程的一种解决方案&#xff0c;比传统的解决方案&#xff08;…

c++的学习之路:17、stack、queue与priority_queue

摘要 本文主要是介绍一下stack、queue、priority_queue的使用以及模拟实现&#xff0c;文章末附上代码以及思维导图。 目录 摘要 一、stack的介绍和使用 1、stack的介绍 2、stack的使用 3、stack的模拟实现 二、queue的介绍和使用 1、queue的介绍 2、queue的使用 3、…

uniapp引入微信小程序版本VantUI,使用VantUI的自定义tabbar,并解决自定义tabbar出现闪烁的情况

视频教程地址&#xff1a;https://www.bilibili.com/video/BV13m41167iG 1.uniapp引入微信小程序版本VantUI 去vant官网下载源码&#xff0c;源码放在github&#xff0c;自行去下载下来 https://vant-contrib.gitee.io/vant-weapp/#/home 在pages.json的globalStyle里面注册组…

打印月历 Open Judge

题面链接: http://noi.openjudge.cn/ch0113/24/ 题目描述: 评析: 大模拟题&#xff0c;考察的是你的耐心和毅力&#xff01;很不错的模拟题练习题&#xff0c;小白(like me)可以练一练 思路: 先一个月一个月的模拟&#xff0c;求出来题目问的这个一年的这一个月的第一天是星期…

欧盟网络安全局:公共数据空间中的个人数据保护设计(上)

文章目录 前言一、背景:欧盟数据空间(一)欧盟数据空间的设计原则二、欧盟数据空间中数据保护的注意事项(一)欧盟数据空间关键术语(二)数据共享环境中输入隐私和输出隐私问题(三)数据保护工程的作用(四)数据空间中的数据保护影响评估(五)问责制的主要内容(六)通过…

Day30 回溯 LeedCode 332.重新安排行程 51. N皇后 37. 解数独 蓝桥杯 与或异或

332. 重新安排行程 给你一份航线列表 tickets &#xff0c;其中 tickets[i] [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。 所有这些机票都属于一个从 JFK&#xff08;肯尼迪国际机场&#xff09;出发的先生&#xff0c;所以该行程必须从 JFK…

Macos 部署自己的privateGpt(2024-0404)

Private Chatgpt 安装指引 https://docs.privategpt.dev/installation/getting-started/installation#base-requirements-to-run-privategpt 下载源码 git clone https://github.com/imartinez/privateGPT cd privateGPT安装软件 安装&#xff1a; Homebrew /bin/bash -c…

径流场水土流失自动监测系统的使用

TH-LS1随着环保意识的日益增强&#xff0c;水土流失问题已经成为全球关注的焦点。水土流失不仅破坏了生态环境&#xff0c;还对农业生产、水资源保护等方面产生了严重影响。为了有效监测和控制水土流失&#xff0c;径流场水土流失自动监测系统应运而生。 一、径流场水土流失自…

希尔排序解读

在算法世界中&#xff0c;排序算法是至关重要的一部分。而希尔排序&#xff08;Shell Sort&#xff09;作为一种基于插入排序的改进算法&#xff0c;通过允许交换非相邻元素&#xff0c;从而在一定程度上提高了排序效率。本文将深入探讨希尔排序的原理、实现方式以及它的性能特…

stable diffusion的从安装到使用

stable-diffusion&#xff0c;一个免费开源的文生图软件&#xff0c;文章主要讲怎么从源码开始安装&#xff0c;以及使用的方式 git地址&#xff1a;https://github.com/AUTOMATIC1111/stable-diffusion-webui 本人电脑环境win10&#xff0c;软件pycharm&#xff0c;需要提前…