Jerry's Blog

Recording what I learned everyday

View on GitHub


19 October 2019

LeetCode(42) -- 937, 938, 941, 942, 944

by Jerry Zhang

P937. Reorder Data in Log Files (Easy)

有一个字符串数组,表示一些日志。每条日志都是空格分隔的单词。每一个单词是这条log的ID,后面的单词,要么都是小写字母,要么都是数字。

重新排序这些日志,使得字母日志全排在数字日志前面,并且按字母表顺序排序, 数字日志顺序保持不变。

我的思路

我自己新写了一个类,实现compareTo方法。然后用Collection的sort方法排序。这个方法比较繁琐,先要转换成List,再转换回数组。

我的代码

public class E_937_ReorderDatainLogFiles {
    public String[] reorderLogFiles(String[] logs) {
        List<Log> logList = new ArrayList<>();
        for (String log : logs) {
            logList.add(new Log(log));
        }
        Collections.sort(logList);
        String[] ans = new String[logs.length];
        for (int i = 0; i < ans.length; i++) {
            ans[i] = logList.get(i).getLog();
        }
        return ans;
    }

    class Log implements Comparable {
        String log;
        boolean isLetterLog;

        public Log(String log) {
            this.log = log;
            String[] s = log.split(" ");
            if (Character.isDigit(s[1].charAt(0))) {
                this.isLetterLog = false;
            } else {
                this.isLetterLog = true;
            }
        }

        @Override
        public int compareTo(Object o) {
            if (this == o) return 0;
            Log anotherLog = (Log) o;
            if (isLetterLog && !anotherLog.isLetterLog) {
                return Integer.MIN_VALUE;
            } else if (!isLetterLog && anotherLog.isLetterLog) {
                return Integer.MAX_VALUE;
            } else if (!isLetterLog) {
                return 0;
            } else {
                String thisLogContent = log.substring(log.indexOf(" ") + 1);
                String anotherLogContent = anotherLog.log.substring(anotherLog.log.indexOf(" ") + 1);
                int compare = thisLogContent.compareTo(anotherLogContent);
                if (compare == 0) {
                    return log.compareTo(anotherLog.log);
                } else {
                    return compare;
                }
            }
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Log log1 = (Log) o;
            return log.equals(log1.log) && isLetterLog == log1.isLetterLog;
        }

        public String getLog() {
            return log;
        }

        public void setLog(String log) {
            this.log = log;
        }

        public boolean isLetterLog() {
            return isLetterLog;
        }

        public void setLetterLog(boolean letterLog) {
            isLetterLog = letterLog;
        }
    }
}

70%

最优解

class Solution {
    public String[] reorderLogFiles(String[] logs) {

    int size = logs.length;
    String[] reorderedLogs = new String[size];
    int digitNextInsertIndex = size - 1;
    int alphaLastInsertIndex = -1;
    for (int i = size - 1; i >= 0; i--) {
      String log = logs[i];
      char firstNonIdentifierChar = log.charAt(log.indexOf(' ') + 1);
      if (firstNonIdentifierChar >= '0' && firstNonIdentifierChar <= '9') {
        reorderedLogs[digitNextInsertIndex--] = log;
      } else {
        for (int j = 0; j < size; j++) {
          if (reorderedLogs[j] == null) {
            reorderedLogs[j] = log;
            alphaLastInsertIndex++;
            break;
          } else if (isBefore(log, reorderedLogs[j])) {
            for (int k = alphaLastInsertIndex; k >= j; k--) {
              reorderedLogs[k+1] = reorderedLogs[k];
            }
            reorderedLogs[j] = log;
            alphaLastInsertIndex++;
            break;
          }
        }
      }
    }

    return reorderedLogs;
  }

  private boolean isBefore(String s1, String s2) {
    int s1fs = s1.indexOf(' ');
    int s2fs = s2.indexOf(' ');
    String s1_content = s1.substring(s1fs + 1);
    String s2_content = s2.substring(s2fs + 1);
    int comp = s1_content.compareTo(s2_content);
    if (comp != 0) {
      return comp < 0;
    }

    String s1Ident = s1.substring(0, s1fs);
    String s2Ident = s2.substring(0, s2fs);

    return s1Ident.compareTo(s2Ident) < 0;
  }
}

答案

class Solution {
    public String[] reorderLogFiles(String[] logs) {
        Arrays.sort(logs, (log1, log2) -> {
            String[] split1 = log1.split(" ", 2);
            String[] split2 = log2.split(" ", 2);
            boolean isDigit1 = Character.isDigit(split1[1].charAt(0));
            boolean isDigit2 = Character.isDigit(split2[1].charAt(0));
            if (!isDigit1 && !isDigit2) {
                int cmp = split1[1].compareTo(split2[1]);
                if (cmp != 0) return cmp;
                return split1[0].compareTo(split2[0]);
            }
            return isDigit1 ? (isDigit2 ? 0 : 1) : -1;
        });
        return logs;
    }
}

用lamda expression写了一个定制化的sort方法。Arrays.sort这个方法值得学习。 第二点是split后面加个2,就是把字符串分成两部分,这个也值得学习。 但是这个性能很差,只有15%

次优解

class Solution {
    public String[] reorderLogFiles(String[] logs) {

        Comparator<String> myComp = new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                int s1si = s1.indexOf(' ');
                int s2si = s2.indexOf(' ');
                char s1fc = s1.charAt(s1si+1);
                char s2fc = s2.charAt(s2si+1);

                if (s1fc <= '9') {
                    if (s2fc <= '9') return 0;
                    else return 1;
                }
                if (s2fc <= '9') return -1;

                int preCompute = s1.substring(s1si+1).compareTo(s2.substring(s2si+1));
                if (preCompute == 0) return s1.substring(0,s1si).compareTo(s2.substring(0,s2si));
                return preCompute;
            }
        };

        Arrays.sort(logs, myComp);
        return logs;
    }
    }

我觉得这个最好

P938. Range Sum of BST (Easy)

在一个BST中,找出所有在给定范围内的数的sum

我的思路

遍历BST,符合条件就累加起来。后来优化了一下,因为只有当当前值大于范围的下限时,才需要去看左子树; 只有当当前节点的值小于范围上限时,才需要去看右子树。如果当前值都已经小于最小值了,左子树只会比它更小,不用看了。

我的代码

public class E_938_RangeSumofBST {

    int sum;

    public int rangeSumBST(TreeNode root, int L, int R) {
        inOrder(root, L, R);
        return sum;
    }

    private void inOrder(TreeNode node, int L, int R) {
        if (node == null) {
            return;
        }
        if (node.val >= L) {
            inOrder(node.left, L, R);
        }
        if (node.val >= L && node.val <= R) {
            sum += node.val;
        }
        if (node.val <= R) {
            inOrder(node.right, L, R);
        }
    }
}

52% -> 100%

最优解

class Solution {
    public int rangeSumBST(TreeNode root, int L, int R) {
        if (root == null) return 0;
        if (root.val < L) {
            return rangeSumBST(root.right, L, R);
        }
        if (root.val > R) {
            return rangeSumBST(root.left, L, R);
        }
        return root.val + rangeSumBST(root.left, L, R) + rangeSumBST(root.right, L, R);
    }
}

其实不需要辅助方法。这个方法最简洁。值得学习。

P941. Valid Mountain Array (Easy)

判断一个数组是否是山峰形数组。先增长后减少。

我的思路

两个while循环,然后判断是否走到最后了。还要判断是否两个循环都进去过,也就是说曾经有过增加,也有过减少。

我的代码

public class E_941_ValidMountainArray {
    public boolean validMountainArray(int[] A) {
        if (A.length < 3) {
            return false;
        }
        int i = 1;
        boolean increase = false, decrease = false;
        while (i < A.length && A[i] > A[i - 1]) {
            increase = true;
            i++;
        }
        while (i < A.length && A[i] < A[i - 1]) {
            decrease = true;
            i++;
        }
        return i == A.length && increase && decrease;
    }

    public static void main(String[] args) {
        int[] test = {0,3,2,1};
        boolean b = new E_941_ValidMountainArray().validMountainArray(test);
        System.out.println("b = " + b);

    }
}

100%

最优解

class Solution {
    public boolean validMountainArray(int[] A) {

        int length = A.length;
        int i = 0;

        while (i+1 < length && A[i+1] > A[i])
            i++;

        if (i == 0 || i == length-1) return false;

        while(i+1 < length && A[i+1] < A[i])
            i++;

        return (i == length-1);

    }
}

中间判断了一下i是否是第一个或者最后一个,这两种情况都不行。

P942. DI String Match (Easy)

给一个字符串,只含有I或者D。I表示上升,D表示下降。N是这个字符串的长度。数组[0, 1, ..., N],调换顺序,使得它的上升下降趋势符合字符串中的表示。

我的思路

所有上升的地方,就从前往后依次排;所有下降的地方就从后往前依次排。

我的代码

public class E_942_DIStringMatch {
    public int[] diStringMatch(String S) {
        char[] chars = S.toCharArray();
        int[] ans = new int[S.length() + 1];
        int l = 0, r = S.length();
        int i = 0;
        for ( ; i < S.length(); i++) {
            if (chars[i] == 'I') {
                ans[i] = l;
                l++;
            } else {
                ans[i] = r;
                r--;
            }
        }
        ans[i] = l;
        return ans;
    }
}

94%

最优解

class Solution {
    public int[] diStringMatch(String S) {
        int n,add,sub,num,i;
        n = (S.length());
        add = 0;
        sub = n;
        num = 0;
        char ch[]= S.toCharArray();
        int a[] = new int[n+1];
        for(i = 0 ; i< n ; i++){
            if(ch[i] == 'I'){
                a[i] = add;
                add = add+1;

            }else{
                a[i] = sub;
                sub = sub - 1;
            }

        }
       a[n]= sub;
       return a;
    }
}

答案

class Solution {
    public int[] diStringMatch(String S) {
        int N = S.length();
        int lo = 0, hi = N;
        int[] ans = new int[N + 1];
        for (int i = 0; i < N; ++i) {
            if (S.charAt(i) == 'I')
                ans[i] = lo++;
            else
                ans[i] = hi--;
        }

        ans[N] = lo;
        return ans;
    }
}

感觉都差不多,随便用哪个都可以。

P944. Delete Columns to Make Sorted (Easy)

我的思路

每个单词都跟前一个比较,每个字符去比,如果有小于前一个的就记下来,说明这个位置需要删除掉。

我的代码

class Solution {
    public int minDeletionSize(String[] A) {
        boolean[] ans = new boolean[A[0].length()];
        for (int i = 1; i < A.length; i++) {
            for (int j = 0; j < A[i].length(); j++) {
                if (ans[j]) continue;
                if (A[i].charAt(j) < A[i - 1].charAt(j)) {
                    ans[j] = true;
                }
            }
        }
        int count = 0;
        for (boolean an : ans) {
            if (an) {
                count++;
            }
        }
        return count;
    }
}

12%

最优解

class Solution {
    public int minDeletionSize(String[] a) {
        int result = 0;
        int stringLength = a[0].length();

        for (int i = 0; i < stringLength; i++) {
            if (isNotSort(a, i)) {
                result++;
            }
        }
        return result;
    }

    public static boolean isNotSort(String[] s, int num) {
        char prev = s[0].charAt(num);
        for (String value : s) {
            char curr = value.charAt(num);
            if (curr < prev)
                return true;
            prev = curr;
        }
        return false;
    }
}

98% 这是最快的。我不明白为什么拆分出来一个函数,做同样的事,就比其他方法快很多。

答案:

class Solution {
    public int minDeletionSize(String[] A) {
        int ans = 0;
        for (int c = 0; c < A[0].length(); ++c)
            for (int r = 0; r < A.length - 1; ++r)
                if (A[r].charAt(c) > A[r+1].charAt(c)) {
                    ans++;
                    break;
                }

        return ans;
    }
}

18%

tags: LeetCode