Bootstrap

LeetCode 热题 HOT 100

LeetCode 热题 HOT 100之JAVA解法

第1题 两数之和(简单)

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。

示例 1:

输入 输出
nums = [2,7,11,15], target = 9 [0,1]

解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入 输出
nums = [3,2,4], target = 6 [1,2]

示例 3:

输入 输出
nums = [3,3], target = 6 [0,1]

代码

// 两数之和:哈希表
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);//如果哈希表中不存在,则将当前数以及下标分别存到哈希表的Key和Value上
        }
        return new int[0];//如果遍历完数组,还是没找到结果,则返回空数组
    }
}

时间复杂度为O(n),n为数组的长度,遍历数组耗费O(n)的时间
空间复杂度为O(n),哈希表最大存储空间

第2题 两数相加(中等)

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1:
在这里插入图片描述

输入 输出
l1 = [2,4,3], l2 = [5,6,4] [7,0,8]

解释:342 + 465 = 807.

示例 2:

输入 输出
l1 = [0], l2 = [0] [0]

示例 3:

输入 输出
l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9] [8,9,9,9,0,0,0,1]

代码

// 两数相加:链表
class Solution {
   
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
   
        ListNode pre = new ListNode(0);//建立新链表
        ListNode cur = pre;//当前节点
        int carry=0;//进位值
        while(l1 != null || l2 != null){
   //判断是否遍历完两个链表
            int x = l1 != null ? l1.val : 0;//l1未遍历完,则取它的值
            int y = l2 != null ? l2.val : 0;//同理
            int sum = x + y + carry;//两个链表的节点值加进位值
            carry = sum / 10;//新的进位值
            sum %= 10;//新链表的节点值
            cur.next = new ListNode(sum);//将节点值插入新链表
            cur = cur.next;//指针指向下一位置
            if(l1 != null)//判断是否遍历完l1
                l1 = l1.next;
            if(l2 != null)//同理
                l2 = l2.next;
        }
        if(carry != 0){
   //如果遍历完了两个链表,还存在进位,则将进位值加入新链表
            cur.next = new ListNode(carry);
        }
        return pre.next;
    }
}

时间复杂度为O(max(m,n)),m,n为两个链表的长度
空间复杂度为O(1)

第3题 无重复字符的最长子串(中等)

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入 输出
s = “abcabcbb” 3

解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。

示例 2:

输入 输出
s = “bbbbb” 1

解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。

示例 3:

输入 输出
s = “pwwkew” 3

解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

示例 4:

输入 输出
s = “” 0

代码

// 无重复字符的最长子串:滑动窗口
class Solution {
   
    public int lengthOfLongestSubstring(String s) {
   
        Set<Character> occ = new HashSet<Character>();//建立哈希集合
        int n = s.length();        
        int rk = -1, ans = 0;// 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
        for (int i = 0; i < n; ++i) {
   
            if (i != 0) {
                   
                occ.remove(s.charAt(i - 1));// 左指针向右移动一格,移除一个字符
            }
            while (rk + 1 < n && !occ.contains(s.charAt(rk + 1))) {
   //哈希集合中不存在当前字符                
                occ.add(s.charAt(rk + 1));// 不断地移动右指针
                ++rk;
            }
            ans = Math.max(ans, rk - i + 1);//如果遇到重复字符,则先退出去比较长度
        }
        return ans;
    }
}

时间复杂度为O(n),n为字符串的长度
空间复杂度为O(∣Σ∣),Σ为字符集的大小,哈希集合存储大小

第4题 寻找两个正序数组的中位数(困难)

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。

示例 1:

输入 输出
nums1 = [1,3], nums2 = [2] 2.00000

解释:合并数组 = [1,2,3] ,中位数 2

示例 2:

输入 输出
nums1 = [1,2], nums2 = [3,4] 2.50000

解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

示例 3:

输入 输出
nums1 = [0,0], nums2 = [0,0] 0.00000

示例 4:

输入 输出
nums1 = [], nums2 = [1] 1.00000

示例 5:

输入 输出
nums1 = [2], nums2 = [] 2.00000

代码

// 寻找两个正序数组的中位数:二分查找
class Solution {
   
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
   //寻找中位数
        int length1 = nums1.length, length2 = nums2.length;
        int totalLength = length1 + length2;
        if (totalLength % 2 == 1) {
   //两个数组相加为奇数
            int midIndex = totalLength / 2;//两个数组元素个数的一半
            double median = getKthElement(nums1, nums2, midIndex + 1);
            return median;
        } else {
   //为偶数时
            int midIndex1 = totalLength / 2 - 1, midIndex2 = totalLength / 2;
            double median = (getKthElement(nums1, nums2, midIndex1 + 1) + getKthElement(nums1, nums2, midIndex2 + 1)) / 2.0;//求两个相邻元素和的一半
            return median;
        }
    }
    public int getKthElement(int[] nums1, int[] nums2, int k) {
   //得到第k个元素
        int length1 = nums1.length, length2 = nums2.length;
        int index1 = 0, index2 = 0;
        int kthElement = 0;
        while (true) {
   
            if (index1 == length1) {
   //数组1为空
                return nums2[index2 + k - 1];
            }
            if (index2 == length2) {
   //数组2为空
                return nums1[index1 + k - 1];
            }
            if (k == 1) {
   //中间数为1,则返回最小的数组中的值
                return Math.min(nums1[index1], nums2[index2]);
            }            
            int half = k / 2;
            int newIndex1 = Math.min(index1 + half, length1) - 1;
            int newIndex2 = Math.min(index2 + half, length2) - 1;//找到各自矩阵的最小值
            int pivot1 = nums1[newIndex1], pivot2 = nums2[newIndex2];
            if (pivot1 <= pivot2) {
   //比较两个数组当前值(从最小值开始比较)
                k -= (newIndex1 - index1 + 1);//如果数组二大。则k减去索引
                index1 = newIndex1 + 1;//变更数组1为下一个值
            } else {
   //同理,直到k为0
                k -= (newIndex2 - index2 + 1);
                index2 = newIndex2 + 1;
            }
        }
    }
}

时间复杂度为O(log(m+n)),m和n为数组nums1和nums2的长度
空间复杂度为O(1)

第5题 最长回文子串(中等)

给你一个字符串 s,找到 s 中最长的回文子串。

示例 1:

输入 输出
s = “babad” “bab”

示例 2:

输入 输出
s = “cbbd” “bb”

示例 3:

输入 输出
s = “a” “a”

示例 4:

输入 输出
s = “ac” “a”

代码

// 最长回文子串:双指针
class Solution {
   
    public String longestPalindrome(String s) {
   
        if (s == null || s.length() < 1) {
   
            return "";
        }
        int start = 0, end = 0;
        for (int i = 0; i < s.length(); i++) {
   
            int len1 = expandAroundCenter(s, i, i);//中间有单个字符的回文串
            int len2 = expandAroundCenter(s, i, i + 1);//中间无单个字符的回文串
            int len = Math.max(len1, len2);//找到最大长度
            if (len > end - start) {
   //如果最大长度比字符串首尾位置差还大
                start = i - (len - 1) / 2;//则起点为首位
                end = i + len / 2;//终点位末位
            }
        }
        return s.substring(start, end + 1);//将从起点到结尾的字符输出
    }
    public int expandAroundCenter(String s, int left, int right) {
   //中心扩展
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
   //如果左右指针在字符串内且左右指针所指的值一样
            --left;
            ++right;//则左指针左移,右指针右移
        }
        return right - left - 1;//直到左右出范围或者左右指针所指字符不等,则返回左右指针之间的距离
    }
}

时间复杂度为O( n 2 n^2 n2),n为字符串的长度
空间复杂度为O(1)

第10题 正则表达式匹配(困难)

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。
‘.’ 匹配任意单个字符
’ * ’ 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

示例 1:

输入 输出
s = “aa” p = “a” false

解释:“a” 无法匹配 “aa” 整个字符串。

示例 2:

输入 输出
输入:s = “ab” p = “.*” true

示例 3:

输入 输出
s = “aa” p = “a*” true

解释:因为 ‘*’ 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 ‘a’。因此,字符串 “aa” 可被视为 ‘a’ 重复了一次。

示例 4:

输入 输出
输入:s = “aab” p = “cab” true

解释:因为 ‘*’ 表示零个或多个,这里 ‘c’ 为 0 个, ‘a’ 被重复一次。因此可以匹配字符串 “aab”。

示例 5:

输入 输出
s = “mississippi” p = “misisp*.” false

代码

// 正则表达式匹配:动态规划
class Solution {
   
    public boolean isMatch(String s, String p) {
   
        int m = s.length();
        int n = p.length();
        boolean[][] f = new boolean[m + 1][n + 1];
        f[0][0] = true;
        for (int i = 0; i <= m; ++i) {
   
            for (int j = 1; j <= n; ++j) {
   
                if (p.charAt(j - 1) == '*') {
   //如果p中第j-1个字符是*,则可以匹配多次
                    f[i][j] = f[i][j - 2];//对p中第j-1个字符匹配多次,匹配0次的情况
                    if (matches(s, p, i, j - 1)) {
   //判断匹配
                        f[i][j] = f[i][j] || f[i - 1][j];//如果符合,则字符串s中前i个字符和字符串p中前j个字符是否匹配取决于前一个i和当前的i是否匹配
                    }
                } else {
   
                    if (matches(s, p, i, j)) {
   //如果p的第j个字符是一个小写字母
                        f[i][j] = f[i - 1][j - 1];//则是否匹配取决于i-1和j-1
                    }
                }
            }
        }
        return f[m][n];
    }
    public boolean matches(String s, String p, int i, int j) {
   //匹配判断
        if (i == 0) {
   
            return false;
        }
        if (p.charAt(j - 1) == '.') {
   //出现‘.’代表匹配
            return true;
        }
        return s.charAt(i - 1) == p.charAt(j - 1);//字符是否匹配
    }
}

时间复杂度为O(mn),m和n分别为字符串s和p的长度
空间复杂度为O(mn)

第11题 盛最多水的容器(中等)

给你 n 个非负整数 a 1 , a 2 , . . . , a n , a_1,a_2,...,a_n, a1a2...an每个数代表坐标中的一个点 (i, a i a_i ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, a i a_i ai) 和 (i, 0) 。找出其中的两条线,使得它们与 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

示例 3:

输入 输出
height = [4,3,2,1,4] 16

示例 4:

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

代码

// 盛最多水的容器:双指针
public class Solution {
   
    public int maxArea(int[] height) {
   
        int l = 0, r = height.length - 1;//定义双指针,指向首尾
        int ans = 0;
        while (l < r) {
   
            int area = Math.min(height[l], height[r]) * (r - l);//比较高度,找出左右边界最低的高度,然后乘上左右边界之间的距离
            ans = Math.max(ans, area);
            if (height[l] <= height[r]) {
   //比较左右边界高度
                ++l;//左边低,左指针右移
            }
            else {
   
                --r;//右边低,右指针左移
            }
        }
        return ans;
    }
}

时间复杂度为O(n),n为数组的长度
空间复杂度为O(1)

第15题 三数之和(中等)

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。

示例 1:

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

示例 2:

输入 输出
nums = [] []

示例 3:

输入 输出
nums = [0] []

代码

// 三数之和:排序 + 双指针
class Solution {
   
    public List<List<Integer>> threeSum(int[] nums) {
   
        int n = nums.length;
        Arrays.sort(nums);//排序
        List<List<Integer>> ans = new ArrayList<List<Integer>>();
        for (int first = 0; first < n; ++first) {
   //第一个数
            if (first > 0 && nums[first] == nums[first - 1]) {
   //当first大于0且相邻两个数相等时,跳过
                continue;
            }
            int third = n - 1;//第三个数
            int target = -nums[first];//目标值
            for (int second = first + 1; second < n; ++second) {
   //第二个数在第一个数之后
                if (second > first + 1 && nums[second] == nums[second - 1]) {
   //如果第二个数在第一个数后一个位置以后,且相邻两个数相等时,跳过
                    continue;
                }
                while (second < third && nums[second] + nums[third] > target) {
   //如果第二个数加第三个数大于目标值,则第三个数右移
                    --third;
                }
                if (second == third) {
   //如果第二个数等于第三个数,则终止本层循环
                    break;
                }
                if (nums[second] + nums[third] == target) {
   //如果第二个数和第三个数相加等于目标值,则输出结果
                    List<Integer> list = new ArrayList<Integer>();
                    list.add(nums[first]);
                    list.add(nums[second]);
                    list.add(nums[third]);
                    ans.add(list);
                }
            }
        }
        return ans;
    }
}

时间复杂度为O( N 2 N^2 N2),n为数组的长度
空间复杂度为O( log ⁡ N

;