1823. 找出游戏的获胜者 / 剑指 Offer II 028. 展平多级双向链表

本文介绍了两种算法问题的解决方案,一是利用约瑟夫环原理找出游戏的获胜者,二是通过深度优先搜索将多级双向链表展平。在约瑟夫环问题中,通过循环更新下标找到最后的获胜者;在链表扁平化中,递归遍历并连接子节点,形成单链表。这两个问题都涉及到循环和递归的巧妙应用。
摘要由CSDN通过智能技术生成

1823. 找出游戏的获胜者【中等题】【每日一题】

思路:【约瑟夫环】

本题使用约瑟夫环进行求解,下面写一下我对约瑟夫环解法的一点理解。

  1. 根据题意,小伙伴们的编号从1开始,到n结束。现在假设这是一个数组,下标从0开始,则数组值比对应下标多1
  2. 根据规则,每次游戏数组都会根据规损失一个元素,那么游戏结束时数组长度一定为1,即只剩下一个游戏获胜者,它的数组下标必然是0
  3. 游戏获胜者在每一轮游戏中都不可能被淘汰,否则怎么取得最后的胜利,于是我们可以根据一定的规则,倒着推导出获胜者在每一轮游戏开始时所在的数组下标,直到推导出第一轮游戏开始时的数组下标,得出获胜者在第一轮游戏开始时的数组下标之后,由于下标与值之间的对应关系,我们很容易就能得出最后的获胜者编号等于其在第一轮游戏开始时的编号+1
  4. 那么如何确定倒推规则?
  5. 我们已经确定最后一轮游戏开始时,数组长度为1,那么倒数第2轮游戏开始时,数组长度肯定为2,倒数第3轮数组长度为3,…。
  6. 游戏的规则是,从某个i位置开始,删除第i+k-1位,然后从i+k开始,删除第 i+k+k-1位,…,可以认为,从第i个位置开始,第i+k位一定不会被删除(因为删的是第i+k-1位),那么当我知道了这一轮游戏胜利者的下标之后,在下一轮游戏中,对胜利者这样操作必不可能导致胜利者被删除。
  7. 假设现在是倒数第i轮游戏(i2开始,到n结束,倒数第n轮即游戏第1轮)。
  8. 设胜利者在第i轮下标为i,那么在下一轮(即第i-1轮)的下标必然为 (i+k) % i (由步骤5可知,倒数第i轮游戏时数组长度为i,将下标对i取余是为了防止数组越界,因为这是一个环,当走到数组最后一位时,会跳回数组第1位所在位置)。
  9. 因此我们设倒数第1轮下标 为 ans = 0,倒序遍历每一轮,即 i2n遍历,每一次倒推都会更新当前游戏胜利者所在的下标为 ans = (ans + k) % i
  10. 循环结束时的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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值