LeetCode 热题 HOT 100之JAVA解法
- 第1题 两数之和(简单)
- 第2题 两数相加(中等)
- 第3题 无重复字符的最长子串(中等)
- 第4题 寻找两个正序数组的中位数(困难)
- 第5题 最长回文子串(中等)
- 第10题 正则表达式匹配(困难)
- 第11题 盛最多水的容器(中等)
- 第15题 三数之和(中等)
- 第17题 电话号码的字母组合(中等)
- 第19题 删除链表的倒数第 N 个结点(中等)
- 第20题 有效的括号(简单)
- 第21题 合并两个有序链表(简单)
- 第22题 括号生成(中等)
- 第23题 合并K个升序链表(困难)
- 第31题 下一个排列(中等)
- 第32题 最长有效括号(困难)
- 第33题 搜索旋转排序数组(中等)
- 第34题 在排序数组中查找元素的第一个和最后一个位置(中等)
- 第39题 组合总和(中等)
- 第42题 接雨水(困难)
- 第46题 全排列(中等)
- 第49题 字母异位词分组(中等)
- 第53题 最大子序和(简单)
- 第55题 跳跃游戏(中等)
- 第56题 合并区间(中等)
- 第62题 不同路径(中等)
- 第64题 最小路径和(中等)
- 第70题 爬楼梯(简单)
- 第72题 编辑距离(困难)
- 第75题 颜色分类(中等)
- 第76题 最小覆盖子串(困难)
- 第78题 子集(中等)
- 第79题 单词搜索(中等)
- 第84题 柱状图中最大的矩形(困难)
- 第85题 最大矩形(困难)
- 第94题 二叉树的中序遍历(简单)
- 第96题 不同的二叉搜索树(中等)
- 第98题 验证二叉搜索树(中等)
- 第101题 对称二叉树(简单)
- 第102题 二叉树的层序遍历(中等)
- 第104题 二叉树的最大深度(简单)
第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, a1,a2,...,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