LeetCode Weekly Contest 33解题思路

LeetCode Weekly Contest 33解题思路

详细代码可以fork下Github上leetcode项目,不定期更新。

赛题

本次周赛主要分为以下4道题:

594 Longest Harmonious Subsequence (3分)

Problem:

We define a harmonious array is an array where the difference between its maximum value and its minimum value is exactly 1.

Now, given an integer array, you need to find the length of its longest harmonious subsequence among all its possible subsequences.

Example 1:

Input: [1,3,2,2,5,2,3,7]
Output: 5
Explanation: The longest harmonious subsequence is [3,2,2,2,3].

Note:

  • The length of the input array will not exceed 20,000.

思路:
对数组中的每个数进行计数,遍历所有的key,累加key和key+1的个数,取最大即可,代码如下:

public int findLHS(int[] nums) {
        Map<Integer,Integer> count = new HashMap<>();
        for (int i = 0; i < nums.length; i++){
            count.put(nums[i], count.getOrDefault(nums[i], 0)+1);
        }
        int max = 0;
        for (int key : count.keySet()){
            if (count.containsKey(key+1)){
                max = Math.max(max, count.get(key) + count.get(key+1));
            }
        }
        return max;
    }

593 Valid Square (6分)

Problem:

Given the coordinates of four points in 2D space, return whether the four points could construct a square.

The coordinate (x,y) of a point is represented by an integer array with two integers.

Example:

Input: p1 = [0,0], p2 = [1,1], p3 = [1,0], p4 = [0,1]
Output: True

Note:

  • All the input integers are in the range [-10000, 10000].
  • A valid square has four equal sides with positive length and four equal angles (90-degree angles).
  • Input points have no order.

思路:
利用正方形的基本性质,四条边相等且两条对角线相等。所以简单的方法,利用set,把六条边计算出来,最后剩下两条边就一定是正方形。代码如下:

    public boolean validSquare(int[] p1, int[] p2, int[] p3, int[] p4) {
        Set<Integer> set = new HashSet<Integer>();
        set.add(distance(p1, p2));
        set.add(distance(p1, p3));
        set.add(distance(p1, p4));
        set.add(distance(p2, p3));
        set.add(distance(p2, p4));
        set.add(distance(p3, p4));
        return set.size() == 2 && !set.contains(0);
    }

    private int distance(int[] p1, int[] p2){
        int x1 = p1[0] - p2[0];
        int x2 = p1[1] - p2[1];
        return x1 * x1 + x2 * x2;
    }

这道题有更一般的解法,已知四个点,p1,p2,p3,p4,如下图:
alt text

对于正方形的判断代码如下:

private int distance(int[] p1, int[] p2){
        int x1 = p1[0] - p2[0];
        int x2 = p1[1] - p2[1];
        return x1 * x1 + x2 * x2;
    }

    private boolean check(int[] p1, int[] p2, int[] p3, int[] p4){
        return distance(p1, p2) == distance(p2, p3) && distance(p2, p3) == distance(p3, p4) 
                && distance(p3, p4) == distance(p4, p1) && distance(p1, p3) == distance(p2, p4) && distance(p1,p2) != 0;
    }


    public boolean validSquare(int[] p1, int[] p2, int[] p3, int[] p4) {
        return check(p1, p2, p3, p4) || check(p1, p3, p2, p4) || check(p1, p2, p4, p3);
    }

check的代码很好理解,主要是validSquare中应该如何构造点的顺序,一个简单的做法,求出所有点的nextPermutation(),只要有一种置换序符合正方形的性质就返回true,但实际它有很多重复的计算。

而理解check的三个式子,我们可以把正方形的点分割成两条唯一对角线即可。假设有点{p1,p2,p3,p4},那么我们可以构造非重复的正方形可以有:

对角线:{p1,p3},{p2,p4}
对角线:{p1,p2},{p3,p4}
对角线:{p1,p4},{p2,p3}

也就三种情况,所以只要check以上三种情况即可。

592 Fraction Addition and Subtraction (7分)

Problem:

Given a string representing an expression of fraction addition and subtraction, you need to return the calculation result in string format. The final result should be irreducible fraction. If your final result is an integer, say 2, you need to change it to the format of fraction that has denominator 1. So in this case, 2 should be converted to 2/1.

Example 1:

Input:”-1/2+1/2”
Output: “0/1”

Example 2:

Input:”-1/2+1/2+1/3”
Output: “1/3”

Example 3:

Input:”1/3-1/2”
Output: “-1/6”

Example 4:

Input:”5/3+1/3”
Output: “2/1”

Note:

  • The input string only contains ‘0’ to ‘9’, ‘/’, ‘+’ and ‘-‘. So does the output.
  • Each fraction (input and output) has format ±numerator/denominator. If the first input fraction or the output is positive, then ‘+’ will be omitted.
  • The input only contains valid irreducible fractions, where the numerator and denominator of each fraction will always be in the range [1,10]. If the denominator is 1, it means this fraction is actually an integer in a fraction format defined above.
  • The number of given fractions will be in the range [1,10].
  • The numerator and denominator of the final result are guaranteed to be valid and in the range of 32-bit int.

思路:
字符串的解析,以及求最大公约数,递归各种技术。解法:把所有问题都规约到最基本的两个分数加减问题,所以找寻字符串的第一个符号“+”或者符号“-”,注意优先级顺序,从字符串的右侧找,规约成求左侧的子问题。

假设得到了两个字符串的答案,把它们合并即可。代码如下:

public String fractionAddition(String expression) {
        if (noOpeartor(expression)) {
            return expression;
        }

        String ans = "";
        for (int i = expression.length()-1; i >= 0; i--) {
            if (expression.charAt(i) == '+' || expression.charAt(i) == '-') {
                String left = fractionAddition(expression.substring(0, i));
                String right = fractionAddition(expression.substring(i + 1));
                int fz1 = Integer.valueOf(left.split("/")[0]);
                int fz2 = Integer.valueOf(right.split("/")[0]);
                int fm1 = Integer.valueOf(left.split("/")[1]);
                int fm2 = Integer.valueOf(right.split("/")[1]);

                int time = fm1 * fm2;
                switch (expression.charAt(i)) {
                case '+': {
                    int fz3 = fz1 * fm2 + fz2 * fm1;
                    int fm3 = time;
                    ans = reduce(fz3, fm3);
                }
                    break;
                case '-': {
                    int fz3 = fz1 * fm2 - fz2 * fm1;
                    int fm3 = time;
                    ans = reduce(fz3, fm3);
                }
                    break;
                default:
                    break;
                }

                return ans;
            }
        }
        return ans;
    }

    private String reduce(int fz, int fm){
        if (fz == 0){
            return 0 + "/" + String.valueOf(fm);
        }

        int flag = fz > 0 ? 1 : -1;
        fz = Math.abs(fz);
        int num = fz;

        boolean canReduce = false;
        String ans = String.valueOf(fz*flag) + "/" + String.valueOf(fm);
        for (int i = 2; i <= num; i++){
            if(fz % i == 0 && fm % i == 0){
                fz /= i;
                fm /= i;
                canReduce = true;
                break;
            }
        }
        if (!canReduce) return ans;
        return reduce(fz*flag, fm);
    }


    private boolean noOpeartor(String expression) {
        for (int i = 0; i < expression.length(); i++) {
            if (expression.charAt(i) == '+' || expression.charAt(i) == '-') {
                if (i == 0) continue;
                return false;
            }
        }
        return true;
    }

上述是递归的做法,我们也可以直接进行迭代,不断合并也能得到答案。

思路:
对原来的expression的减法做一些改造,如:

1/3-1/2
改造:
1/3 +-1/2
然后split(" +")
这样就能得到每个分数,进行累加即可

代码如下:

private class Fraction {
        long a, b;

        Fraction(long a, long b) {
            if (b < 0) {
                a = -a;
                b = -b;
            }
            long g = gcd(Math.abs(a), Math.abs(b));
            a /= g;
            b /= g;
            this.a = a;
            this.b = b;
        }

        private long gcd(long a, long b) {
            return b == 0 ? a : gcd(b, a % b);
        }

        private Fraction add(Fraction that) {
            return new Fraction(a * that.b + b * that.a, b * that.b);
        }

        @Override
        public String toString() {
            return a + "/" + b;
        }
    }

    public String fractionAddition(String expression) {
        String[] exps = expression.replace("-", " +-").replace("+", " +").split(" +");
        Fraction zero = new Fraction(0, 1);
        for (int i = 0; i < exps.length; i++){
            if (exps[i].isEmpty()) continue;
            long a = 0;
            if(exps[i].charAt(0) == '+') a = Long.parseLong(exps[i].substring(1).split("/")[0]);
            else a = Long.parseLong(exps[i].split("/")[0]);
            long b = 0;
            if(exps[i].charAt(0) == '+') b = Long.parseLong(exps[i].substring(1).split("/")[1]);
            else b = Long.parseLong(exps[i].split("/")[1]);
            zero = zero.add(new Fraction(a, b));
        }
        return zero.toString();
    }

replace空格的原因在于split中的正则表达式“+”是一个特殊符号,识别它可以转意。所以,我们还可以这样,代码如下:

    public String fractionAddition(String expression) {
        String[] exps = expression.replace("-", "+-").split("\\+");
        Fraction zero = new Fraction(0, 1);
        for (int i = 0; i < exps.length; i++){
            if (exps[i].isEmpty()) continue;
            long a = Long.parseLong(exps[i].split("/")[0]);
            long b = Long.parseLong(exps[i].split("/")[1]);
            zero = zero.add(new Fraction(a, b));
        }
        return zero.toString();
    }

588 Design In-Memory File System (9分)

Problem:

Design an in-memory file system to simulate the following functions:

ls: Given a path in string format. If it is a file path, return a list that only contains this file’s name. If it is a directory path, return the list of file and directory names in this directory. Your output (file and directory names together) should in lexicographic order.

mkdir: Given a directory path that does not exist, you should make a new directory according to the path. If the middle directories in the path don’t exist either, you should create them as well. This function has void return type.

addContentToFile: Given a file path and file content in string format. If the file doesn’t exist, you need to create that file containing given content. If the file already exists, you need to append given content to original content. This function has void return type.

readContentFromFile: Given a file path, return its content in string format.

Example:

Input:
[“FileSystem”,”ls”,”mkdir”,”addContentToFile”,”ls”,”readContentFromFile”]
[[],[“/”],[“/a/b/c”],[“/a/b/c/d”,”hello”],[“/”],[“/a/b/c/d”]]
Output:
[null,[],null,null,[“a”],”hello”]
Explanation:
alt text

Note:

  • You can assume all file or directory paths are absolute paths which begin with / and do not end with / except that the path is just “/”.
  • You can assume that all operations will be passed valid parameters and users will not attempt to retrieve file content or list a directory or file that does not exist.
  • You can assume that all directory names and file names only contain lower-case letters, and same names won’t exist in the same directory.

。。。对我这种才刷了算法题半年的人来说,就这道题,我能做一天,更别说在一个半小时内完成四道题了。题目意思是让我们搭一个小的文件系统,数据结构用字符串代替。

分析:
文件系统,最关键的在于对文件的管理,我们使用文件都知道,它是一个树结构,且file可以分为文件夹和文件两种类型,所以我们可以设计如下数据结构:

    private class File{
            char type;
            Map<String, File> files; 
            StringBuilder content;

            //初始化文件 d表示文件夹,f表示文件
            public File(char type) {
                this.type = type;
                if (type == 'd'){
                    files = new HashMap<>();
                }
                if(type == 'f'){
                    content = new StringBuilder();
                }
            }
        }

对文件系统进行初始化:

//根目录
File root;
public FileSystem(){
    root = new File('d');
}

整个文件系统代码如下:

public class FileSystem {

    private class File {
        char type;
        Map<String, File> files;
        StringBuilder content;
        // 初始化文件 d表示文件夹,f表示文件
        public File(char type) {
            this.type = type;
            if (type == 'd') {
                files = new HashMap<>();
            }
            if (type == 'f') {
                content = new StringBuilder();
            }
        }
    }

    File root;

    public FileSystem() {
        root = new File('d');
    }

    public List<String> ls(String path) {
        String[] names = path.split("/");
        File cur = root;
        for (int i = 0; i < names.length; i++){
            String name = names[i];
            if(name.isEmpty()) continue;
            cur = cur.files.get(name);
        }

        List<String> dirs = new ArrayList<>();
        if (cur.type == 'f'){
            return Collections.singletonList(names[names.length-1]);
        }
        else{
            for (String files : cur.files.keySet()){
                dirs.add(files);
            }
            Collections.sort(dirs);
            return dirs;
        }
    }

    public void mkdir(String path) {
        String[] names = path.split("/");
        File cur = root;
        for (int i = 0; i < names.length; i++){
            String name = names[i];
            if (name.isEmpty()) continue;
            if (!cur.files.containsKey(name)){ //当前文件夹不存在
                cur.files.put(name, new File('d'));
            }
            cur = cur.files.get(name);
        }
    }

    public void addContentToFile(String filePath, String content) {
        String[] names = filePath.split("/");
        File cur = root;
        for (int i = 0; i < names.length; i++){
            String name = names[i];
            if (name.isEmpty()) continue;
            if (i == names.length - 1 && !cur.files.containsKey(name)){ //这些文件已经存在了
                cur.files.put(name, new File('f'));
            }
            cur = cur.files.get(name);
        }
        cur.content.append(content);
    }

    public String readContentFromFile(String filePath) {
        String[] names = filePath.split("/");
        File cur = root;
        for (int i = 0; i < names.length; i++){
            String name = names[i];
            if(name.isEmpty()) continue;
            if (i == names.length - 1 && !cur.files.containsKey(name)){ //这些文件已经存在了
                cur.files.put(name, new File('f'));
            }
            cur = cur.files.get(name);
        }
        return cur.content.toString();
    }

    public static void main(String[] args) {
        FileSystem fileSystem = new FileSystem();
        fileSystem.mkdir("/goowmfn");
        System.out.println(fileSystem.ls("/"));
        System.out.println(fileSystem.ls("/goowmfn"));
        fileSystem.mkdir("/z");
        System.out.println(fileSystem.ls("/"));
        System.out.println(fileSystem.ls("/"));
        fileSystem.addContentToFile("/goowmfn/c", "shetopcy");
        System.out.println(fileSystem.ls("/goowmfn/c"));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值