NC132 环形链表的约瑟夫问题
单向循环链表+模拟
- 尾插法建立单向链表,节点的val为自己的编号
- 最后将尾节点的 next 指向头结点,形成单向循环列表
- 进行循环操作,循环结束的条件为链表中只剩下一个元素,也就是
p.next === p
- count 用于计数,初始化为1;p为当前节点,初始化从head开始;pre为p的前一个节点,初始化为rear;
- 如果当前计数达到 m ,表明p需要被删除,让 pre.next = p.next 即可,p后移一位
- 如果不需要删除,count + 1,pre,p都后移一位
- 最终循环结束,循环链表中只剩下一个p,返回 p.val 也就是这个节点的编号
/**
*
* @param n int整型
* @param m int整型
* @return int整型
*/
function Node(x){
this.val = x
this.next = null
}
function ysf( n , m ) {
if(n === 1) return 1
// 单向循环列表
let head = new Node(1)
let rear = head
for(let i = 2; i <= n ; i++){
let node = new Node(i)
rear.next = node
rear = node
}
rear.next = head
let count = 1
let p = head
let pre = rear
// 当链表中有大于两个节点的时候
while(p.next !== p){
// 当前节点需要被删除
if(count === m){
pre.next = p.next
p = p.next
count = 1
}else{
count ++
pre = p
p = p.next
}
}
return p.val
}
module.exports = {
ysf : ysf
};
数学
看了题解发现可以使用数学方法来解答,通过寻找其中的规律来解答
f(n,m)
表示有n个人,报m出局f(1,m)
一个人的话最终留下的编号为0(从0开始计数)f(2,m)
需要以前一轮结束的位置开始再进行一轮,结果也就是留下 (f(1,m)
+ m ) % 2...
f(n,m)
= (f(n - 1 , m)
+ m ) % n- 所以只需要根据这样的规律推出
f(n,m)
就可以了
/**
*
* @param n int整型
* @param m int整型
* @return int整型
*/
function Node(x){
this.val = x
this.next = null
}
function ysf( n , m ) {
if(n === 1) return 1
let res = 0
for(let i = 2; i <= n; i ++){
res = (res + m) % i
}
//题目要求编号从 1 开始,所以这里加上1
return res + 1
}
module.exports = {
ysf : ysf
};