作者: CheungQ

LeetCode刷题【611】有效三角形的个数

给定一个包含非负整数的数组,你的任务是统计其中可以组成三角形三条边的三元组个数。

示例 1:

输入: [2,2,3,4]
输出: 3
解释:
有效的组合是: 
2,3,4 (使用第一个 2)
2,3,4 (使用第二个 2)
2,2,3

注意:

  1. 数组长度不超过1000。
  2. 数组里整数的范围为 [0, 1000]。
Related Topics
  • 贪心
  • 数组
  • 双指针
  • 二分查找
  • 排序
  • \n
  • 👍 229
  • 👎 0
  • 题解

    构成三角形的条件:两条较短的边的合大于最长的边长

    所以,先数组排序

    然后按条件求解

    class Solution {
        public int triangleNumber(int[] nums) {
            Arrays.sort(nums);
            int count = 0;
            for (int line1Index = nums.length - 1; line1Index >= 2; line1Index--) {
                for (int line2Index = line1Index - 1; line2Index >= 1; line2Index--) {
                    for (int line3Index = line2Index - 1; line3Index >= 0; line3Index--) {
                        if (nums[line3Index]+nums[line2Index]>nums[line1Index]){
                            count++;
                        }else{
                            break;
                        }
                    }
                }
            }
            return count;
        }
    }
    

    内部查找的方法可以替换成二分法,后面再补充

    LeetCode刷题【743】 网络延迟时间

    n 个网络节点,标记为 1 到 n

    给你一个列表 times,表示信号经过 有向 边的传递时间。 times[i] = (ui, vi, wi),其中 ui 是源节点,vi 是目标节点, wi 是一个信号从源节点传递到目标节点的时间。

    现在,从某个节点 K 发出一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回 -1

     

    示例 1:

    输入:times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2
    输出:2
    

    示例 2:

    输入:times = [[1,2,1]], n = 2, k = 1
    输出:1
    

    示例 3:

    输入:times = [[1,2,1]], n = 2, k = 2
    输出:-1
    

     

    提示:

    • 1 <= k <= n <= 100
    • 1 <= times.length <= 6000
    • times[i].length == 3
    • 1 <= ui, vi <= n
    • ui != vi
    • 0 <= wi <= 100
    • 所有 (ui, vi) 对都 互不相同(即,不含重复边)
    Related Topics
  • 深度优先搜索
  • 广度优先搜索
  • 最短路
  • 堆(优先队列)
  • \n
  • 👍 395
  • 👎 0
  • 题解

    
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Set;
    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
    
        private int totalTime = -1;
        private int[] arriveTime;
        private int[][] map;
    
    
        public int networkDelayTime(int[][] times, int n, int k) {
            map = new int[n+1][n+1];
            for (int i = 0; i < map.length; i++) {
                int[] tmp = new int[n+1];
                Arrays.fill(tmp,-1);
                map[i] =tmp;
            }
            arriveTime = new int[n+1];
            Arrays.fill(arriveTime,-1);
            //到自己的位置需要花费时间为0
            arriveTime[k] = 0;
            for (int[] time : times) {
                //time[0];源节点 time[1];目标节点 time[2];时间
                map[time[0]][time[1]] = time[2];
            }
            dfs(k,0);
            int arrivedCount = 0;
            for (int i = 1; i < arriveTime.length; i++) {
                //i=-1表示没有到达这个点,
                if (arriveTime[i]>=0){
                    arrivedCount++;
                    totalTime = Math.max(totalTime,arriveTime[i]);
                }
            }
            return arrivedCount==n?totalTime:-1;
        }
    
        private void dfs(int startPoint,int time){
            int[] branch = map[startPoint];
            //从出发点开始,寻找可以走的下一步
            for (int i = 0; i < branch.length; i++) {
                //不等于0的 说明这条路是通的,可以访问,。
                if (branch[i]>=0){
                    //走下一步的第二个条件,目标点没有走过 即还没记录过走到下一个点需要多久
                    //或者 到达目标点将要花费的时间比 现在 已知的走到下一个点的时间要短
                    if (arriveTime[i]==-1 || arriveTime[i] > time+branch[i]){
                        //更新 到达下个点的时间  或者是  更短的到达下个点的时间
                        arriveTime[i] = time+branch[i];
                        dfs(i,time+branch[i]);
                    }
                }
            }
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)
    

    LeetCode刷题【581】最短无序连续子数组

    8月3日每日一题,=A=,今天才知道还有每日一题这个东西。

    给你一个整数数组 nums ,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。

    请你找出符合题意的 最短 子数组,并输出它的长度。

     

    示例 1:

    输入:nums = [2,6,4,8,10,9,15]
    输出:5
    解释:你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
    

    示例 2:

    输入:nums = [1,2,3,4]
    输出:0
    

    示例 3:

    输入:nums = [1]
    输出:0
    

     

    提示:

    • 1 <= nums.length <= 104
    • -105 <= nums[i] <= 105

     

    进阶:你可以设计一个时间复杂度为 O(n) 的解决方案吗?

    Related Topics
  • 贪心
  • 数组
  • 双指针
  • 排序
  • 单调栈
  • \n
  • 👍 612
  • 👎 0
  • 题解

    最直接能想到的解法。

    1,把原数组排序。

    2,从左到右遍历把排序数组和原数组对比,找到需要调整的左边界。

    3,从右向左遍历把排序数组和原数组对比,找到需要调整的右边界。

    4,左右边界中间部分就是需要重新排序的最短区间

    复杂度取决于使用的排序算法。

    那么,从上面的已知的思路可以看出来,需要找到的是左边起顺序的递增的区间,和右边起倒序递减的区间。

    在两边的区间里(不含中间无序部分)第i位都比左边i-n位大,都比右边第i+n位小。

    所以:

    1,从左向右遍历,记录找到的最大值,如果当前值比最大值小,说明当前值应该在已知最大值的左边,当前位置需要调整,记录位置rangeR

    2,同理,从右向左遍历,记录找到的最小值,如果当前的值比已知最小值大,说明当前值应该在已知的最小值右边,当前位置需要调整,记录位置rangeL

    3,最终需要调整的区间为rangeR到rangeL

    这里可以取个巧,用双指针,一次遍历,一个指针从头向尾部遍历,另一个指针从尾部像头部遍历。这样双指针的作法减少另一次遍历循环

    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
    
    
        /**
         * 从左向右找最大值
         * 如果当前值比最大值小,说明当前值应该在已知最大值的左边,当前位置需要调整,记录位置rangeR
         *
         * 同理,从右向左找最小值,
         * 如果当前的值比已知最小值大,说明当前值应该在已知的最小值右边,当前位置需要调整,记录位置rangeL
         *
         * 最终需要调整的区间为rangeR-rangeL
         * @param nums
         * @return
         */
        public int findUnsortedSubarray(int[] nums) {
            int min = Integer.MAX_VALUE;
            int max = Integer.MIN_VALUE;
            int rangeL = nums.length-1;
            int rangeR = 0;
            for (int l = 0; l < nums.length; l++) {
                int r = nums.length - l - 1;
                max = Math.max(nums[l],max);
                min = Math.min(nums[r],min );
                if (nums[l]<max){
                    rangeR = l;
                }
                if (nums[r]>min){
                    rangeL = r;
                }
            }
            return rangeR>rangeL?rangeR-rangeL+1:0;
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)

    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;
        }

    不过性能好像没啥提升