1823. 找出游戏的获胜者【中等题】【每日一题】
思路:【约瑟夫环】
本题使用约瑟夫环进行求解,下面写一下我对约瑟夫环解法的一点理解。
- 根据题意,小伙伴们的编号从
1
开始,到n
结束。现在假设这是一个数组,下标从0
开始,则数组值比对应下标多1
。- 根据规则,每次游戏数组都会根据规损失一个元素,那么游戏结束时数组长度一定为
1
,即只剩下一个游戏获胜者,它的数组下标必然是0
。- 游戏获胜者在每一轮游戏中都不可能被淘汰,否则怎么取得最后的胜利,于是我们可以根据一定的规则,倒着推导出获胜者在每一轮游戏开始时所在的数组下标,直到推导出第一轮游戏开始时的数组下标,得出获胜者在第一轮游戏开始时的数组下标之后,由于下标与值之间的对应关系,我们很容易就能得出最后的获胜者编号等于其在第一轮游戏开始时的编号
+1
。- 那么如何确定倒推规则?
- 我们已经确定最后一轮游戏开始时,数组长度为
1
,那么倒数第2
轮游戏开始时,数组长度肯定为2
,倒数第3
轮数组长度为3
,…。- 游戏的规则是,从某个
i
位置开始,删除第i+k-1
位,然后从i+k
开始,删除第i+k+k-1
位,…,可以认为,从第i
个位置开始,第i+k
位一定不会被删除(因为删的是第i+k-1
位),那么当我知道了这一轮游戏胜利者的下标之后,在下一轮游戏中,对胜利者这样操作必不可能导致胜利者被删除。- 假设现在是倒数第
i
轮游戏(i
从2
开始,到n
结束,倒数第n
轮即游戏第1
轮)。- 设胜利者在第
i
轮下标为i
,那么在下一轮(即第i-1
轮)的下标必然为(i+k) % i
(由步骤5
可知,倒数第i
轮游戏时数组长度为i
,将下标对i
取余是为了防止数组越界,因为这是一个环,当走到数组最后一位时,会跳回数组第1
位所在位置)。- 因此我们设倒数第
1
轮下标 为ans = 0
,倒序遍历每一轮,即i
从2
到n
遍历,每一次倒推都会更新当前游戏胜利者所在的下标为ans = (ans + k) % i
。- 循环结束时的
ans
即为胜利者在第一轮游戏开始时的下标,其游戏编号为ans + 1
。
代码:
class Solution {
public int findTheWinner(int n, int k) {
int ans = 0;
for(int i = 2;i <= n; i++){
ans = (ans + k) % i;
}
return ans + 1;
}
}
剑指 Offer II 028. 展平多级双向链表【中等题】
思路:【DFS】
从头节点开始依次遍历,遇到子节点就进入子节点继续判断是否还有子节点,直到遍历到当前这级链表的末尾,将这个尾结点标记为这一级子链表的尾结点,将其插入到其父节点和父节点原来的右节点之间。
写的可能有点绕,看注释更容易理解一点,参考自官方题解。
代码:
/*
// Definition for a Node.
class Node {
public int val;
public Node prev;
public Node next;
public Node child;
};
*/
class Solution {
public Node flatten(Node head) {
dfs(head);
return head;
}
public Node dfs(Node node){
//记录传入的当前节点
Node cur = node;
//记录待处理链表的最后一个节点
Node last = null;
while(cur != null){
Node next = cur.next;
//如果存在子节点,那么需要将子节点链表扁平化处理插入到当前节点后边
if(cur.child != null){
//将子节点处理为一个链表,返回其尾部节点
Node childLast = dfs(cur.child);
//将node与child相连
cur.next = cur.child;
cur.child.prev = cur;
//将待处理链表的尾结点更新为childLast
last = childLast;
//此时如果next不为null,则将next连接在待处理链表尾结点的后边
if(next != null){
last.next = next;
next.prev = last;
}
//将child置为null
cur.child = null;
}else{//如果不存在子节点,那么更新待处理链表的尾部为当前节点
last = cur;
}
//遍历下一个节点
cur = next;
}
//返回扁平化处理后的链表尾部节点
return last;
}
}