tx_orderByFrequency_1~10

625. 最小因式分解

给定一个正整数 a,找出最小的正整数 b 使得 b 的所有数位相乘恰好等于 a

如果不存在这样的结果或者结果不是 32 位有符号整数,返回 0。
思路:

  1. dfs 求得所有的解集合,取最小的值。dfs注意剪枝:232减去,因为232>223。复杂度高。
  2. 贪心算法,从个位往前添加因子,能除9就先除9不除3*3;
class Solution {
    public int smallestFactorization(int a) {
        if(a<=9){
            return a;
        }
        List<Integer> list = new ArrayList();
        for(int i=9;i>1;i--){
            if(a==1){
                break;
            }
            while(a%i==0){
                a = a/i;
                list.add(i);
            }
        }
        Collections.reverse(list);
        if(a!=1){
            return 0; 
        }else{
            int base = 1;
            int result =0;
            if(list.size()>=10){
                return 0;
            }
            for(int i=list.size()-1;i>=0;i--){
                result += list.get(i)*base;
                base = base*10;
            }
            return result;
        }
    }
}

1024. 视频拼接

难度中等38 收藏 分享 切换为英文 关注 反馈

你将会获得一系列视频片段,这些片段来自于一项持续时长为 T 秒的体育赛事。这些片段可能有所重叠,也可能长度不一。

视频片段 clips[i] 都用区间进行表示:开始于 clips[i][0] 并于 clips[i][1] 结束。我们甚至可以对这些片段自由地再剪辑,例如片段 [0, 7] 可以剪切成 [0, 1] + [1, 3] + [3, 7] 三部分。

我们需要将这些片段进行再剪辑,并将剪辑后的内容拼接成覆盖整个运动过程的片段([0, T])。返回所需片段的最小数目,如果无法完成该任务,则返回 -1

示例 1:

输入:clips = [[0,2],[4,6],[8,10],[1,9],[1,5],[5,9]], T = 10
输出:3
解释:
我们选中 [0,2], [8,10], [1,9] 这三个片段。
然后,按下面的方案重制比赛片段:
将 [1,9] 再剪辑为 [1,2] + [2,8] + [8,9] 。
现在我们手上有 [0,2] + [2,8] + [8,10],而这些涵盖了整场比赛 [0, 10]。

解法: 贪心算法。能选择9作为最后,不选择3,4,5,6,7,8;

class Solution {
    public int videoStitching(int[][] clips, int T) {
        
        int count = 0;
        int max  = 0;
        for(int i=0;i<=T;){
            for(int j=0;j<clips.length;j++){
// 在所有片段中选择满足i且右边界最大的。直接跳到右边界,接着循环。
                if(clips[j][0]<=i){
                    max = Math.max(max,clips[j][1]); 
                }
            }
// 如果最右也比i小,说明该点缺失。
            if(max<=i){
                return -1;
            }
            i = max;
            count++;
            if(max==T){
                break;
            }
        }
        return count;
    }   
}

588. 设计内存文件系统

设计一个内存文件系统,模拟以下功能:

ls: 以字符串的格式输入一个路径。如果它是一个文件的路径,那么函数返回一个列表,仅包含这个文件的名字。如果它是一个文件夹的的路径,那么返回该 文件夹内 的所有文件和子文件夹的名字。你的返回结果(包括文件和子文件夹)应该按字典序排列。

mkdir:输入一个当前不存在的 文件夹路径 ,你需要根据路径名创建一个新的文件夹。如果有上层文件夹路径不存在,那么你也应该将它们全部创建。这个函数的返回类型为 void 。

addContentToFile: 输入字符串形式的 文件路径文件内容 。如果文件不存在,你需要创建包含给定文件内容的文件。如果文件已经存在,那么你需要将给定的文件内容 追加 在原本内容的后面。这个函数的返回类型为 void 。

readContentFromFile: 输入 文件路径 ,以字符串形式返回该文件的 内容

注意:以两个hashMap去表示文件系统;
Map<String, String> files:文件; 名字:文件
Map<String, Dir> dirs: 文件夹; 名字:文件夹;

public class FileSystem {
    class Dir {
        HashMap < String, Dir > dirs = new HashMap < > ();
        HashMap < String, String > files = new HashMap < > ();
    }
    Dir root;
    public FileSystem() {
        root = new Dir();
    }
    public List < String > ls(String path) {
        Dir t = root;
        List < String > files = new ArrayList < > ();
        if (!path.equals("/")) {
            String[] d = path.split("/");
            for (int i = 1; i < d.length - 1; i++) {
                t = t.dirs.get(d[i]);
            }
            if (t.files.containsKey(d[d.length - 1])) {
                files.add(d[d.length - 1]);
                return files;
            } else {
                t = t.dirs.get(d[d.length - 1]);
            }
        }
        files.addAll(new ArrayList < > (t.dirs.keySet()));
        files.addAll(new ArrayList < > (t.files.keySet()));
        Collections.sort(files);
        return files;
    }

    public void mkdir(String path) {
        Dir t = root;
        String[] d = path.split("/");
        for (int i = 1; i < d.length; i++) {
            if (!t.dirs.containsKey(d[i]))
                t.dirs.put(d[i], new Dir());
            t = t.dirs.get(d[i]);
        }
    }

    public void addContentToFile(String filePath, String content) {
        Dir t = root;
        String[] d = filePath.split("/");
        for (int i = 1; i < d.length - 1; i++) {
            t = t.dirs.get(d[i]);
        }
        t.files.put(d[d.length - 1], t.files.getOrDefault(d[d.length - 1], "") + content);
    }

    public String readContentFromFile(String filePath) {
        Dir t = root;
        String[] d = filePath.split("/");
        for (int i = 1; i < d.length - 1; i++) {
            t = t.dirs.get(d[i]);
        }
        return t.files.get(d[d.length - 1]);
    }
}

/**
 * Your FileSystem object will be instantiated and called as such:
 * FileSystem obj = new FileSystem();
 * List<String> param_1 = obj.ls(path);
 * obj.mkdir(path);
 * obj.addContentToFile(filePath,content);
 * String param_4 = obj.readContentFromFile(filePath);
 */

469. 凸多边形

给定一个按顺序连接的多边形的顶点,判断该多边形是否为凸多边形。(凸多边形的定义)

注:

  1. 顶点个数至少为 3 个且不超过 10,000。
  2. 坐标范围为 -10,000 到 10,000。
  3. 你可以假定给定的点形成的多边形均为简单多边形(简单多边形的定义)。换句话说,保证每个顶点处恰好是两条边的汇合点,并且这些边 **互不相交 **。

示例 1:

<pre style="box-sizing: border-box; font-size: 13px; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; margin-top: 0px; margin-bottom: 1em; overflow: auto; background: rgb(247, 249, 250); padding: 10px 15px; color: rgb(38, 50, 56); line-height: 1.6; border-radius: 3px; white-space: pre-wrap;">[[0,0],[0,1],[1,1],[1,0]]

输出: True

</pre>

示例 2:

<pre style="box-sizing: border-box; font-size: 13px; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; margin-top: 0px; margin-bottom: 1em; overflow: auto; background: rgb(247, 249, 250); padding: 10px 15px; color: rgb(38, 50, 56); line-height: 1.6; border-radius: 3px; white-space: pre-wrap;">[[0,0],[0,10],[10,10],[10,0],[5,5]]

输出: False

</pre>

解法: 向量叉积全为正/全为负。 a,b 均为向量。
* 叉积小于0,那么a在b的逆时针方向,
* 叉积大于0,a在b的顺时针方向
* 叉积等于0,a,b方向相同

import java.util.List;

class Solution {
   public boolean isConvex(List<List<Integer>> points) {
       boolean positive = false;
       boolean negative = false;
        int n = points.size();
       for(int i=0;i<points.size();i++){
           if(crossMult(points.get(i),points.get((i+1)%n),points.get((i+2)%n))>0){
               positive = true;
           }else if(crossMult(points.get(i),points.get((i+1)%n),points.get((i+2)%n))<0){
               negative = true;
           }
            if(positive&&negative){
                return false;
            }
      }
        return true;
   }

    public int crossMult(List<Integer> A, List<Integer> B , List<Integer> C){
       /**
        * 使用向量叉积,
        * a = (x1,y1)
        * b = (x2,y2)
        * aXb = x1y2-x2y1
        * 叉积小于0,那么a在b的逆时针方向,
        * 叉积大于0,a在b的顺时针方向
        * 叉积等于0,a,b方向相同
        */
        int xA = A.get(0) - B.get(0);
        int xC = C.get(0) - B.get(0);
        int yA = A.get(1) - B.get(1);
        int yC = C.get(1) - B.get(1);
        return xA*yC - xC*yA;
    }
}

887. 鸡蛋掉落

你将获得 K 个鸡蛋,并可以使用一栋从 1N 共有 N 层楼的建筑。

每个蛋的功能都是一样的,如果一个蛋碎了,你就不能再把它掉下去。

你知道存在楼层 F ,满足 0 <= F <= N 任何从高于 F 的楼层落下的鸡蛋都会碎,从 F 楼层或比它低的楼层落下的鸡蛋都不会破。

每次移动,你可以取一个鸡蛋(如果你有完整的鸡蛋)并把它从任一楼层 X 扔下(满足 1 <= X <= N)。

你的目标是确切地知道 F 的值是多少。

无论 F 的初始值如何,你确定 F 的值的最小移动次数是多少?

思路: 解题需要求得,最坏情况下的最小次数N;
ex: N = 7; K = 1;为了得到准确楼层,只能从1层到7层依次遍历。此时最坏情况就是第7层刚好碎,准确楼层为7。次数为7。

以某i层扔下为例子,如果碎了,dp[K-1,i-1];如果没碎,dp[K,i+1~N] = dp[K,N-i];
最坏情况就是取更大的,max(dp[K-1,i-1],dp[K,N-i]);
最小次数,就是遍历i,取得最小的次数。此时得到解。

for(int i=1;i<=N;i++){
  res = min(res,max((dp[K-1,i-1],dp[K,N-i]));
}

可以利用hashMap存储已经记录下的值,作为优化。
但这种优化不够,由于dp[K-1,i-1] 随着i递增,dp[K,N-i]递减。▽。取其最小的最大值,就是取其凹点。因此,用二分法改进。

class Solution {
    HashMap<Integer,Integer> map = new HashMap();
    public int superEggDrop(int K, int N) {
        return dp(K,N);
    }
    public int dp(int K, int N){
        if(K==1){
            return N;
        }
        if(N == 0){
            return 0;
        }
        if(map.containsKey(100*N+K)){
            return map.get(100*N+K);
        }
        int res = Integer.MAX_VALUE;
        // 二分: dp(K,i-1)随着i递增,dp(K-1,N-i) 随着i递减
        // 二者相加,得到类似▼形状,二者相等时,Math.min(res,Math.max(dp(K,i-1),dp(K-1,N-i))+1)值最小。
        // 因为不相等时,总是取更大的(更上面的),即取凹点。
        int low = 1;
        int high = N;
        while(low<=high){
            int mid = (low+high)/2;
            int left = dp(K-1,mid-1);
            int right = dp(K,N-mid);
            if(left<right){
                low = mid + 1;
                res =  Math.min(res,right + 1);
            }else if(left >= right){
                high = mid -1;
                res =  Math.min(res,left + 1);
            }
        }
        map.put(100*N+K,res);
        return res;
    }
}

1153. 字符串转化

给出两个长度相同的字符串,分别是 str1str2。请你帮忙判断字符串 str1 能不能在 零次多次 转化后变成字符串 str2

每一次转化时,将会一次性将 str1 中出现的 所有 相同字母变成其他 任何 小写英文字母(见示例)。

只有在字符串 str1 能够通过上述方式顺利转化为字符串 str2 时才能返回 True,否则返回 False。​​
示例 1:

输入:str1 = "aabcc", str2 = "ccdee"
输出:true
解释:将 'c' 变成 'e',然后把 'b' 变成 'd',接着再把 'a' 变成 'c'。注意,转化的顺序也很重要。
示例 2:

输入:str1 = "leetcode", str2 = "codeleet"
输出:false
解释:我们没有办法能够把 str1 转化为 str2。

思路: 示例一的规则;a->c b->d c->e,如果aabbcc a -> ccdee f则返回false;
利用HashMap记录转换规则(顶多26个字母),当出现重复字母,判断对应的str2字符是否与规则中的相同。如果str2有26个字母,将形成死循环。

class Solution {
    public boolean canConvert(String str1, String str2) {
        if(str1.length()!=str2.length()){
            return false;
        }
        if(str1.equals(str2))
            return true;
        HashMap<Character,Character> map = new HashMap();
        Set<Character> set=new HashSet<>();
        for(int i=0;i<str1.length();i++){
            if(!map.containsKey(str1.charAt(i))){
                map.put(str1.charAt(i),str2.charAt(i));
            }else{
                if(str2.charAt(i)!=map.get(str1.charAt(i))){
                    return false;
                }
            }
            set.add(str2.charAt(i));
        }
        return set.size()<26;
    }
}

632. 最小区间

你有 k 个升序排列的整数数组。找到一个最小区间,使得 k 个列表中的每个列表至少有一个数包含在其中。

我们定义如果 b-a < d-c 或者在 b-a == d-ca < c,则区间 [a,b] 比 [c,d] 小。

示例 1:

<pre style="box-sizing: border-box; font-size: 13px; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; margin-top: 0px; margin-bottom: 1em; overflow: auto; background: rgb(247, 249, 250); padding: 10px 15px; color: rgb(38, 50, 56); line-height: 1.6; border-radius: 3px; white-space: pre-wrap;">输入:[[4,10,15,24,26], [0,9,12,20], [5,18,22,30]]
输出: [20,24]
解释:
列表 1:[4, 10, 15, 24, 26],24 在区间 [20,24] 中。
列表 2:[0, 9, 12, 20],20 在区间 [20,24] 中。
列表 3:[5, 18, 22, 30],22 在区间 [20,24] 中。</pre>

原始思路:
所有数字形成的集合T[0,4,5,9,12,18....],遍历整个数字集合,找到该数字的其他列表中大于该数字的最小数字集Y,其min为该数字,其max为max{数字集}。
进一步思考讨论:
如果T中该数字的下一个数字,就是该数字列表中的下一个数字,则Y不变。数字集变成{x_next,Y}
如果T中该数字的下一个数字,不是其原先列表的下一个数字,那么必为Y中的一个数字。
如y2,则y3至yn不变,Y集合变成{x_next,y3至yn}。数字集为{x_next,y2,y3~至} -> {x_next,Y}。
因此,通过K路指针法,维护K个指针指向数字集中的数字。int[] next = new int[nums.length];如果该指针指向的数字超过了该列表的长度,则退出循环。
通过最小堆,维护指向最小数所在列表的指针。
code如下:

class Solution {
    public int[] smallestRange(List<List<Integer>> nums) {
        int minx = 0, miny = Integer.MAX_VALUE, max = Integer.MIN_VALUE;
        int[] next = new int[nums.size()];
        boolean flag = true;
        // 最小堆维护的是当前的最小值,在哪个列表中。注意该堆的比较器。
        PriorityQueue<Integer> min_queue = new PriorityQueue<Integer>((i,j) -> nums.get(i).get(next[i])-nums.get(j).get(next[j]));
        // 初始化堆
        for(int i=0;i<nums.size();i++){
            min_queue.offer(i);
            max = Math.max(max,nums.get(i).get(0));
        }
        // 从堆中取得当前最小元素所在的列表。根据next[i],取得其所在位置,多路指针法,类似于丑数;
        // 经前面分期,第二个列表就是min_i在其列表的后一位及其之前其他数字组成的区间。最小值由最小堆(决定min_i)及next[min_i]维护,
        // 最大值由max = Math.max(max, nums.get(min_i).get(next[min_i]))维护。
        for(int i=0;i<nums.size()&&flag;i++){
            for(int j=0;j<nums.get(i).size()&&flag;j++){
                int min_i = min_queue.poll();
                // 选择更小的区间
                if(miny-minx> max - nums.get(min_i).get(next[min_i])){
                    miny = max;
                    minx = nums.get(min_i).get(next[min_i]);
                }
                // 由于next改变,min_i加入堆后,堆结构也会改变
                next[min_i]++;
                if(next[min_i]>=nums.get(min_i).size()){
                    flag = false;
                    break;
                }
                min_queue.offer(min_i);
                max = Math.max(max, nums.get(min_i).get(next[min_i]));
            }
        }
        return new int[]{minx,miny};
    }
}

330. 按要求补齐数组

给定一个已排序的正整数数组 nums,和一个正整数 n 。[1, n] 区间内选取任意个数字补充到 *nums *中,使得 [1, n] 区间内的任何数字都可以用 *nums *中某几个数字的和来表示。请输出满足上述要求的最少需要补充的数字个数。

输入: nums = [1,3], n = 6
输出: 1
解释:
根据 nums 里现有的组合 [1], [3], [1,3],可以得出 1, 3, 4。
现在如果我们将 2 添加到 nums 中, 组合变为: [1], [2], [3], [1,3], [2,3], [1,2,3]。
其和可以表示数字 1, 2, 3, 4, 5, 6,能够覆盖 [1, 6] 区间里所有的数。
所以我们最少需要添加一个数字。

思路: 确认其所覆盖的区间,初始化区间为[1,1);
当区间right > n 时,则完成。初始化nums的索引index=0;
for(int i=1;i<=n;),每次将i更新为区间的right,避免重复计算。
如果nums[index]<i,由于现在的区间是[1,right]即[1,i]。通过将区间内的所有数+nums[index]得到新的区间 [1,right+nums[index]];更新i为新值,并且将index右移一位。
如果nums[index]>i,则需要新增i在nums中,作为新加数,此时right更新为2i;而index不变。
如果index超过索引长度,需要新增i在nums作为新加数,同样更新right = 2
i;

代码:

class Solution {
    public int minPatches(int[] nums, int n) {
        long right = 1;
        int index = 0;
        int count = 0;
        for(long i=1;i<=n;){
            if(index<nums.length&&nums[index]>i){
                count ++;
                right = 2*i; //[1,2i)
            }
            else if(index<nums.length&&nums[index]<=i){
                // 不一定要添加;
                right += nums[index];
                index ++;  // [1,i+nums[index]];
            }else{
                right = 2*i;
                count++;
            }
            i = right;
        }
        return count;
    }
}

818. 赛车

你的赛车起始停留在位置 0,速度为 +1,正行驶在一个无限长的数轴上。(车也可以向负数方向行驶。)

你的车会根据一系列由 A(加速)和 R(倒车)组成的指令进行自动驾驶 。

当车得到指令 "A" 时, 将会做出以下操作: position += speed, speed *= 2。

当车得到指令 "R" 时, 将会做出以下操作:如果当前速度是正数,则将车速调整为 speed = -1 ;否则将车速调整为 speed = 1。 (当前所处位置不变。)

例如,当得到一系列指令 "AAR" 后, 你的车将会走过位置 0->1->3->3,并且速度变化为 1->2->4->-1。

现在给定一个目标位置,请给出能够到达目标位置的最短指令列表的长度。

示例 1:
输入:
target = 3
输出: 2
解释:
最短指令列表为 "AA"
位置变化为 0->1->3

示例 2:
输入:
target = 6
输出: 5
解释:
最短指令列表为 "AAARA"
位置变化为 0->1->3->7->7->6

解题思路: Math.pow(2,i) = 1<<i;
假设: k-1次A到k次A之间包含了target;

则有两种前进方式:
①:
先来(k-1)个A,如果此时刚好为target,return;
如果不是,反向操作R后。后退j(j<(k-1),R反向掉头。此时target= target - 1<<(k-1)-1<<j;
具体后退多少个j。取 所有递归里最小的。
②:
来(k)个A,总距离为 2^k-1即 (1<<k)-1
,R,target = (1<<(k))-1-target;递归求解;

取 1和2中更小的那个。

class Solution {
    public int racecar(int target) {
        int res = dp(target);
        return res;
    }
    
    public int dp(int target){
        if(target <=0){
            return 0;
        }
        if(target == 1){
            return 1;
        }
        if(target == 2){
            return 4;
        }
        int n = 0;
        for(int i=0;i<target;i++){
            if((1<<i)>target){
                n = i;
                break;
            }
        }
        if(target+1==(1<<n)){
            return n;
        }
        int min = Integer.MAX_VALUE;
        for(int j=0;j<n-1;++j){
            min = Math.min(min,dp((int) target-(1<<(n-1))+(1<<j))+n-1+j+2);
        }
        return Math.min(min,dp((1<<(n))-1-target)+n+1);
    }
}

注意: 1. 1<<i = 2^i; 2. 如果往回倒,具体倒多少个A,要遍历取最小的。 3. 利用数组记录递归值,减少复杂度

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

推荐阅读更多精彩内容

  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,410评论 0 2
  • 个人学习批处理的初衷来源于实际工作;在某个迭代版本有个BS(安卓手游模拟器)大需求,从而在测试过程中就重复涉及到...
    Luckykailiu阅读 4,779评论 0 11
  • 排序算法几种分类方式: 1,稳定排序和不稳定排序 如果a==b, 当排序之前a在b的前面,排序后,a仍然在b...
    fly_ever阅读 429评论 0 0
  • <center>#104 Maximum Depth of Binary Tree</center> link D...
    铛铛铛clark阅读 1,609评论 0 0
  • 连着写了几天的东西,话痨又浅薄的我终于无话可说了,急于表达的东西都已经用不同的方式或口述或笔述,清楚又冗长的...
    慕尔是仝一阅读 158评论 0 1