21年10月第三周 力扣每日一题记录

本周每日一题 题目

  • lc273. 整数转换英文表示
  • lc29. 两数相除
  • lc412. Fizz Buzz
  • 剑指 Offer II 069. 山峰数组的顶部
  • lc38. 外观数列
  • lc282. 给表达式添加运算符
  • lc230. 二叉搜索树中第K小的元素

10-11 lc273. 整数转换英文表示

  • 这题真是把我恶心到了,没有给数字与单词的映射表,我真是服了,自己敲半天,而且我单词敲错了好几个,真是书白读了
  • 不过还算是好题,我的思路是先解决十亿和百万的级别,逻辑为,用 i i i 标识当前位,用 e x c e e d exceed exceed 来标识超出当前档次的数位,比如当前是9位,就是超过了7位的百万2位
  • 然后百万以下的单独算,也是一样的逻辑,先来弄千,再来百,然后每次都是先处理超过的部分,再加上当前的档次,然后再处理后序的部分。在这中间,需要单独处理0
  • 整体挺复杂的,写的脖子发酸
  • 稍微有点乱,不过其实还好
class Solution {
    //如果是人的话会怎样思考呢,从左往右,到每个位置都数0的个数(需要提前确定一共有多少位)
    //有几个档次,根据第几位来确定:3是百,4是千,7是百万,10是十亿
    //每次读取时尽量往高了比,如果刚好符合档次,就只说当前,如果超过档次,比如是7,那么就是几十百万,要把后面的位数也算上
    //整体的逻辑就是这样
    //映射表也够大的。。。1-9,然后11-19,然后还有20-90
    private static Map<Integer,String> map = new HashMap<>(){{
            put(1,"One ");
            put(2,"Two ");
            put(3,"Three ");
            put(4,"Four ");
            put(5,"Five ");
            put(6,"Six ");
            put(7,"Seven ");
            put(8,"Eight ");
            put(9,"Nine ");
            put(10,"Ten ");
            put(11,"Eleven ");
            put(12,"Twelve ");
            put(13,"Thirteen ");
            put(14,"Fourteen ");
            put(15,"Fifteen ");
            put(16,"Sixteen ");
            put(17,"Seventeen ");
            put(18,"Eighteen ");
            put(19,"Nineteen ");
            put(20,"Twenty ");
            put(30,"Thirty ");
            put(40,"Forty ");
            put(50,"Fifty ");
            put(60,"Sixty ");
            put(70,"Seventy ");
            put(80,"Eighty ");
            put(90,"Ninety ");
        }};
    private static char[] ss;
    public static String numberToWords(int num) {
        ss = String.valueOf(num).toCharArray();
        int n = ss.length;
        StringBuilder sb = new StringBuilder();

        for(int i=0; i<n; i++){
            if(ss[i]=='0'){//0就跳过
                if(n==1) return "Zero";
                continue;
            }
            int bit = n - i;//当前多少位
            if(bit>=10){
                sb.append(help(i,bit-9));
                sb.append("Billion ");
                i += bit -10;
            }
            else if(bit>=7){
                sb.append(help(i,bit-6));
                sb.append("Million ");
                i += bit -7;
            }
            else{
                sb.append(help(i,bit));
                break;
            }
        }
        return sb.toString().trim();//最终要处理一下空格

    }
    //计算百万以下的部分
    private static String help(int i, int exceedBit){
        if(exceedBit<=0) return "";
        if(ss[i]=='0') return help(i+1,exceedBit-1);
        StringBuilder sb = new StringBuilder();
        //以千为档
        if(exceedBit>=4){
            sb.append(help(i,exceedBit-3));
            sb.append("Thousand ");
            sb.append(help(i+exceedBit-3,3));
        }
        else if(exceedBit>=3){
            sb.append(help(i,exceedBit-2));
            sb.append("Hundred ");
            sb.append(help(i+exceedBit-2,2));
        }
        else{
            //还要区分90还是19
            int num = Integer.valueOf(new String(ss,i,exceedBit));
            if(num==0) return "";
            if(num>=20){
                sb.append(map.get(num/10*10));
                sb.append(help(i+1,exceedBit-1));
            }
            else sb.append(map.get(num)); //处理1-19
        }
        return sb.toString();
    }
}

10-12 lc29. 两数相除

  • 因为不能用乘法,除法,所以想法就是用2^n来进行减法
  • 每次减去2^n*divisor,然后就能够快速逼近0了。这里的乘法用位运算来代替
  • 而结果就是 2 i + 2 j + . . . 2^i + 2^j +... 2i+2j+...
class Solution {
    //每次减divisor * 2^n, 
    public int divide(int dividend, int divisor) {
        if(divisor==0) return 0;
        if(dividend==Integer.MIN_VALUE&&divisor==-1) return Integer.MAX_VALUE; //处理
        //确定符号
        int sign = (dividend>=0&&divisor>=0)||(dividend<0&&divisor<0)?1: -1;
        int res = 0;
        long t = Math.abs((long)dividend);
        long d = Math.abs((long)divisor);
        for(int i=31; i>=0; i--){
            if((t>>i)>=d){
                t -= d<<i;
                res += 1<<i;
            }
        }
        return res * sign;
    }
}

10-13 lc412. Fizz Buzz

  • 很没有意思的一道简单题,浪费时间记录
class Solution {
    //返回List.size()= n
    //每次添加,必须为O(n)
    //计算是3和5的倍数,也不用担心时间复杂度的把,这题。。。无聊
    public List<String> fizzBuzz(int n) {
        List<String> res = new ArrayList<>();
        for(int i=1; i<=n; i++){
            if(i%5==0&&i%3==0) res.add("FizzBuzz");
            else if(i%5==0) res.add("Buzz");
            else if(i%3==0) res.add("Fizz");
            else res.add(String.valueOf(i));
        }
        return res;
    }
}

10-14 剑指 Offer II 069. 山峰数组的顶部

  • 就是一个二分查找的简单题
  • 这里用r-l==1来防止查询二分时死循环,是一个可以重复使用的trick
class Solution {
    // 只有一个顶峰呗,最大值不是吗,直接O(n),不仅仅是这样,很明显有规律的
    //因为输入必为山峰数组,那么一定是从小到大,从大到小
    //就是一个二分查找就可以解决
    //有前后进行比较,如果mid>=前,那么l=mid,eles if mid <前,r = mid -1
    public int peakIndexInMountainArray(int[] arr) {
        int l = 0, r = arr.length-1;
        while(l<r){
            if(r-l==1) return arr[l]>arr[r]?l:r;
            int mid = (l+r) / 2;
            if(arr[mid]>arr[mid-1]) l = mid;
            else r = mid -1;
        }
        return l;
    }
}

10-15 lc38. 外观数列

  • 之前做过这题,虽然已经是秋招7月份的事了
  • 这一题我是用模拟来做的,每次读取上面一行,然后挨个统计个数,来生成新的一行
  • 逻辑在于:如果当前和前一个不等,则记录前一个;如果相等,则cnt++。然后到达右边界在单独添加到结果中
  • 然后这里的左边界,我是选择直接从n=2开始,这样就可以避免
class Solution {
    //模拟就可以了,每一行读上面的一行,然后就是按字符统计个数,然后进行append就可以了
    //因为n<=30,所以这个就很好来弄了
    //最后一个单独判断,强制写入右边界
    public String countAndSay(int n) {
        if(n<=0) return "";
        if(n==1) return "1";
        String s = "11";
        for(int i=2; i<n; i++){
            StringBuilder sb = new StringBuilder();
            char[] ss = s.toCharArray();
            int cnt = 1;
            for(int j=1; j<ss.length; j++){
                //不同,记录前一个,相同,cnt++
                //右边界,都要添加
                if(ss[j]!=ss[j-1]){
                    sb.append(cnt);
                    sb.append(ss[j-1]);
                    cnt = 1;
                }
                else cnt++;
                if(j+1==ss.length){
                    sb.append(cnt);
                    sb.append(ss[j]);
                }
            }
            s = sb.toString();
        }
        return s;
    }
}

10-16 lc282. 给表达式添加运算符

  • 这题写了很久很久,真的很恶心
  • 首先是题目整体的思路不难,但是很多边界,还有恶心的地方
  • 思路为:最多10个数字,也就是有9个位置,四种选择, + − ∗ 不 加 +-*不加 +,又因为题意要求返回所有等于target的结果,那么枚举所有是必须的了,所以有4^9种情况。这就是个回溯的问题。然后就是将所有的表达式来进行判断,是否和target相等
  • 以上是简单的思路,这里我是直接用到了之前的计算表达式II的代码
  • 当然,这样比较慢,也可以在枚举的过程中直接计算,这样也可以,而且会快很多
  • 恶心的地方在于,还要考虑当前字母是否为0,然后0的话要看前面有的是数字还是符号,如果是符号,那后面必为符号
  • 然后还有就是target可能是-2^32,这个也是题目恶心人的地方,所以t设置为long,防止溢出
class Solution {
    //是个回溯的题
    //因为要返回所有的答案,而num.length又在10之内,一个用3^10种组合,6w+种可能,其实还好
    //比较直接的方法,直接穷举所有的可能,然后组合成字符串,然后来计算
    //难点在于,可能不需要在每个数之间插入符号,所以有四种
    //还要单独考虑0,有点恶心 比如 105 这种,还有开头015这种,你要如何处理,开头是0可能要单独的搞
    //不要符号遍历了,直接简单的全部调用一遍。。。
    private static long t;
    private static List<String> res;
    public static List<String> addOperators(String num, int target) {
        t = (long)target; 
        res = new ArrayList<>();
        StringBuilder sb = new StringBuilder();
        enumerate(num.toCharArray(),sb,0);
        return res;
    }

    //枚举所有的可能
    private static void enumerate(char[] num,StringBuilder sb,int index){
        if(index==num.length-1){
            sb.append(num[index]);
            String s = sb.toString();
            if(calc(s)==t) 
                res.add(s);
            // res.add(s);
            sb.deleteCharAt(sb.length()-1);
            return;
        }
        char ch = num[index];
        sb.append(ch);
        //添加三种符号并回溯
        sb.append('*');
        enumerate(num,sb,index+1);
        sb.deleteCharAt(sb.length()-1);
        sb.append('+');
        enumerate(num,sb,index+1);
        sb.deleteCharAt(sb.length()-1);
        sb.append('-');
        enumerate(num,sb,index+1);
        sb.deleteCharAt(sb.length()-1);
        if(ch!='0'||(index!=0&&Character.isDigit(sb.charAt(sb.length()-2))))
            enumerate(num,sb,index+1);
        sb.deleteCharAt(sb.length()-1);
    }

    //计算结果
    //遇到乘法,就计算结果
    private static long calc(String s){
        char preSign = '+';
        Deque<Long> stack = new LinkedList<>();
        char[] ss = s.toCharArray();
        int n = ss.length;
        long cur = 0;
        for(int i=0; i<n; i++){
            char ch = ss[i];
            if(Character.isDigit(ch)) cur = cur*10+ch-'0';
            if(ch==' '&&i!=n-1) continue;
            if(!Character.isDigit(ch)||i==n-1){
                if(preSign=='+') stack.push(cur);
                else if(preSign=='-') stack.push(cur*-1);
                else if(preSign=='*') stack.push(stack.pop()*cur);
                else if(preSign=='/') stack.push(stack.pop()/cur);
                cur = 0;
                preSign = ch;
            }
        }
        long res = 0;
        while(!stack.isEmpty()) res += stack.pop();
        return res;
    }
}

10-17 lc230. 二叉搜索树中第K小的元素

  • BST,所以中序遍历就可以,想了一会儿,如果一个方法,确实没办法直接输出结果
  • 所以两个方法,一个dfs,一个api调用
  • 然后逻辑是:用cnt表示当前第几个,res代表第几个值,然后中序遍历,每次都cnt–,只要一旦cnt为0,则直接返回,所以dfs中有两个return
  • 这次的代码比之前第一次写的要快很多,因为有剪枝
class Solution {
    //这是个BST,中序遍历就是顺序,中序遍历计数,结果就是第k个
    //左,根右
    private static int res;
    private static int cnt;
    public static int kthSmallest(TreeNode root, int k) {
        cnt = k;
        help(root);
        return res;
    }

    private static void help(TreeNode root){
        if(cnt<=0||root==null) return;
        help(root.left);
        cnt--;
        if(cnt<0) return;
        res = root.val;
        help(root.right);
    }
}

你可能感兴趣的:(LeetCode,java,leecode,力扣)