LeetCode 725. 分隔链表 / 326. 3的幂 / 剑指 Offer 62. 圆圈中最后剩下的数字(约瑟夫环问题)

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 725. 分隔链表 / 326. 3的幂 / 剑指 Offer 62. 圆圈中最后剩下的数字(约瑟夫环问题)相关的知识,希望对你有一定的参考价值。

725. 分隔链表

2021.9.22 每日一题

题目描述

给定一个头结点为 root 的链表, 编写一个函数以将链表分隔为 k 个连续的部分。

每部分的长度应该尽可能的相等: 任意两部分的长度差距不能超过 1,也就是说可能有些部分为 null。

这k个部分应该按照在链表中出现的顺序进行输出,并且排在前面的部分的长度应该大于或等于后面的长度。

返回一个符合上述规则的链表的列表。

举例: 1->2->3->4, k = 5 // 5 结果 [ [1], [2], [3], [4], null ]

示例 1:

输入:
root = [1, 2, 3], k = 5
输出: [[1],[2],[3],[],[]]
解释:
输入输出各部分都应该是链表,而不是数组。
例如, 输入的结点 root 的 val= 1, root.next.val = 2, \\root.next.next.val = 3, 且 root.next.next.next = null。
第一个输出 output[0] 是 output[0].val = 1, output[0].next = null。
最后一个元素 output[4] 为 null, 它代表了最后一个部分为空链表。

示例 2:

输入:
root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], k = 3
输出: [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
解释:
输入被分成了几个连续的部分,并且每部分的长度相差不超过1.前面部分的长度大于等于后面部分的长度。

提示:

root 的长度范围: [0, 1000].
输入的每个节点的大小范围:[0, 999].
k 的取值范围: [1, 50].

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/split-linked-list-in-parts
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

不算简单的模拟
思路很简单,但是代码实现起来还是比较困难的

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode[] splitListToParts(ListNode head, int k) {
        ListNode node = head;
        int l = 0;

        while(node != null){
            l++;
            node = node.next;
        }

        int t = l / k;
        int remain = l % k;
    
        node = head;
        ListNode[] res = new ListNode[k];
        int tt = t;
        
        int idx = 0;
        while(idx < k){
            res[idx] = node;

            while(tt-- > 0 && node != null){
                node = node.next;
            }

            if(remain > 0){
                node = node.next;
                remain--;
            }
            tt = t;
            idx++;
        }
        idx = 1;
        node = head;
        while(idx < k && node != null){
            if(node.next == res[idx]){
                node.next = null;
                node = res[idx];
                idx++;
            }
            else
                node = node.next;
        }
        return res;
    }
}

我是不知道怎么写成一次遍历才分开写的
学一下一次怎么写

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode[] splitListToParts(ListNode head, int k) {
        int l = 0;
        ListNode node = head;
        while(node != null){
            node = node.next;
            l++;
        }
        int t = l / k;
        int remain = l % k;
        node = head;
        ListNode[] res = new ListNode[k];
        int idx = 0;
        while(idx < k && node != null){
            res[idx] = node;
            //因为要到达的是下一个起始节点的前一个节点
            int step = t + (remain-- > 0 ? 1 : 0);
            while(step-- > 1){
                node = node.next;
            }
            
            ListNode cur = node.next;
            node.next = null;
            node = cur;
            idx++;
        }
        return res;
    }
}

326. 3的幂

2021.9.23 每日一题

题目描述

给定一个整数,写一个函数来判断它是否是 3 的幂次方。如果是,返回 true ;否则,返回 false 。

整数 n 是 3 的幂次方需满足:存在整数 x 使得 n == 3x

示例 1:

输入:n = 27
输出:true

示例 2:

输入:n = 0
输出:false

示例 3:

输入:n = 9
输出:true

示例 4:

输入:n = 45
输出:false

提示:

-2^31 <= n <= 2^31 - 1

进阶:

你能不使用循环或者递归来完成本题吗?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/power-of-three
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

首先,最常规的思路,就是一直除以3,如果余数不为0,那么就不是3的次幂;如果最后剩下1了,那么就是

class Solution {
    public boolean isPowerOfThree(int n) {
        while(n != 0 && n % 3 == 0){
            n /= 3;
        }
        return n == 1;
    }
}

然后打表,

class Solution {
    static Set<Integer> set = new HashSet<>();
    static{
        int i = 1;
        set.add(i);
        while(i <= Integer.MAX_VALUE / 3){
            i *= 3;
            set.add(i);
        }
    }
    public boolean isPowerOfThree(int n) {
        return n > 0 && set.contains(n);
    }
}

然后注意,我刚开始写的是这样打表的,然后报的是内存溢出的错误,想了一下,肯定是i*3超出范围了

class Solution {
    static Set<Integer> set = new HashSet<>();
    static{
        int i = 1;
        set.add(i);
        //错误代码
        while(i * 3 <= Integer.MAX_VALUE){
            i *= 3;
            set.add(i);
        }
    }
    public boolean isPowerOfThree(int n) {
        return n > 0 && set.contains(n);
    }
}

因为3是质数,所以3的幂,因数也都是3的幂,所以这里直接取Integer范围里面最大的3的幂,然后对n取余

public class Solution {
    //知道了 n 的限制,我们现在可以推断出 n 的最大值,也就是 3 的幂,是 1162261467。
    //由于3是质数,所以最大值的因数只能是3的次幂,所以如果最大值除以n余数为0,得到的就是3的幂
    public boolean isPowerOfThree(int n) {
        return n > 0 && 1162261467 % n == 0;
    }
}

下一个方法,用toString()方法,将n转换成3进制数
然后判断这个3进制数,是否是10000…这种形式,用正则表达式判断,这里正则表达式又忘了,不过这个东西一看就会了

public class Solution {
    public boolean isPowerOfThree(int n) {
        //第一个方法用于将数n转换为以3为底的基数,并且以字符串的形式返回;
        //第二个方法用于检查字符串中是否存在特定的字符串,
        //这个正则表达式用来检查字符串是否以1开头,后跟0 或 多个 0 并且不包含任何其他值 $
        return Integer.toString(n, 3).matches("^10*$");
    }
}

下一个方法,也是数学
因为n = 3^x,那么求出x,判断x是不是一个整数,也就是求log以3位底,n的对数
然后因为java中有的是以10为底的对数,所以用公式转换一下

public class Solution {
    //这个方法是纯数学计算,就是i = log3(n);java中有一个计算以10为低的对数,所以用公式变成:
    //log10(n)/log10(3),判断是否结果是一个整数 
    public boolean isPowerOfThree(int n) {
        return (Math.log10(n) / Math.log10(3)) % 1 == 0;
    }
}

剑指 Offer 62. 圆圈中最后剩下的数字

题目描述

0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。

示例 1:

输入: n = 5, m = 3
输出: 3

示例 2:

输入: n = 10, m = 17
输出: 2

限制:

1 <= n <= 10^5
1 <= m <= 10^6

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

今天笔试遇到了约瑟夫环问题,来复习一下
首先很直观的模拟,很慢:

class Solution {
    public int lastRemaining(int n, int m) {
        List<Integer> list = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            list.add(i);
        }
        int idx = 0;
        while (n > 1) {
            idx = (idx + m - 1) % n;
            list.remove(idx);
            n--;
        }
        return list.get(0);
    }
}

然后想规律
假设这个问题的答案是f(n, m)
那么第一次删除的位置就是m % n这个位置,然后第m%n+1这个位置就变成了f(n -1, m)这个问题的初始位置,也就是零位置
删除一个以后,问题变成了f(n - 1, m),假设这个问题的解是x
那么说明f(n, m)这个问题的答案应该是在m % n位置基础上,再移动x个位置
递归可以写,动态规划节省空间

class Solution {
    public int lastRemaining(int n, int m) {
        int pre = 0;
        for(int i = 2; i <= n; i++){
            int f = (m + pre) % i;
            pre = f;
        }
        return pre;
    }
}

以上是关于LeetCode 725. 分隔链表 / 326. 3的幂 / 剑指 Offer 62. 圆圈中最后剩下的数字(约瑟夫环问题)的主要内容,如果未能解决你的问题,请参考以下文章

力扣:725 分隔链表

725 分隔链表

725. 分隔链表

725. 分隔链表

725. 分隔链表

[M链表] lc725. 分隔链表(模拟+代码优化+代码实现)