月度归档: 2021年7月

LeetCode刷题【171】Excel 表列序号

给你一个字符串 columnTitle ,表示 Excel 表格中的列名称。返回该列名称对应的列序号。

 

例如,

    A -> 1
    B -> 2
    C -> 3
    ...
    Z -> 26
    AA -> 27
    AB -> 28 
    ...

 

示例 1:

输入: columnTitle = "A"
输出: 1

示例 2:

输入: columnTitle = "AB"
输出: 28

示例 3:

输入: columnTitle = "ZY"
输出: 701

示例 4:

输入: columnTitle = "FXSHRXW"
输出: 2147483647

 

提示:

  • 1 <= columnTitle.length <= 7
  • columnTitle 仅由大写英文组成
  • columnTitle 在范围 ["A", "FXSHRXW"]
Related Topics
  • 数学
  • 字符串
  • \n
  • 👍 259
  • 👎 0
  • 题解

    
    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        public int titleToNumber(String columnTitle) {
            int num = 0;
            char[] chars = columnTitle.toCharArray();
            int pow = 1;
            for (int index = chars.length-1; index >= 0; index--) {
                num += (chars[index]-64)*pow;
                pow *= 26;
            }
            return num;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)
    

    已知字母一共26位

    window.CustomEvent浏览器自定义事件

    因为厂商、版本等的差异导致的兼容性问题,CustomEvent这个对象并不是所有浏览器都支持,官方给了一个这样的方法用作兼容

    (function(){
        try{
            // a : While a window.CustomEvent object exists, it cannot be called as a constructor.
            // b : There is no window.CustomEvent object
            new window.CustomEvent('T');
        }catch(e){
            let CustomEvent = function(event, params){
                params = params || { bubbles: false, cancelable: false, detail: undefined };
                let evt = document.createEvent('CustomEvent');
                evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
                return evt;
            };
            CustomEvent.prototype = window.Event.prototype;
            window.CustomEvent = CustomEvent;
        }
    })();

    至于构造和触发事件的方法的话,我们可以自己写个

    let publishCustomEvent = async ( eventName, eventParam , domElement) => {
        if ( domElement instanceof EventTarget){
            let customEvent = new CustomEvent(eventName, {detail:eventParam});
            domElement.dispatchEvent(customEvent);
            console.log(eventName+" pubed")
        }
    }

    至于监听方的就addEventListener方法了,这个太普遍了,就不再赘述了。

    LeetCode刷题【134】加油站

    在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

    你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

    如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。

    说明: 

    • 如果题目有解,该答案即为唯一答案。
    • 输入数组均为非空数组,且长度相同。
    • 输入数组中的元素均为非负数。

    示例 1:

    输入: 
    gas  = [1,2,3,4,5]
    cost = [3,4,5,1,2]
    
    输出: 3
    
    解释:
    从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
    开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
    开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
    开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
    开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
    开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
    因此,3 可为起始索引。

    示例 2:

    输入: 
    gas  = [2,3,4]
    cost = [3,4,3]
    
    输出: -1
    
    解释:
    你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。
    我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油
    开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油
    开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油
    你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。
    因此,无论怎样,你都不可能绕环路行驶一周。
    Related Topics
  • 贪心
  • 数组
  • \n
  • 👍 693
  • 👎 0
  • 题解

    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
    
        private int[] gasDouble;
        private int[] costDouble;
        private boolean doubled = false;
        private int singleLength = 0;
    
        public int canCompleteCircuit(int[] gas, int[] cost) {
            int start = -1;
            singleLength = gas.length;
            for (int i = 0; i < singleLength; i++) {
                if (gas[i] < cost[i]){
                    continue;
                }
                if (!doubled){
                    doubled = true;
                    gasDouble = new int[singleLength*2];
                    costDouble = new int[singleLength*2];
                    for (int i1 = 0; i1 < singleLength; i1++) {
                        gasDouble[i1] = gas[i1];
                        gasDouble[i1+singleLength] = gas[i1];
                        costDouble[i1] = cost[i1];
                        costDouble[i1+singleLength] = cost[i1];
                    }
                }
                if (tryDrive(i,i+ gas.length)){
                    return i;e1d
                }
    
            }
            return start;
        }
    
    
        private boolean tryDrive(int startIndex, int target){
            int gasLeft = 0;
            for (int i = startIndex; i < singleLength+startIndex; i++) {
                gasLeft += gasDouble[i];
                gasLeft -= costDouble[i];
                if (gasLeft<0){
                    return false;
                }
            }
            return true;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)
    

    这边先找到一个起步点,起步点必定要加气量大于等于到下一个加气站的消耗量。

    然后从这个站点的开始往后尝试,挨个求和,这边其实可以考虑下前缀和的作法。判断从 i点能否到i+n的的判断规则是,从i点往后,每个点的加气前缀和要大于对应点的消耗量前缀和。

    所以这里尝试一下从i点开始往后,一直再绕一圈回来回到i位置,而对应的破换成链的作法之一,选择了构建一个双倍长度的数组。从i点位置,往后跑原来的环的长度gas.length个位置,及等同于跑了一整圈。

    如果能跑成功,那么返回当前位置,如果跑不成功,继续尝试下一个,直到从环上每个位置都尝试过了一遍。

    这边其实还有一个点可以,直接抛出结论,如果从i位置开始跑,跑到i+n位置,失败了。那么下一次就直接可以i+n位置开始。要证明起来有点复杂,emmm,不大好描述,不过前面已经说了其实。

    就是从 i点能否到i+n的的判断规则是,从i点往后,每个点的加气前缀和要大于对应点的消耗量前缀和。

    因为我们是从i点开始的,所以必定有i点的加气量,大于i点的消耗量。但是到了i+n点的时候却跑步过去了,那么在i点之后到i+n点的时候,加气量之和一定是小于消耗量之和的。如果我们把这一段看成是一个点的话,比如是点i_virtual,那么显然i_virtual点的加气量小于消耗量。那么这段就是可以跳过的。

    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
    
        private int[] gasDouble;
        private int[] costDouble;
        private boolean doubled = false;
        private int singleLength = 0;
        private int startNext = -1;
    
        public int canCompleteCircuit(int[] gas, int[] cost) {
            int start = -1;
            singleLength = gas.length;
            for (int i = 0; i < singleLength; i++) {
                if (gas[i] < cost[i]){
                    continue;
                }
                if (!doubled){
                    doubled = true;
                    gasDouble = new int[singleLength*2];
                    costDouble = new int[singleLength*2];
                    for (int i1 = 0; i1 < singleLength; i1++) {
                        gasDouble[i1] = gas[i1];
                        gasDouble[i1+singleLength] = gas[i1];
                        costDouble[i1] = cost[i1];
                        costDouble[i1+singleLength] = cost[i1];
                    }
                }
                if (tryDrive(i,i+ gas.length)){
                    return i;
                }else{
                    i = startNext;
                }
    
            }
            return start;
        }
    
    
        private boolean tryDrive(int startIndex, int target){
            int gasLeft = 0;
            startNext = startIndex+1;
            for (int i = startIndex; i < singleLength+startIndex; i++) {
                gasLeft += gasDouble[i];
                gasLeft -= costDouble[i];
                startNext = i;
                if (gasLeft<0){
                    return false;
                }
            }
            return true;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)
    
    解答成功:
    执行耗时:28 ms,击败了34.70% 的Java用户
    内存消耗:38.7 MB,击败了37.53% 的Java用户

    额,马马虎虎吧

    LeetCode刷题【1876】长度为三且各字符不同的子字符串

    如果一个字符串不含有任何重复字符,我们称这个字符串为  字符串。

    给你一个字符串 s ,请你返回 s 中长度为 3 的 好子字符串 的数量。

    注意,如果相同的好子字符串出现多次,每一次都应该被记入答案之中。

    子字符串 是一个字符串中连续的字符序列。

     

    示例 1:

    输入:s = "xyzzaz"
    输出:1
    解释:总共有 4 个长度为 3 的子字符串:"xyz","yzz","zza" 和 "zaz" 。
    唯一的长度为 3 的好子字符串是 "xyz" 。
    

    示例 2:

    输入:s = "aababcabc"
    输出:4
    解释:总共有 7 个长度为 3 的子字符串:"aab","aba","bab","abc","bca","cab" 和 "abc" 。
    好子字符串包括 "abc","bca","cab" 和 "abc" 。
    

     

    提示:

    • 1 <= s.length <= 100
    • s​​​​​​ 只包含小写英文字母。
    Related Topics
  • 哈希表
  • 字符串
  • 计数
  • 滑动窗口
  • \n
  • 👍 3
  • 👎 0
  • 题解

        public int countGoodSubstrings(String s) {
            int count = 0;
            for (int i = 0; i < s.length()-2; i++) {
                if (s.charAt(i)!=s.charAt(i+1) && s.charAt(i+1)!= s.charAt(i+2) && s.charAt(i)!=s.charAt(i+2)){
                    count++;
                }
            }
            return count;
        }

    稍微改一下下,本次的第二第三个字符的对比结果,在下一次作为第一第二个字符的对比结果

    public int countGoodSubstrings(String s) {
            if (s.length() < 3){
                return 0;
            }
            int count = 0;
            boolean firstEqualSecond = s.charAt(0)!=s.charAt(1);
            for (int i = 0; i < s.length()-2; i++) {
                boolean secondEqualThird = s.charAt(i+1)!= s.charAt(i+2);
                if (firstEqualSecond && secondEqualThird && s.charAt(i)!=s.charAt(i+2)){
                    count++;
                }
                firstEqualSecond = secondEqualThird;
            }
            return count;
        }

    顺便再改下

    public int countGoodSubstrings(String s) {
            if (s.length() < 3){
                return 0;
            }
            int count = 0;
            boolean firstEqualSecond = s.charAt(0)!=s.charAt(1);
            for (int i = 0; i < s.length()-2; i++) {
                boolean secondEqualThird = s.charAt(i+1)!= s.charAt(i+2);
                if (!secondEqualThird){
                    //如果第二第三个字符相同,那么下一轮也是不用比较的,直接进入下下轮
                    i++;
                    //但是firstEqualSecond缓存的值就失效了,需要重新算下
                    //又但是下下轮可能越界
                    //所以,下面的i+1是下下轮的第一位
                    if (i+1+2< s.length()){
                        firstEqualSecond = s.charAt(i+1)!=s.charAt(i+2);
                    }
                    continue;
                }
    
                if (firstEqualSecond && secondEqualThird && s.charAt(i)!=s.charAt(i+2)){
                    count++;
                }
                firstEqualSecond = secondEqualThird;
            }
            return count;
        }

    不过性能好像没啥提升

    LeetCode刷题【1870】准时到达的列车最小时速

    给你一个浮点数 hour ,表示你到达办公室可用的总通勤时间。要到达办公室,你必须按给定次序乘坐 n 趟列车。另给你一个长度为 n 的整数数组 dist ,其中 dist[i] 表示第 i 趟列车的行驶距离(单位是千米)。

    每趟列车均只能在整点发车,所以你可能需要在两趟列车之间等待一段时间。

    • 例如,第 1 趟列车需要 1.5 小时,那你必须再等待 0.5 小时,搭乘在第 2 小时发车的第 2 趟列车。

    返回能满足你准时到达办公室所要求全部列车的 最小正整数 时速(单位:千米每小时),如果无法准时到达,则返回 -1

    生成的测试用例保证答案不超过 107 ,且 hour小数点后最多存在两位数字

     

    示例 1:

    输入:dist = [1,3,2], hour = 6
    输出:1
    解释:速度为 1 时:
    - 第 1 趟列车运行需要 1/1 = 1 小时。
    - 由于是在整数时间到达,可以立即换乘在第 1 小时发车的列车。第 2 趟列车运行需要 3/1 = 3 小时。
    - 由于是在整数时间到达,可以立即换乘在第 4 小时发车的列车。第 3 趟列车运行需要 2/1 = 2 小时。
    - 你将会恰好在第 6 小时到达。
    

    示例 2:

    输入:dist = [1,3,2], hour = 2.7
    输出:3
    解释:速度为 3 时:
    - 第 1 趟列车运行需要 1/3 = 0.33333 小时。
    - 由于不是在整数时间到达,故需要等待至第 1 小时才能搭乘列车。第 2 趟列车运行需要 3/3 = 1 小时。
    - 由于是在整数时间到达,可以立即换乘在第 2 小时发车的列车。第 3 趟列车运行需要 2/3 = 0.66667 小时。
    - 你将会在第 2.66667 小时到达。

    示例 3:

    输入:dist = [1,3,2], hour = 1.9
    输出:-1
    解释:不可能准时到达,因为第 3 趟列车最早是在第 2 小时发车。

     

    提示:

    • n == dist.length
    • 1 <= n <= 105
    • 1 <= dist[i] <= 105
    • 1 <= hour <= 109
    • hours 中,小数点后最多存在两位数字
    Related Topics
  • 数组
  • 二分查找
  • \n
  • 👍 16
  • 👎 0
  • 题解

    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
    
    
        public int minSpeedOnTime(int[] dist, double hour) {
    
            int l=1;
            int r = 10000000;
            int lastAvailableSpeed = -1;
            while (l<r){
                int mid = l + (r-l)/2;
                if (canArrive(dist,mid,hour)){
                    lastAvailableSpeed = mid;
                    r = mid;
                }else{
                    l = mid+1;
                }
            }
            return canArrive(dist,l,hour)?l:lastAvailableSpeed;
        }
    
    
        private boolean canArrive(int[] distArr, int speed, double hourLimit){
            double totalHour = 0;
            for (int i = 0; i < distArr.length; i++) {
                if (i==distArr.length-1){
                    totalHour += (double)distArr[i]/speed;
                }else{
                    totalHour += Math.ceil((double)distArr[i]/speed);
                }
            }
            return totalHour*1.0 <= hourLimit;
        }
    
    }
    //leetcode submit region end(Prohibit modification and deletion)
    

    写好判断规则,剩下的就是二分查找的工作,比较简单。

    不过最后一个用例

    测试用例:[1,1,100000]
    2.01

    被坑了会儿,最终left应该还可以再取一次判断,有可能最后left=mid+1的值等于right,但是被while的条件过滤掉了。

    此外这题里还有一个坑,就是浮点运算的时候精度丢失的问题,不小心就容易忽略。

    对二分法查找,目前虽然会用了,但是对于left,right的+1还是-1的时机还是有点模糊,以及最终是要取left的值还是取right的值也有点拎不清。还需多加练习理解努力。

    LeetCode刷题【剑指 Offer 38】字符串的排列

    输入一个字符串,打印出该字符串中字符的所有排列。

     

    你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。

     

    示例:

    输入:s = "abc"
    输出:["abc","acb","bac","bca","cab","cba"]
    

     

    限制:

    1 <= s 的长度 <= 8

    Related Topics
  • 字符串
  • 回溯
  • \n
  • 👍 391
  • 👎 0
  • 题解

    看起来有点面熟,很直白的深度搜索回溯算法。

    根据所给字符串s,挨个位置遍历,在新得出的字符串的每一位上穷举,并记录已举过的字符串,在穷举的时候,如果已经存在于已使用过的位置的集合的记录中,则跳过。

    而最终要求“里面不能有重复元素”,所以最终的结果集用set集合保存,直接进行去重。

    class Solution {
    
    
        private HashSet<String> list;
    
        private Integer length;
    
        public String[] permutation(String s) {
            list = new HashSet<>();
            length = s.length();
            HashSet<Integer> visited = new HashSet<>();
            dfs(s.toCharArray(),visited,0,new char[s.length()]);
            return list.toArray(new String[0]);
        }
    
    
        private void dfs(char[] charArr,HashSet<Integer> visited,int index,char[] newCharArr){
            if (index==length){
                list.add(new String(newCharArr));
                return;
            }
            for (int i = 0; i < charArr.length; i++) {
                if (visited.contains(i)){
                    continue;
                }
                visited.add(i);
                newCharArr[index] = charArr[i];
                dfs(charArr,visited,index+1,newCharArr);
                visited.remove(i);
            }
        }
    }

    结果

    解答成功:
    执行耗时:69 ms,击败了10.93% 的Java用户
    内存消耗:44.2 MB,击败了21.26% 的Java用户

    而在结果集没有使用set集合的时候,跑这样一个用例出了问题。

    测试用例:"aab"
    测试结果:["aab","aba","aab","aba","baa","baa"]
    期望结果:["aba","aab","baa"]

    所以,取第一个a还是第二个a是没有区别的,这边之前记录的是HashSet<Integer> visited字符串的索引位置,所以在此之外,可以再借助一个字符串集合进行剪枝操作Set<Character> sameChar。剪枝之后的结果集也可以不用set集合了,直接更简单的List集合替代。

    另外还有一点小小的改动,一开始写的visited是Set集合,而记录内容是某个索引位置是否访问过,都知道索引的特征是从0-N连续的,这边可以再简化成一个boolean[] visited数组。

    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
    
    
        private List<String> list;
    
        private Integer length;
    
        public String[] permutation(String s) {
            list = new ArrayList<>();
            length = s.length();
            boolean[] visited = new boolean[s.length()];
            dfs(s.toCharArray(),visited,0,new char[s.length()]);
            String[] res = new String[list.size()];
            int i = 0;
            for (String s1 : list) {
                res[i] = s1;
                i++;
            }
            return res;
        }
    
    
        private void dfs(char[] charArr,boolean[] visited,int index,char[] newCharArr){
            if (index==length){
                list.add(new String(newCharArr));
                return;
            }
            Set<Character> sameChar = new HashSet<>();
            for (int i = 0; i < charArr.length; i++) {
                if (visited[i]){
                    continue;
                }
                if (sameChar.contains(charArr[i])){
                    continue;
                }
                sameChar.add(charArr[i]);
                visited[i] = true;
                newCharArr[index] = charArr[i];
                dfs(charArr,visited,index+1,newCharArr);
                visited[i] = false;
            }
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)
    
    
    解答成功:
    执行耗时:14 ms,击败了64.61% 的Java用户
    内存消耗:42.7 MB,击败了73.19% 的Java用户

    LeetCode刷题【169】多数元素

    给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

    你可以假设数组是非空的,并且给定的数组总是存在多数元素。

     

    示例 1:

    输入:[3,2,3]
    输出:3

    示例 2:

    输入:[2,2,1,1,1,2,2]
    输出:2
    

     

    进阶:

    • 尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。
    Related Topics
  • 数组
  • 哈希表
  • 分治
  • 计数
  • 排序
  • \n
  • 👍 1075
  • 👎 0
  • 题解

    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        public int majorityElement(int[] nums) {
            int maj = nums[0];
            int count = 1;
            for (int i = 1; i < nums.length; i++) {
                if (nums[i]==maj){
                    count++;
                }else{
                    count--;
                    if (count==0){
                        maj=nums[i];
                        count=1;
                    }
                }
            }
            return maj;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)
    

    看到这个Boyer-Moore摩尔投票的解法的时候,确实惊艳到了,虽然只是个简单题。

    根据题中描述

    你可以假设数组是非空的,并且给定的数组总是存在多数元素。

    题中的众数必定是超过半数的,及时数量总和为偶数的情况下,众数的数量也一定是超过一半的,不会等于1/2。

    一开始看到这个解法有点懵,画个图辅助理解下。每一种颜色代表一个数字,每一个格子代表给定的数组中有一个这个数字。

    从图中可以很明显看到,蓝为众数,那么按照摩尔投票方法的作法,随机取一个格子,然后在其他颜色区域的内容中,随机找一个格子和他相对抵消。在这里我们用数字x和对应的抵消数字x’来标示。

    那么其实很明显了,最终还有空余,没有被全部抵消掉的颜色只有蓝色了,这个蓝色代表的数字必然是众数。

    另外还是说下常规解法,就写一个比较好想的hashmap统计了。

    
    import java.util.HashMap;
    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        public int majorityElement(int[] nums) {
            int half = nums.length>>1;
            HashMap<Integer,Integer> map = new HashMap<>();
            for (int num : nums) {
                int c = 1;
                if (map.containsKey(num)){
                    c = map.get(num)+1;
                }
                if (c > half){
                    return num;
                }
                map.put(num,c);
            }
    
            return 0;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)