• LeetCode刷新记录


    1. 两数之和

    给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

    示例:

    给定 nums = [2, 7, 11, 15], target = 9

    因为 nums[0] + nums[1] = 2 + 7 = 9
    所以返回 [0, 1]

    个人思路:暴力破解

    class Solution {
        public int[] twoSum(int[] nums, int target) {
            int[] result = new int[2];
            for(int i = 0; i < nums.length; i++){
                    for(int j = i + 1; j < nums.length; j++){
                        if(nums[i] + nums[j] == target){
    						return new int[]{i, j};
                        }
                    }
            }
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    结果:失败

    原因:超出时间限制

    官方解题代码

    思路:

    使用哈希表,可以将寻找 target - x 的时间复杂度降低到从O(N) 降低到 O(1)

    这样我们创建一个哈希表,对于每一个 x,我们首先查询哈希表中是否存在 target - x,然后将 x 插入到哈希表中,即可保证不会让 x 和自己匹配。

    算法:

    class Solution {
        public int[] twoSum(int[] nums, int target) {
            Map<Integer, Integer> hashtable = new HashMap<Integer, Integer>();
            for (int i = 0; i < nums.length; ++i) {
                if (hashtable.containsKey(target - nums[i])) {
                    return new int[]{hashtable.get(target - nums[i]), i};
                }
                hashtable.put(nums[i], i);
            }
            return new int[0];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    参考

    Leetcode - 两数之和题解

    java中hashmap容器实现查找O(1)时间复杂度的思考

    7. 整数反转

    给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。

    示例 1:

    输入: 123
    输出: 321

    示例 2:

    输入: -123
    输出: -321

    示例 3:

    输入: 120
    输出: 21

    注意:

    假设我们的环境只能存储得下 32 位 的有符号整数,则其数值范围为 [-2147483648, 2147483647]。请根据这个假设,如果反转后整数溢出那么就返回 0

    个人思路1

    int 转 String,反转,String 转 int。

    太繁琐,放弃。

    个人思路2

    通过取模运算

    class Solution {
        public int reverse(int x) {
            int result = 0;
            while (x != 0) {
                int temp = x % 10;
                x /= 10;
                result = result * 10 + temp;
            }
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    结果:失败

    原因:没有解决整数溢出的问题(不知道怎么完美处理)

    参考画解算法后的代码

    class Solution {
        public int reverse(int x) {
            int result = 0;
            while (x != 0) {
                int temp = x % 10;
                x /= 10;
                if (result > Integer.MAX_VALUE / 10 || (result == Integer.MAX_VALUE / 10 && temp > 7)) 
                    return 0;
                if (result < Integer.MIN_VALUE / 10 || (result == Integer.MIN_VALUE / 10 && temp < -8)) 
                    return 0;          
                result = result * 10 + temp;
            }
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    思路:
    在这里插入图片描述

    另一种解题方式

    class Solution {
        public int reverse(int x) {
            long result = 0;
            while(x != 0){
                result = result * 10 + x % 10;
                x /= 10;
            }
            return (int)result == result ? (int)result : 0;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    思路:

    1. 通过long类型接收int反转后的值,
    2. 再将结果强转为int
    3. 如果强转后和结果溢出,则返回0。否则,返回该值。

    参考

    Leetcode - 整数反转题解

    9. 回文数

    判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

    示例1:

    输入: 121
    输出: true

    示例二:

    输入: -121
    输出: false
    解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

    示例三:

    输入: 10
    输出: false
    解释: 从右向左读, 为 01 。因此它不是一个回文数。

    进阶:

    你能不将整数转为字符串来解决这个问题吗?

    个人思路1

    1. 根据 整数反转 的思路改动
    2. 题目没有提到溢出的问题,所以代码中也没有对溢出问题做处理
    3. 假如 x < 0,则一定 非回文数,所以直接return false
    4. 接着,将反转后的 tempx 作比较,若相等,则为回文数;
    class Solution {
        public boolean isPalindrome(int x) {
            int origin = x;
            int temp = 0;
            if (x < 0) {
                return false;
            }        
            while (x != 0) {
                temp = temp * 10 + x % 10;
                x /= 10;
            }
            return temp == origin ? true : false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    结果:成功

    个人思路2

    1. 假如 x < 0,则一定 非回文数,所以直接return false
    2. int 转换成 string
    3. 通过下标移动进行比较
    class Solution {
        public boolean isPalindrome(int x) {
            String stringX = String.valueOf(x);
            if (x < 0) {
                return false;
            }        
            for (int i = 0, j = stringX.length() - 1; i < stringX.length(); i++, j--) {
                if (j <= i) {
                    return true;
                }
                if (stringX.charAt(i) != stringX.charAt(j)) {
                    return false;
                }
            }
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    结果:成功

    个人思路1的进阶版:取后半段数字翻转后与前半段数字作比较

    在这里插入图片描述

    class Solution {
        public boolean isPalindrome(int x) {
            // 结尾为0,则一定不会是回文数。因为开头的数字不可能为0
            if (x < 0 || (x % 10 == 0 && x != 0)) return false;
            int revertedNumber = 0;
            while (x > revertedNumber) {
                revertedNumber = revertedNumber * 10 + x % 10;
                x /= 10;
            }
            return x == revertedNumber || x == revertedNumber / 10;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    参考

    动画:回文数的三种解法 | 法解种三的数文回:画动

    个人思路2的优化版:使用StringBuilder.reverse()

    class Solution {
        public boolean isPalindrome(int x) {
            String reversedStr = (new StringBuilder(x + "")).reverse().toString();
            return (x + "").equals(reversedStr);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    参考

    动画:回文数的三种解法 | 法解种三的数文回:画动

    13. 罗马数字转整数

    罗马数字包含以下七种字符: IVXLCDM

    在这里插入图片描述
    例如, 罗马数字 2 写做 II ,即为两个并列的 112 写做 XII ,即为 X + II27 写做 XXVII, 即为 XX + V + II

    通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

    • I 可以放在 V (5)X (10) 的左边,来表示 49
    • X 可以放在 L (50)C (100) 的左边,来表示 4090
    • C 可以放在 D (500)M (1000) 的左边,来表示 400 900

    给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。

    示例 1:

    输入: “III”
    输出: 3

    示例 2:

    输入: “IV”
    输出: 4

    示例 3:

    输入: “IX”
    输出: 9

    示例 4:

    输入: “LVIII”
    输出: 58
    解释: L = 50, V= 5, III = 3.

    示例 5:

    输入: “MCMXCIV”
    输出: 1994
    解释: M = 1000, CM = 900, XC = 90, IV = 4.

    提示:

    题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
    IC 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。

    个人思路

    1. 将所有存在的可能都放进map当中
    2. 第一次取前两位的字符,判断是否在map当中
    3. 是 -> 从map中获取对应数值,结束此次循环
    4. 否 -> 取第一位字符,从map中获取对应数值
    class Solution {
        public int romanToInt(String s) {
            Map<String, Integer> romanNumerals = new HashMap<>();
            romanNumerals.put("I", 1);
            romanNumerals.put("V", 5);
            romanNumerals.put("X", 10);
            romanNumerals.put("L", 50);
            romanNumerals.put("C", 100);
            romanNumerals.put("D", 500);
            romanNumerals.put("M", 1000);
            romanNumerals.put("IV", 4);
            romanNumerals.put("IX", 9);
            romanNumerals.put("XL", 40);
            romanNumerals.put("XC", 90);
            romanNumerals.put("CD", 400);
            romanNumerals.put("CM", 900);
    
            int temp = 0;
            int result = 0;
            int stringLength = s.length();
            while (temp < stringLength) {
                if (temp + 2 <= stringLength && romanNumerals.containsKey(s.substring(temp, temp + 2))) {
                    result += romanNumerals.get(s.substring(temp, temp + 2));
                    temp += 2;
                    continue;
                }
                result += romanNumerals.get(String.valueOf(s.charAt(temp)));
                temp += 1;
            }   
            return result;     
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    结果:成功

    参考LeetCode上Smileyan的代码

    class Solution {
        public int romanToInt(String s) {
            s = s.replace("IV","a");
            s = s.replace("IX","b");
            s = s.replace("XL","c");
            s = s.replace("XC","d");
            s = s.replace("CD","e");
            s = s.replace("CM","f");
            
            int result = 0;
            for (int i=0; i<s.length(); i++) {
                result += which(s.charAt(i));
            }
            return result;
        }
    
        public int which(char ch) {
            switch(ch) {
                case 'I': return 1;
                case 'V': return 5;
                case 'X': return 10;
                case 'L': return 50;
                case 'C': return 100;
                case 'D': return 500;
                case 'M': return 1000;
                case 'a': return 4;
                case 'b': return 9;
                case 'c': return 40;
                case 'd': return 90;
                case 'e': return 400;
                case 'f': return 900;
            }
            return 0;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    其他的解法都大同小异,这个代码更加的简洁。

    参考

    Java性能测试的困惑:switch和map的性能比较
    看评论区 - Smileyan

    20. 有效的括号

    给定一个只包括 '('')''{''}''['']' 的字符串,判断字符串是否有效。

    有效字符串需满足:

    1. 左括号必须用相同类型的右括号闭合。
    2. 左括号必须以正确的顺序闭合。

    注意空字符串可被认为是有效字符串。

    示例 1:

    输入: “()”
    输出: true

    示例 2:

    输入: “()[]{}”
    输出: true

    示例 3:

    输入: “(]”
    输出: false

    示例 4:

    输入: “([)]”
    输出: false

    示例 5:

    输入: “{[]}”
    输出: true

    个人思路

    1. 空字符串,直接返回true
    2. 字符串长度 % 2 != 0,直接返回false
    3. 左右括号的关系放进HashMap
    4. 利用Stack的先进后出的特性
    5. 遍历字符串
      1. 如果是左括号,添加进Stack
      2. 如果是右括号,从HashMap中得到对应的Value,用这个ValueStack中的最后一个元素作比较。若相等,则继续循环;若不相等,则直接返回false
      3. 直到循环全部结束,如果都没有提前返回false。则判断Stack中是否有未取出的元素。若存在未取出的元素,则返回false,否则返回true。(这是为了避免((的情况)
    class Solution {
        public boolean isValid(String s) {
            // 1. 空字符串直接返回true
            if (s.length() == 0) {
                return true;
            }
    
            // 2. 如果字符段长度 % 2 != 0,则return false
            if (s.length() % 2 != 0) {
                return false;
            }
    
            // 3. 用HashMap存储括号之间的关系
            Map<Character, Character> bracketsMap = new HashMap<>();
            bracketsMap.put(']', '[');
            bracketsMap.put('}', '{');
            bracketsMap.put(')', '(');
    
            // 4. 利用LinkedList
            LinkedList<Character> stack = new LinkedList<>();
            for (Character c : s.toCharArray()) {
                // 5. 如果得到的字符在Map的key中,证明它是右括号。那么,从stack中获取元素,它需要与这个Key所对应的Value相等,否则不匹配。
                if (bracketsMap.containsKey(c)) {
                    if (stack.isEmpty() || !stack.pop().equals(bracketsMap.get(c))) {
                        return false;
                    }
                    continue;
                }
                stack.push(c);
            }
            return stack.isEmpty() ? true : false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    结果:成功

    执行用时:2 ms

    内存消耗:36.7 MB

    参考LeetCode上淺い空的代码

    class Solution {
        public boolean isValid(String s) {
            LinkedList<Character> stack = new LinkedList<>();
            for (char c : s.toCharArray()) {
                if (c == '[') stack.push(']');
                else if (c == '(') stack.push(')');
                else if (c == '{') stack.push('}');
                else if (stack.isEmpty() || c != stack.pop()) return false;
            }
            return stack.isEmpty();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    结果:成功

    执行用时:1 ms

    内存消耗:36.6 MB

    思路差不多,果然数据量比较少的时候,可以抛弃Map。

    参考

    看评论区 - 淺い空
    ArrayList和LinkedList和Vector的区别
    java集合系列之LinkList

    14. 最长公共前缀

    编写一个函数来查找字符串数组中的最长公共前缀。

    如果不存在公共前缀,返回空字符串 “”。

    示例 1:

    输入: [“flower”,“flow”,“flight”]
    输出: “fl”

    示例 2:

    输入: [“dog”,“racecar”,“car”]
    输出: “”
    解释: 输入不存在公共前缀。

    说明:

    所有输入只包含小写字母 a-z

    个人思路

    1. 首先将字符串数组按照长度从小到大排序,拿到最小的字符串长度
    2. 遍历字符串数组,依次读取字符串的前缀字符,然后放进HashSet
    3. 根据HashSet中元素不可重复的原理,每一次遍历下来,如果HashSet中的长度超过1,则证明此次循环中的前缀不一致,返回上一次记录的前缀
    class Solution {
        public String longestCommonPrefix(String[] strs) {
            if (strs.length == 0){
                return "";
            }        
            String result = "";
            Set<String> stringSet = new HashSet<>();
            // 按照长度排序,获得字符串的最小长度,超过最小长度的字符串就不再需要判断了
            Arrays.sort(strs, (x, y) -> {
                return x.length() <= y.length() ? -1 : 1;
            });
            int minLength = strs[0].length();
    
            int index = 1;
            while (index <= minLength) {
                for (String data : strs) {
                    stringSet.add(data.substring(0, index));           
                }
                // 用HashSet排除重复元素,如果一次循环下来,HashSet的长度大于1,则证明此次循环中,有不同的前缀字符
                if (stringSet.size() > 1) {
                    break;
                } else {
                    result = stringSet.iterator().next();
                    stringSet.clear();
                    index++;
                }
            }
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    结果:成功

    官方解题

    官方提出了4种解题思路:

    1. 横向扫描
    2. 纵向扫描
    3. 分治
    4. 二分查找

    其中 横向扫描纵向扫描 的原理其实类似,我个人解题思路偏向于 纵向扫描

    分治 的解题思路,在横向扫描的基础上,将字符串数组切分计算再做整合。

    分治

    在这里插入图片描述

    class Solution {
        public String longestCommonPrefix(String[] strs) {
            if (strs == null || strs.length == 0) {
                return "";
            } else {
                return longestCommonPrefix(strs, 0, strs.length - 1);
            }
        }
    
        public String longestCommonPrefix(String[] strs, int start, int end) {
            if (start == end) {
                return strs[start];
            } else {
                int mid = (end - start) / 2 + start;
                String lcpLeft = longestCommonPrefix(strs, start, mid);
                String lcpRight = longestCommonPrefix(strs, mid + 1, end);
                return commonPrefix(lcpLeft, lcpRight);
            }
        }
    
        public String commonPrefix(String lcpLeft, String lcpRight) {
            int minLength = Math.min(lcpLeft.length(), lcpRight.length());       
            for (int i = 0; i < minLength; i++) {
                if (lcpLeft.charAt(i) != lcpRight.charAt(i)) {
                    return lcpLeft.substring(0, i);
                }
            }
            return lcpLeft.substring(0, minLength);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    二分查找

    在这里插入图片描述

    class Solution {
        public String longestCommonPrefix(String[] strs) {
            if (strs == null || strs.length == 0) {
                return "";
            }
            int minLength = Integer.MAX_VALUE;
            for (String str : strs) {
                minLength = Math.min(minLength, str.length());
            }
            int low = 0, high = minLength;
            while (low < high) {
                int mid = (high - low + 1) / 2 + low;
                if (isCommonPrefix(strs, mid)) {
                    low = mid;
                } else {
                    high = mid - 1;
                }
            }
            return strs[0].substring(0, low);
        }
    
        public boolean isCommonPrefix(String[] strs, int length) {
            String str0 = strs[0].substring(0, length);
            int count = strs.length;
            for (int i = 1; i < count; i++) {
                String str = strs[i];
                for (int j = 0; j < length; j++) {
                    if (str0.charAt(j) != str.charAt(j)) {
                        return false;
                    }
                }
            }
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    流程:

    1. 字符串数组:flowerflowflight
    2. 最小长度字符串为:flow,长度为:4
    3. Time#1
      1. low = 0
      2. mid = (high - low + 1) / 2 + low = (4 - 0 + 1) / 2 + 0 = 2
      3. high = 4
    4. 取字符串中第一个字符串的(0, mid) -> flower.substring(0, 2) = fl
    5. flflow & flight 的前缀对比,return true
    6. Time#2
      1. low = 2
      2. mid = (high - low + 1) / 2 + low = (4 - 2 + 1) / 2 + 2 = 3
      3. high = 4
    7. 取字符串中第一个字符串的(0, mid) -> flower.substring(0, 3) = flo
    8. floflow & flight 的前缀对比,return false
    9. high = mid - 1 = 3 - 1 = 2
    10. Time#3
      1. low < high = 2 < 2 = false
      2. 返回结果
      3. strs[0].substring(0, low) = flower.substring(0, 2) = fl

    参考

    LeetCode - 最长公共前缀

    21. 合并两个有序链表

    将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

    输入:1->2->4, 1->3->4
    输出:1->1->2->3->4->4

    个人思路

    看了一眼官方给出的两个思路:迭代 & 递归,一开始看题目的时候,大致思路是一致的,就是写代码的时候写的一塌糊涂。(吐了~菜是原罪)

    耗时:2.5h

    递归

    在这里插入图片描述

    class Solution {
        public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
            if (l1 == null) {
                return l2;
            } else if (l2 == null) {
                return l1;
            } else if (l1.val < l2.val) {
                l1.next = mergeTwoLists(l1.next, l2);
                return l1;
            } else {
                l2.next = mergeTwoLists(l1, l2.next);
                return l2;
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    迭代

    在这里插入图片描述

    class Solution {
        public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
            ListNode prehead = new ListNode(-1);
    
            ListNode prev = prehead;
            while (l1 != null && l2 != null) {
                if (l1.val <= l2.val) {
                    prev.next = l1;
                    l1 = l1.next;
                } else {
                    prev.next = l2;
                    l2 = l2.next;
                }
                prev = prev.next;
            }
    
            // 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
            prev.next = l1 == null ? l2 : l1;
    
            return prehead.next;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    参考

    合并两个有序链表

    26. 删除排序数组中的重复项

    给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
    不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

    示例 1:

    给定数组 nums = [1,1,2],
    函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
    你不需要考虑数组中超出新长度后面的元素。

    示例 2:

    给定 nums = [0,0,1,1,1,2,2,3,3,4],
    函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
    你不需要考虑数组中超出新长度后面的元素。

    说明:
    为什么返回数值是整数,但输出的答案是数组呢?
    请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
    你可以想象内部操作如下:

    // nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
    int len = removeDuplicates(nums);
    // 在函数里修改输入数组对于调用者是可见的。
    // 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
    for (int i = 0; i < len; i++) {
        print(nums[i]);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    个人思路

    1. 题目给出的是一个排序的数组,且要求不要使用额外的数组空间
    2. 将不相等的元素一个个的覆盖前面相同的元素
    class Solution {
        public int removeDuplicates(int[] nums) {
            if (nums.length < 2) {
                return 1;
            }
            int i = 0, j = 1;
            while (j < nums.length) {
                if (nums[i] == nums[j]) {
                    j++;
                } else {
                    nums[i + 1] = nums[j];
                    i++;
                    j++;
                }
            }
            return i + 1;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    参考

    【双指针】删除重复项-带优化思路

    27. 移除元素

    给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

    不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

    元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

    示例 1:

    给定 nums = [3,2,2,3], val = 3,

    函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。

    你不需要考虑数组中超出新长度后面的元素。

    示例 2:

    给定 nums = [0,1,2,2,3,0,4,2], val = 2,

    函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。

    注意这五个元素可为任意顺序。

    你不需要考虑数组中超出新长度后面的元素。

    说明:

    为什么返回数值是整数,但输出的答案是数组呢?

    请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

    你可以想象内部操作如下:

    // nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
    int len = removeElement(nums, val);
    
    // 在函数里修改输入数组对于调用者是可见的。
    // 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
    for (int i = 0; i < len; i++) {
        print(nums[i]);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    个人思路

    此题思路和【LeetCode刷题记录】 - 删除排序数组中的重复项是一样的。

    注意题目中的提醒:

    1. 原地移除
    2. 不要使用额外空间
    3. 元素顺序可以改变
    4. 不需要考虑数组中超出新长度后面的元素
    class Solution {
        public int removeElement(int[] nums, int val) {
            int i = 0, j = 0;
            while (j < nums.length) {
                if (nums[j] == val) {
                    j++;
                } else {
                    nums[i] = nums[j];
                    i++;
                    j++;
                }
            }
            return i;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    用的也是双指针的方式

    官方思路

    在这里插入图片描述

    参考

    移除元素

    28. 实现 strStr()

    实现 strStr() 函数。

    给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1

    示例 1:

    输入: haystack = “hello”, needle = “ll”
    输出: 2

    示例 2:

    输入: haystack = “aaaaa”, needle = “bba”
    输出: -1

    说明:

    needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。

    对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。

    个人思路

    1. 如果needle为空,则返回 0
    2. 如果haystackneedle 长度相等,且字符串相等,则返回0
    3. 如果haystack的长度小于needle,则返回-2
    4. 如果haystackneedle 长度相等,但是字符串不相等,则返回-2
    5. For循环,不断从haystack中截取与needle长度一致的字符串进行比较。得到我们想要的结果
    class Solution {
        public int strStr(String haystack, String needle) {
            if (needle.length() == 0 || (haystack.length() == needle.length() && needle.equals(haystack))) {
                return 0;
            }
            if (haystack.length() < needle.length() || (haystack.length() == needle.length() && !needle.equals(haystack))) {
                return -1;
            }
            for (int i = 0, j = needle.length(); j <= haystack.length(); i++, j++) {
                if (haystack.substring(i, j).equals(needle)) {
                    return i;
                }
            }
            return -1;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    结果:成功

    官方代码 - 子串逐一比较 - 线性时间复杂度

    fig

    class Solution {
      public int strStr(String haystack, String needle) {
        int L = needle.length(), n = haystack.length();
    
        for (int start = 0; start < n - L + 1; ++start) {
          if (haystack.substring(start, start + L).equals(needle)) {
            return start;
          }
        }
        return -1;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    ★官方代码 - 双指针 - 线性时间复杂度

    上一个方法的缺陷是会将 haystack 所有长度为 L 的子串都与 needle 字符串比较,实际上是不需要这么做的。

    首先,只有子串的第一个字符跟 needle 字符串第一个字符相同的时候才需要比较。

    fig

    其次,可以一个字符一个字符比较,一旦不匹配了就立刻终止。

    fig

    如下图所示,比较到最后一位时发现不匹配,这时候开始回溯。需要注意的是,pn 指针是移动到 pn = pn - curr_len + 1 的位置,而 不是 pn = pn - curr_len 的位置。

    fig

    这时候再比较一次,就找到了完整匹配的子串,直接返回子串的开始位置 pn - L。

    fig

    class Solution {
      public int strStr(String haystack, String needle) {
        int L = needle.length(), n = haystack.length();
        if (L == 0) return 0;
    
        int pn = 0;
        while (pn < n - L + 1) {
          // find the position of the first needle character
          // in the haystack string
          while (pn < n - L + 1 && haystack.charAt(pn) != needle.charAt(0)) ++pn;
    
          // compute the max match string
          int currLen = 0, pL = 0;
          while (pL < L && pn < n && haystack.charAt(pn) == needle.charAt(pL)) {
            ++pn;
            ++pL;
            ++currLen;
          }
    
          // if the whole needle string is found,
          // return its start position
          if (currLen == L) return pn - L;
    
          // otherwise, backtrack
          pn = pn - currLen + 1;
        }
        return -1;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    参考

    实现 strStr()

    35. 搜索插入位置

    给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

    你可以假设数组中无重复元素。

    示例 1:

    输入: [1,3,5,6], 5
    输出: 2

    示例 2:

    输入: [1,3,5,6], 2
    输出: 1

    示例 3:

    输入: [1,3,5,6], 7
    输出: 4

    示例 4:

    输入: [1,3,5,6], 0
    输出: 0

    个人思路1

    整个数组遍历一次,遇到小于等于某个值时,返回该值下标。

    class Solution {
        public int searchInsert(int[] nums, int target) {
            for (int i = 0; i < nums.length; i++) {
                if (target <= nums[i]) {
                    return i;
                }
            }
            return nums.length;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    个人思路2:二分查找

    class Solution {
        public int searchInsert(int[] nums, int target) {
            int min = 0;
            int max = nums.length;
            while (min < max) {
                int middle = (max + min) / 2;
                if (nums[middle] >= target) {
                    max = middle;
                } else {
                    min = middle + 1;
                }
            }
            return min;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    69. x 的平方根

    实现 int sqrt(int x) 函数。
    计算并返回 x 的平方根,其中 x 是非负整数。
    由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

    示例 1:
    输入: 4
    输出: 2

    示例 2:
    输入: 8
    输出: 2
    说明: 8 的平方根是 2.82842…,
    由于返回类型是整数,小数部分将被舍去。

    代码

    class Solution {
        public int mySqrt(int x) {
            int min = 0;
            int max = x;
            if (x == 1) {
                return 1;
            }
            while (max - min > 1) {
                int mid = (max - min) / 2 + min;
                if (x / mid >= mid) {
                    min = mid;
                } else {
                    max = mid;
                }
            }
            return min;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    167. 两数之和 II - 输入有序数组

    给定一个已按照升序排列有序数组,找到两个数使得它们相加之和等于目标数。
    函数应该返回这两个下标值 index1 index2,其中 index1 必须小于 index2

    说明:
    返回的下标值(index1 和 index2)不是从零开始的。
    你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。

    示例:
    输入: numbers = [2, 7, 11, 15], target = 9
    输出: [1,2]
    解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

    代码

    class Solution {
        public int[] twoSum(int[] numbers, int target) {
            for (int i = 0; i < numbers.length; i++) {
                int min = i + 1;
                int max = numbers.length - 1;
                while (min <= max) {
                    int middle = (max - min) / 2 + min;
                    if (numbers[i] + numbers[middle] == target) {
                        return new int[]{i + 1, middle + 1};
                    } else if (numbers[i] + numbers[middle] > target) {
                        max = middle - 1;
                    } else {
                        min = middle + 1;
                    }
                }
            }
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    第一个错误的版本

    你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。

    假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。

    你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。

    示例:

    给定 n = 5,并且 version = 4 是第一个错误的版本。

    调用 isBadVersion(3) -> false
    调用 isBadVersion(5) -> true
    调用 isBadVersion(4) -> true

    所以,4 是第一个错误的版本。

    代码

    public class Solution extends VersionControl {
        public int firstBadVersion(int n) {
            int left = 1;
            int right = n;
            while (left < right) {
                int mid = left + (right - left) / 2;
                if (isBadVersion(mid)) {
                    right = mid;
                } else {
                    left = mid + 1;
                }
            }
            return left;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    53. 最大子序和

    给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

    示例:

    输入: [-2,1,-3,4,-1,2,1,-5,4]
    输出: 6
    解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

    进阶:

    如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

    个人思路

    暴力破解,用Set记录下每一次计算的结果,最后返回最大值

    class Solution {
        public int maxSubArray(int[] nums) {
            Set<Integer> resultSet = new HashSet<>();
            for (int i = 0; i < nums.length; i++) {
                int sum = 0;
                for (int j = i; j < nums.length; j++) {
                    sum += nums[j];
                    resultSet.add(sum);
                }
                resultSet.add(nums[i]);
            }
            return resultSet.stream().max((x, y) -> x > y ? 1 : -1).orElse(0);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    结果:失败,超出时间限制

    官网思路

    • 动态规划的是首先对数组进行遍历,当前最大连续子序列和为 sum,结果为 ans
    • 如果 sum > 0,则说明 sum 对结果有增益效果,则 sum 保留并加上当前遍历数字
    • 如果 sum <= 0,则说明 sum 对结果无增益效果,需要舍弃,则 sum 直接更新为当前遍历数字
    • 每次比较 sum ans的大小,将最大值置为ans,遍历结束返回结果
    • 时间复杂度:O(n)
    class Solution {
        public int maxSubArray(int[] nums) {
            int ans = nums[0];
            int sum = 0;
            for(int num: nums) {
                if(sum > 0) {
                    sum += num;
                } else {
                    sum = num;
                }
                ans = Math.max(ans, sum);
            }
            return ans;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    参考

    画解算法:53. 最大子序和

    66. 加一

    给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。

    最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

    你可以假设除了整数 0 之外,这个整数不会以零开头。

    示例 1:

    输入:digits = [1,2,3]
    输出:[1,2,4]
    解释:输入数组表示数字 123。

    示例 2:

    输入:digits = [4,3,2,1]
    输出:[4,3,2,2]
    解释:输入数组表示数字 4321。

    示例 3:

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

    提示:

    1 <= digits.length <= 100
    0 <= digits[i] <= 9

    个人思路1

    1. 将数组转成字符串
    2. 将String转换成Long
    3. 再将Long转换成数组
    class Solution {
        public int[] plusOne(int[] digits) {
            StringBuilder stringBuilder = new StringBuilder();
            for (int digit : digits) {
                stringBuilder.append(digit);
            }
            Long digit = Long.valueOf(stringBuilder.toString());
            digit += 1;
    
            String stringResult = String.valueOf(digit);
            int length = stringResult.length();
            int[] result = new int[length];
    
            for (int i = 0; i < length; i++) {
                result[i] = Integer.valueOf(String.valueOf(stringResult.charAt(i)));
            }
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    结果:失败

    在这里插入图片描述

    个人思路2

    1. 从后往前取值
    2. 遇到值不等于9,则加一,返回。
    3. 遇到值等于9,则改此值
    class Solution {
        public int[] plusOne(int[] digits) {
            for (int i = digits.length - 1; i >= 0; i--) {
            	// 不等于9,加一返回
                if (digits[i] != 9) {
                    digits[i] += 1;
                    return digits;
                }
                // 等于9,修改为0
                digits[i] = 0;
            }
            // 给的数组中所有下标对应的值都为9,则初始化新的数组,首位为1,其余皆为0
            int[] result = new int[digits.length + 1];
            result[0] = 1;
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    参考

    LeetCode - 加一

    38. 外观数列

    给定一个正整数 n ,输出外观数列的第 n 项。

    「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述

    你可以将其视作是由递归公式定义的数字字符串序列:

    • countAndSay(1) = “1”
    • countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。

    前五项如下:

    1. 1
    2. 11
    3. 21
    4. 1211
    5. 111221

    第一项是数字 1
    描述前一项,这个数是 1 即 “ 一 个 1 ”,记作 “11”
    描述前一项,这个数是 11 即 “ 二 个 1 ” ,记作 “21”
    描述前一项,这个数是 21 即 “ 一 个 2 + 一 个 1 ” ,记作 “1211”
    描述前一项,这个数是 1211 即 “ 一 个 1 + 一 个 2 + 二 个 1 ” ,记作 “111221”

    描述 一个数字字符串,首先要将字符串分割为 最小 数量的组,每个组都由连续的最多 相同字符 组成。然后对于每个组,先描述字符的数量,然后描述字符,形成一个描述组。要将描述转换为数字字符串,先将每组中的字符数量用数字替换,再将所有描述组连接起来。

    例如,数字字符串 "3322251" 的描述如下图:
    在这里插入图片描述

    示例 1:

    输入:n = 1
    输出:“1”
    解释:这是一个基本样例。

    示例 2:

    输入:n = 4
    输出:“1211”
    解释:
    countAndSay(1) = “1”
    countAndSay(2) = 读 “1” = 一 个 1 = “11”
    countAndSay(3) = 读 “11” = 二 个 1 = “21”
    countAndSay(4) = 读 “21” = 一 个 2 + 一 个 1 = “12” + “11” = “1211”

    提示:

    • 1 <= n <= 30

    个人思路:循环

    主要是记住同样的数字出现的次数,如:21 之后的数:

    1. 数字2 出现了 1次,记为:12
    2. 数字1 出现了 1次,记为:11
    3. 将两者拼接起来为:1211
    class Solution {
        public String countAndSay(int n) {
            String preResult = "1";
            if (n == 1) {
                return preResult;
            }
    
            StringBuilder result = new StringBuilder();
    
            for (int i = 2; i <= n; i++) {
                int count = 0;
                char target = preResult.charAt(0);
                result = new StringBuilder();
                for (int j = 0; j < preResult.length(); j++) {
                    if (target != preResult.charAt(j)) {
                        result.append(count);
                        result.append(target);
    
                        target = preResult.charAt(j);
                        count = 1;
                    } else {
                        count++;
                    }
                    if (j + 1 == preResult.length()) {
                        result.append(count);
                        result.append(target);
                    }
                }
                preResult = result.toString();
            }
            return result.toString();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    上述代码优化一下

    class Solution {
        public String countAndSay(int n) {
            String preResult = "1";
            if (n == 1) {
                return preResult;
            }
    
            for (int i = 2; i <= n; i++) {
                int count = 1;
                char target = preResult.charAt(0);
                StringBuilder result = new StringBuilder();
                for (int j = 1; j < preResult.length(); j++) {
                    if (target == preResult.charAt(j)) {
                        count++;
                    } else {
                        result.append(count);
                        result.append(target);
    
                        target = preResult.charAt(j);
                        count = 1;
                    }
                }
                result.append(count);
                result.append(target);
                preResult = result.toString();
            }
            return preResult;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    其他思路:递归

    比如 n=6时,那么用递归得到上一层的字符串str=“111221”
    我们将start指向下标0,我们从下标1开始遍历,遍历到“2”下标3的时候,sb拼上(3-0)个1即sb.append(3).append(1),将start指针指向下标3,接着重复以上操作,直到到达str的最后一位,sb直接拼上即可。

    class Solution {
        public String countAndSay(int n) {
            // 递归终止条件
            if (n == 1) {
                return "1";
            }
            StringBuffer res = new StringBuffer();
            // 拿到上一层的字符串
            String str = countAndSay(n - 1);
            int length = str.length();
            // 开始指针为0
            int start = 0;
            // 注意这从起始条件要和下面长度统一
            for (int i = 1; i < length + 1; i++) {
                // 字符串最后一位直接拼接
                if (i == length) {
                    res.append(i - start).append(str.charAt(start));
                // 直到start位的字符串和i位的字符串不同,拼接并更新start位
                } else if (str.charAt(i) != str.charAt(start) ) {
                    res.append(i - start).append(str.charAt(start));
                    start = i;
                }
            }
            return res.toString();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    参考

    ruo-guang - 外观数列

    414. 第三大的数

    给你一个非空数组,返回此数组中 第三大的数 。如果不存在,则返回数组中最大的数。
    示例1:

    输入:[3, 2, 1]
    输出:1
    解释:第三大的数是 1 。

    示例2:

    输入:[1, 2]
    输出:2
    解释:第三大的数不存在, 所以返回最大的数 2 。

    示例3:

    输入:[2, 2, 3, 1]
    输出:1
    解释:注意,要求返回第三大的数,是指在所有不同数字中排第三大的数。
    此例中存在两个值为 2 的数,它们都排第二。在所有不同数字中排第三大的数为 1 。

    题解一

    使用TreeSet排序 + 去重,如果最后得到的集合大小 <= 2,则取最大值(treeSet.last())。否则,取第三大的值。

        public int thirdMax(int[] nums) {
            TreeSet<Integer> treeSet = new TreeSet<>();
    
            for (int num : nums) {
                treeSet.add(num);
            }
    
            if (treeSet.size() <= 2) {
                return treeSet.last();
            }
    
            treeSet.pollLast();
            treeSet.pollLast();
            return treeSet.last();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    题解二

    还是使用TreeSet排序 + 去重,不过保持集合的大小不超过3,如果超过3,则remove最小值。
    根据最后得到的集合大小判断,如果最后得到的集合大小 <= 2,则取集合的最大值(treeSet.last())。否则,取集合的最小值(treeSet.first())

        public int thirdMax(int[] nums) {
            TreeSet<Integer> treeSet = new TreeSet<>();
    
            for (int num : nums) {
                treeSet.add(num);
                if (treeSet.size() > 3) {
                    // 拿到最小的值,删除
                    treeSet.remove(treeSet.pollFirst());
                }
            }
    
            return treeSet.size() >= 3 ? treeSet.first() : treeSet.last();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    题解三

    不需要排序 + 一次遍历

        public int thirdMax3(int[] nums) {
            Integer a = null;
            Integer b = null;
            Integer c = null;
    
            for (int num : nums) {
                if (a == null || num > a) {
                    c = b;
                    b = a;
                    a = num;
                    continue;
                }
    
                if ((b == null || num > b) && a > num) {
                    c = b;
                    b = num;
                    continue;
                }
    
                if ((c == null || num > c) && (b != null && b > num)) {
                    c = num;
                }
            }
            return c == null ? a : c;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    11. 盛最多水的容器

    给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
    找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
    返回容器可以储存的最大水量。

    说明:你不能倾斜容器。
    示例 1:

    输入:[1,8,6,2,5,4,8,3,7]
    输出:49
    解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

    示例 2:

    输入:height = [1,1]
    输出:1

    提示:

    • n == height.length
    • 2 <= n <= 105
    • 0 <= height[i] <= 104

    题解一:双循环(暴力破解)

    // 暴力破解,双循环,超出时间限制
    public int maxArea(int[] height) {
        int maxArea = 0;
        for (int i = 0; i < height.length - 1; i++) {
            for (int j = i + 1; j < height.length; j++) {
                // 求出当前两个距离的面积
                int currentArea = Math.min(height[i], height[j]) * (j - i);
                // 取得最大面积
                maxArea = Math.max(maxArea, currentArea);
            }
        }
        return maxArea;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    题解二:双指针

    // 双指针
    public int maxArea(int[] height) {
        int maxArea = 0;
        int left = 0;
        int right = height.length - 1;
        while (left < right) {
            // 求出当前两个距离的面积
            int currentArea = Math.min(height[left], height[right]) * (right - left);
            // 取得最大面积
            maxArea = Math.max(maxArea, currentArea);
            if (height[left] < height[right]) {
                left++;
            } else {
                right++;
            }
        }
        return maxArea;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 相关阅读:
    数据库事务——快照读与当前读
    解决问题:There is no tracking information for the current branch
    数学建模--决策树的预测模型的Python实现
    JAVA毕业设计家乡旅游文化推广网站计算机源码+lw文档+系统+调试部署+数据库
    JUC并发编程系列详解篇十五(公平锁VS非公平锁)
    uniapp 获取页面来源
    SpringCloud面试题及答案 300道,springcloud面试题总结 (持续更新)
    vue2_路由04_编程式路由导航push与replace
    Android 中的 线程池
    uni-app使用uView库的格式化时间API
  • 原文地址:https://blog.csdn.net/weixin_41915314/article/details/125571735