热题top100
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]
提示:
2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
只会存在一个有效答案
public static int[] twoSum(int[] nums, int target) {
// 将元数据存储到 map 中 key:元数据值,value:元数据数组下标
// 再次遍历元数据数组 找到 map-key = target - cur-key
Map<Integer, List<Integer>> dataMap = new HashMap<>(nums.length);
for (int i = 0; i < nums.length; i++) {
if (dataMap.containsKey(nums[i])) {
List<Integer> indexList = dataMap.get(nums[i]);
indexList.add(i);
} else {
List<Integer> indexList = new ArrayList<>();
indexList.add(i);
dataMap.put(nums[i], indexList);
}
}
for (int i = 0; i < nums.length; i++) {
Integer search = target - nums[i];
if (dataMap.containsKey(search)) {
List<Integer> list = dataMap.get(search);
for (Integer index : list) {
if (index != i) {
return new int[]{i, index};
}
}
}
}
return new int[]{};
}
刷题记录:
- 2022-03-20 二刷(一遍过)
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]
提示:
每个链表中的节点数在范围 [1, 100] 内
0 <= Node.val <= 9
题目数据保证列表表示的数字不含前导零
Java代码
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode first = l1;
ListNode second = l2;
ListNode ans = new ListNode();
ListNode result = ans;
int multi = 0;
while (first != null || second != null) {
if (first == null) {
int sum = second.val + multi;
multi = sum / 10;
result.next = new ListNode(sum % 10);
result = result.next;
second = second.next;
continue;
}
if (second == null) {
int sum = first.val + multi;
multi = sum / 10;
result.next = new ListNode(sum % 10);
result = result.next;
first = first.next;
continue;
}
int sum = first.val + second.val + multi;
multi = sum / 10;
result.next = new ListNode(sum % 10);
result = result.next;
first = first.next;
second = second.next;
}
if (multi != 0){
result.next = new ListNode(multi);
}
return ans.next;
}
刷题记录
- 2022-03-20 二刷 一遍过,没看题解
3.无重复字符的最长字串
原题地址
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成
public static int lengthOfLongestSubstring(String s) {
Map<Character, Integer> dataMap = new HashMap<>();
char[] chars = s.toCharArray();
// 循环下标开始
int search = 0;
int begin = 0;
int end = 0;
int ans = 0;
while (search < chars.length) {
if (dataMap.containsKey(chars[search])) {
ans = Math.max(end - begin, ans);
search = dataMap.get(chars[search]) + 1;
end = search;
begin = search;
dataMap.clear();
continue;
}
dataMap.put(chars[search], search);
search++;
end++;
}
ans = Math.max(end - begin, ans);
return ans;
}
刷题记录
- 2022-03-20 二刷 (没看题解)
4.寻找两个正序数组的中位数
原题地址
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
示例 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
提示:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106
package xyz.fudongyang.review;
import java.util.PriorityQueue;
/**
* @author: fudy
* @date: 2022/3/20 上午 10:59
* @Decription:
**/
public class Lc_4 {
public static void main(String[] args) {
int[] nums1 = {0,0,0,0,0};
// int[] nums1 = {1, 3};
int[] nums2 = {-1,0,0,0,0,0,1};
// int[] nums2 = {2};
System.out.println("findMedianSortedArrays(nums1, nums2) = " + findMedianSortedArrays(nums1, nums2));
}
public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
// 维护两个优先级队列 一个大顶堆 一个小顶堆
PriorityQueue<Integer> minQueue = new PriorityQueue<>(((o1, o2) -> o2 - o1));
PriorityQueue<Integer> maxQueue = new PriorityQueue<>();
// 放入数据 保证大顶堆 小顶堆的数量是一致的
addQueue(minQueue, maxQueue, nums1);
addQueue(minQueue, maxQueue, nums2);
// 如果哪个数据比较多 拿哪个堆顶 如果一样多 则取两个堆顶的中间值
if (maxQueue.size() == 0 && minQueue.size() == 0) {
return 0;
}
if (minQueue.size() == maxQueue.size()) {
return (double) (minQueue.peek() + maxQueue.peek()) / 2;
}
if (minQueue.size() > maxQueue.size()) {
return minQueue.peek();
} else {
return maxQueue.peek();
}
}
private static void addQueue(PriorityQueue<Integer> minQueue, PriorityQueue<Integer> maxQueue, int[] nums1) {
for (int i : nums1) {
if (minQueue.size() >= maxQueue.size()) {
minQueue.add(i);
maxQueue.add(minQueue.poll());
} else {
maxQueue.add(i);
minQueue.add(maxQueue.poll());
}
}
}
}
解题记录:
- 2022-03-20 二刷,还是没做出来 看题解做出来了,需要三刷
5.最长回文子串
原题地址
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd"
输出:"bb"
提示:
1 <= s.length <= 1000
s 仅由数字和英文字母组成
解题思路 都在代码注释里
public static String longestPalindrome(String s) {
// 如果长度为1 直接返回
int length = s.length();
if (length == 1) {
return s;
}
// 构建 dp 数组 数组的一维成为 i,二维称为j 代表着 i到j之间是不是回文子串 也就是 i 和 j代表着 字符在字符串的下标位置
boolean[][] dp = new boolean[length][length];
char[] chars = s.toCharArray();
// 记录回文串的最大长度 初始化为1 因为字符本身也是回文串
int maxLen = 1;
// 记录回文串最大长度的开始下标 ,为了方便截取结果
int beginIndex = 0;
// 1、显然 i=j 时,肯定是字符串 因为单个字符本身就是回文子串,于是我们可以构建 dp 数组如下
for (int i = 0; i < length; i++) {
dp[i][i] = true;
}
// 我们只需要考虑 i在左边 j在右边的情况 即 i <= j 的情况,因为i = j的情况 我们在1的时候已经考虑过了,只需要考虑 i < j的情况
for (int m = 1; m <= length; m++) {
for (int i = 0; i < length; i++) {
int j = i + m;
if (j >= length) {
break;
}
// 2.如果 i位置 和 j位置 不相等 则他们之间肯定不是回文子串
if (chars[i] != chars[j]) {
dp[i][j] = false;
} else {
// 3.如果i位置 和 j位置相等,且中间最多只隔了一个字符 则肯定时回文串
if (j - i <= 2) {
dp[i][j] = true;
} else {
// 4.如果 中间隔了好几个字符 需要判断这几个字符是否是回文串
dp[i][j] = dp[i + 1][j - 1];
}
}
// 如果是最大的回文子串 则记录开始位置 偏移量
if (dp[i][j] && j - i + 1 > maxLen) {
maxLen = j - i + 1;
beginIndex = i;
}
}
}
return s.substring(beginIndex, beginIndex + maxLen);
}
刷题记录:
- 2022-03-20,二刷,看了代码才做出来
10.正则表达式(不推荐做)
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。
‘.’ 匹配任意单个字符
‘*’ 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
示例 1:
输入:s = "aa", p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。
示例 2:
输入:s = "aa", p = "a*"
输出:true
解释:因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
示例 3:
输入:s = "ab", p = ".*"
输出:true
解释:".*" 表示可匹配零个或多个('*')任意字符('.')。
提示:
1 <= s.length <= 20
1 <= p.length <= 30
s 只包含从 a-z 的小写字母。
p 只包含从 a-z 的小写字母,以及字符 . 和 *。
保证每次出现字符 * 时,前面都匹配到有效的字符
Java代码
package xyz.fudongyang.classic;
/**
* @author: fudy
* @date: 2022/3/8 下午 10:23
* @Decription:
**/
public class Lc_10_test {
public static void main(String[] args) {
String s = "abba";
String p = "ab*a";
System.out.println(isMatch(s, p));
}
public static boolean isMatch(String s, String p) {
int m = s.length();
int n = p.length();
boolean[][] dp = new boolean[m + 1][n + 1];
dp[0][0] = true;
for (int i = 0; i < m + 1; i++) {
for (int j = 1; j < n + 1; j++) {
// 如果 p-1 等于 *
if (p.charAt(j - 1) == '*') {
// 判断 dp 等于 dp[i][j-2] 如果 p-1等于 j 则舍弃 不等于 则 丢掉 i-1
dp[i][j] = dp[i][j - 2];
if (match(s, p, i, j - 1)) {
dp[i][j] = dp[i][j] || dp[i - 1][j];
}
} else {
// P-1 不等于 * 进行 常规字符判断
if (match(s, p, i, j)) {
dp[i][j] = dp[i - 1][j - 1];
}
}
}
}
return dp[m][n];
}
private static boolean match(String s, String p, int m, int n) {
if (m == 0) {
return false;
}
if (p.charAt(n - 1) == '.') {
return true;
}
return s.charAt(m - 1) == p.charAt(n - 1);
}
}
解题记录:
- 2022.03.08 看了好久的题解 比较绕
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 class Lc_5 {
public static void main(String[] args) {
int[] arr = {1, 8, 6, 2, 5, 4, 8, 3, 7};
System.out.println(maxArea(arr));
}
public static int maxArea(int[] height) {
int l = 0, r = height.length - 1;
int ans = Integer.MIN_VALUE;
while (l < r) {
int areas = Math.min(height[l], height[r]) * (r - l);
ans = Math.max(ans, areas);
if (height[l] <= height[r]) {
++l;
} else {
--r;
}
}
return ans;
}
}
练习记录
- 第一次练习时间:2022.02.08 09:44 pm
- 第二次练习时间:2022-03-20 一遍过
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]
输出:[]
提示:
0 <= nums.length <= 3000
-105 <= nums[i] <= 105
Java代码
package xyz.fudongyang.classic;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author: fudy
* @date: 2022/2/28 下午 09:47
* @Decription:
**/
public class Lc_15 {
public static void main(String[] args) {
int[] arr = {-1, 0, 1, 2, -1, -4};
System.out.println(threeSum(arr));
}
public static List<List<Integer>> threeSum(int[] nums) {
int n = nums.length;
// 先给数组排序
Arrays.sort(nums);
List<List<Integer>> ans = new ArrayList<>();
for (int first = 0; first < n; first++) {
// 如果不是第一个 需要判断是否和前一个相同 如果相同则不需要重复枚举了
if (first > 0 && nums[first] == nums[first - 1]) {
continue;
}
// c的指针始终是数组的右边界
int third = n - 1;
// 这是目标值
int target = -nums[first];
for (int second = first + 1; second < n; second++) {
// 同样的 如果b枚举 也不能相同 相同就不需要重复枚举了
if (second > first + 1 && nums[second] == nums[second - 1]) {
continue;
}
// 右侧是最大值 如果b+c小于等于目标值 或者 两个坐标相撞了 就退出
while (second < third && nums[second] + nums[third] > target) {
--third;
}
// 坐标相撞情况
if (second == third) {
break;
}
// 目标值
if (nums[second] + nums[third] == target) {
List<Integer> data = new ArrayList<>(3);
data.add(nums[first]);
data.add(nums[second]);
data.add(nums[third]);
ans.add(data);
}
}
}
return ans;
}
}
- 第一次练习时间:2022/2/28 下午 09:47
- 第二次练习时间:2022-03-20
17.电话号码的字母组合
原题链接
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例 1:
输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:
输入:digits = ""
输出:[]
示例 3:
输入:digits = "2"
输出:["a","b","c"]
提示:
0 <= digits.length <= 4
digits[i] 是范围 ['2', '9'] 的一个数字。
Java代码
package xyz.fudongyang.classic;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author: fudy
* @date: 2022/3/1 下午 08:08
* @Decription:
**/
public class Lc_17 {
public static void main(String[] args) {
System.out.println(letterCombinations("23"));
}
private static List<String> letterCombinations(String digits) {
List<String> combinations = new ArrayList<>();
if (digits == null || digits.length() == 0){
return combinations;
}
// 封装元数据
final Map<Character, String> phoneMap = new HashMap<Character, String>() {{
put('2',"abc");
put('3',"def");
put('4',"hlj");
put('5',"abc");
put('6',"abc");
put('7',"abc");
put('8',"abc");
put('9',"abc");
}};
// 进行递归回溯
backtrack(combinations,phoneMap,digits,0,new StringBuffer(digits.length()));
return combinations;
}
private static void backtrack(List<String> combinations,Map<Character,String> phoneMap,String digits,int index,StringBuffer combination){
// 临界值
if (index == digits.length()){
combinations.add(combination.toString());
return;
}
// 递归循环 数字的位数
char digit = digits.charAt(index);
String letters = phoneMap.get(digit);
// for循环 数字对应的字符
for (int i = 0; i < letters.length(); i++) {
char c = letters.charAt(i);
combination.append(c);
backtrack(combinations,phoneMap,digits,index+1,combination);
combination.deleteCharAt(index);
}
}
}
- 一刷时间:2022.03.01
19.删除链表的倒数第N个结点
原题链接
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
输出:[]
示例 3:
输入:head = [1,2], n = 1
输出:[1]
提示:
链表中结点的数目为 sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
进阶:你能尝试使用一趟扫描实现吗?
Java代码(没看题解,自己一遍过,太帅了)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
if(head == null){
return head;
}
Map<Integer,ListNode> metaData = new HashMap<Integer,ListNode>();
ListNode data = head;
int count = 0;
while(data != null){
metaData.put(count++,data);
data = data.next;
}
int index = count - n;
if(index < 0){
return null;
}else if(index == 0){
// 如果删除头节点
return head.next;
}else{
// 如果删除中间节点
ListNode preData = metaData.get(--index);
if(index+2<count){
preData.next = metaData.get(index+2);
}else{
preData.next = null;
}
return head;
}
}
}
-2022.03.01(自己解题 一遍过 太开心啦)
21.合并两个有序的链表
原题链接
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = []
输出:[]
示例 3:
输入:l1 = [], l2 = [0]
输出:[0]
提示:
两个链表的节点数目范围是 [0, 50]
-100 <= Node.val <= 100
l1 和 l2 均按 非递减顺序 排列
Java代码:一遍过 真的太爽了 终于不看题解可以做出来题啦
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if(list1 == null){
return list2;
}
if(list2 == null){
return list1;
}
ListNode head = new ListNode();
ListNode first = list1;
ListNode second = list2;
// 先把最原始的指针对象存起来
ListNode ans = head;
while(first != null && second != null){
ListNode next = new ListNode();
head.next = next;
if(first.val <= second.val){
next.val = first.val;
first = first.next;
}else{
next.val = second.val;
second = second.next;
}
head = head.next;
}
if(first != null){
head.next = first;
}
if(second != null){
head.next = second;
}
return ans.next;
}
}
- 不看题解,一遍通过,太爽了 2022.03.01
22.括号生成
原题地址
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
示例 2:
输入:n = 1
输出:["()"]
提示:
1 <= n <= 8
Java代码
class Solution {
public List<String> generateParenthesis(int n) {
List<String> ans = new ArrayList<>();
backtrace(ans,new StringBuilder(),0,0,n);
return ans;
}
private void backtrace(List<String> ans,StringBuilder stringBuilder,int open,int close,int max){
if(stringBuilder.length() >= 2*max){
ans.add(stringBuilder.toString());
return;
}
if(open < max){
stringBuilder.append('(');
backtrace(ans,stringBuilder,open+1,close,max);
stringBuilder.deleteCharAt(stringBuilder.length()-1);
}
if(close < open){
stringBuilder.append(')');
backtrace(ans,stringBuilder,open,close+1,max);
stringBuilder.deleteCharAt(stringBuilder.length()-1);
}
}
}
解题时间:
- 2022.03.07 10:03 pm
23.合并k个升序列表
原题地址
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
示例 2:
输入:lists = []
输出:[]
示例 3:
输入:lists = [[]]
输出:[]
提示:
k == lists.length
0 <= k <= 10^4
0 <= lists[i].length <= 500
-10^4 <= lists[i][j] <= 10^4
lists[i] 按 升序 排列
lists[i].length 的总和不超过 10^4
Java代码
package xyz.fudongyang.classic;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class Lc_23 {
public static void main(String[] args) {
ListNode[] lists = new ListNode[5];
lists[0] = new ListNode(1, new ListNode(4, new ListNode(5)));
lists[1] = new ListNode(1, new ListNode(3, new ListNode(4)));
lists[2] = new ListNode(2, new ListNode(6));
System.out.println(mergeKLists(lists));
}
public static ListNode mergeKLists(ListNode[] lists) {
// 暴力递归法
// 总体思路:循环遍历所有的升序链表,do for while 设置空指针标志位为退出条件 第一次for循环 遍历所有的数据 找到最小值 并记录下标 作为下一次进行
// TreeMap用来收集数据 key:数值 value:数组中的下标
ListNode ans = new ListNode();
ListNode temp = ans;
TreeMap<Integer, List<Integer>> tempMap = new TreeMap<>();
for (int i = 0; i < lists.length; i++) {
if (lists[i] != null) {
addElement(lists, tempMap, i);
}
}
while (!tempMap.isEmpty()) {
Map.Entry<Integer, List<Integer>> entry = tempMap.pollFirstEntry();
Integer key = entry.getKey();
List<Integer> values = entry.getValue();
for (Integer value : values) {
temp.next = new ListNode(key);
temp = temp.next;
if (lists[value] != null) {
addElement(lists, tempMap, value);
}
}
}
return ans.next;
}
private static void addElement(ListNode[] lists, TreeMap<Integer, List<Integer>> tempMap, int idx) {
if (tempMap.containsKey(lists[idx].val)) {
List<Integer> list = tempMap.get(lists[idx].val);
list.add(idx);
} else {
List<Integer> list = new ArrayList<>();
list.add(idx);
tempMap.put(lists[idx].val, list);
}
lists[idx] = lists[idx].next;
}
}
解题记录:
- 20220313 直接一遍过(太开心了 这是自己直接ac的hard)
31:下一个排列
原题地址
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
- 例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
- 例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
- 类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
- 而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。
必须 原地 修改,只允许使用额外常数空间。
示例 1:
输入:nums = [1,2,3]
输出:[1,3,2]
示例 2:
输入:nums = [3,2,1]
输出:[1,2,3]
示例 3:
输入:nums = [1,1,5]
输出:[1,5,1]
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 100
class Solution {
public static void nextPermutation(int[] nums) {
int i = nums.length - 2;
// 从右到左 找到一个非升序的数当作较小值
while (i >= 0 && nums[i] >= nums[i + 1]) {
i--;
}
// 如果存在较小值,则从右到左,找到一个大于较小值的数当作较大值
if (i >= 0) {
int j = nums.length - 1;
while (j >= 0 && nums[j] <= nums[i]) {
j--;
}
// 替换较小值 较大值
swap(nums, i, j);
}
// i 后面的数 应该从降序变为升序
reverse(nums, i + 1);
}
private static void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
private static void reverse(int[] nums, int start) {
int end = nums.length - 1;
while (start < end) {
swap(nums, start, end);
start++;
end--;
}
}
}
刷题记录:
- 2022.03.16-看了题解一个小时才做出来
32: 最长的有效括号
原题地址
给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
示例 1:
输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"
示例 2:
输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"
示例 3:
输入:s = ""
输出:0
提示:
0 <= s.length <= 3 * 104
s[i] 为 '(' 或 ')'
Java代码(栈)
package xyz.fudongyang.review;
import java.util.ArrayDeque;
import java.util.Deque;
/**
* @author: fudy
* @date: 2022/3/21 下午 08:17
* @Decription:
**/
public class Lc_32 {
public static void main(String[] args) {
System.out.println(longestValidParentheses("()(()"));
}
public static int longestValidParentheses(String s) {
char left = '(';
char[] chars = s.toCharArray();
Deque<Integer> deque = new ArrayDeque<>();
int maxLen = 0;
// 1. i 和 j 之间有几位 = i - j + 1 所以 我们先放入一个-1 到时候 减这个-1等于加一
deque.addLast(-1);
for (int i = 0; i < chars.length; i++) {
if (chars[i] == left) {
deque.addLast(i);
} else {
deque.pollLast();
if (deque.isEmpty()) {
// 2.和1。一样 我们先放入 i的前一个元素
deque.push(i);
} else {
maxLen = Math.max(maxLen, i - deque.peekLast());
}
}
}
return maxLen;
}
}
Java代码:动态规划
package xyz.fudongyang.review;
/**
* @author: fudy
* @date: 2022/3/21 下午 09:02
* @Decription:
**/
public class Lc_32_dp {
public static void main(String[] args) {
System.out.println(longestValidParentheses("()(()"));
}
// 有效括号的最长长度
// 子串问题:严格以每个结尾计算个答案,最终答案必在其中
public static int longestValidParentheses(String s) {
if (s == null || s.length() < 2) {
return 0;
}
int[] dp = new int[s.length()]; // dp[i]:严格以i位置结尾,形成的有效括号子串最长长度是多少
int max = 0; // 最终的答案
// dp[0] = 0; // 默认
for (int i = 1; i < s.length(); i++) {
// if (s.charAt(i) == '(') dp[i] = 0; 以左括号结尾,无效
if (s.charAt(i) == ')') {
int preLen = dp[i - 1]; // 前面已经形成的有效括号长度
int pre = i - 1 - preLen; // 寻找与当前的右括号相匹配的左括号位置:前面有效括号长度再往前一个位置
if (pre >= 0 && s.charAt(pre) == '(') { // 如果寻找到左括号:前面有效括号长度再往前一个位置是左括号
dp[i] = dp[i-1] + 2; // 可以与当前的右括号闭合,有效长度增加2
// 【注意】此时,需要再往前看下,是否还有有效长度,如果有,合并过来
// 例如:"()(()())" 当前在计算最后一个位置时,dp[7]已经等于 dp[6]+2 = 4+2
// 但需要再往前看一眼,dp[1]还有有效长度,合并过来 dp[7] = 4+2+2
// 那是否还需要再往前看?
// 不需要了,因为,如果前面还有有效长度,其长度肯定已经合并到dp[2]上了
// 因此,每次只需要再往前多看一眼就可以
if (pre-1 >= 0) {
dp[i] += dp[pre-1];
}
}
max = Math.max(max, dp[i]); // 严格以每个结尾抓一个答案,最终答案必在其中
}
}
return max;
}
}
刷题记录:
- 2022-03-21 一刷(思路对,但是没做出来,看的题解)
33.搜索旋转排序数组
原题地址
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
示例 1:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
示例 2:
输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1
示例 3:
输入:nums = [1], target = 0
输出:-1
提示:
1 <= nums.length <= 5000
-10^4 <= nums[i] <= 10^4
nums 中的每个值都 独一无二
题目数据保证 nums 在预先未知的某个下标上进行了旋转
-10^4 <= target <= 10^4
进阶:你可以设计一个时间复杂度为 O(log n) 的解决方案吗?
public int search(int[] nums, int target) {
if (nums.length == 1){
return target == nums[0] ? 0 : -1;
}
int begin = 0;
int end = nums.length-1;
while (begin <= end){
int mid = (begin + end) / 2;
if (nums[mid] == target){
return mid;
}
// 0-mid 升序
if (nums[0] <= nums[mid]){
if (nums[0] <= target && target < nums[mid]){
end = mid-1;
}else {
begin = mid+1;
}
}else {
// mid ~ end 升序 注意等于的情况需要考虑
if (nums[mid] < target && target <=nums[nums.length-1]){
begin = mid+1;
}else {
end = mid-1;
}
}
}
return -1;
}
刷题记录:
- 2022-03-22 看题解做出
34:在排序数组中查找元素的第一个和最后一个位置
原题地址
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:
你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:
输入:nums = [], target = 0
输出:[-1,-1]
提示:
0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums 是一个非递减数组
-109 <= target <= 109
一遍过 代码如下
package xyz.fudongyang.review;
import java.io.Serializable;
import java.lang.annotation.Target;
import java.util.Arrays;
/**
* @author: fudy
* @date: 2022/3/23 下午 09:13
* @Decription:
**/
public class Lc_34 {
public static void main(String[] args) {
int[] nums = {1,2,3,3,3,3,4,5};
System.out.println(Arrays.toString(searchRange(nums, 3)));
}
public static int[] searchRange(int[] nums, int target) {
if (nums.length == 0) {
return new int[]{-1, -1};
}
int[] ans = new int[]{-1, -1};
dfs(ans, 0, nums.length - 1, false, target,nums,-1);
return ans;
}
private static void dfs(int[] ans, int begin, int end, boolean isBegin, int target,int [] nums,int beginIndex) {
if (begin <= end) {
int mid = (begin + end) / 2;
if (target == nums[mid]) {
if (!isBegin) {
ans[0] = mid;
ans[1] = mid;
dfs(ans,begin,mid-1, true,target,nums,mid);
dfs(ans,mid+1,end,true,target,nums,mid);
}else {
if (mid > beginIndex){
ans[1] = mid;
dfs(ans,mid+1,end,true,target,nums,mid);
} else if (mid < beginIndex){
ans[0] = mid;
dfs(ans,begin,mid-1, true,target,nums,mid);
}
}
}else if (target < nums[mid]){
dfs(ans,begin,mid-1,isBegin,target,nums,beginIndex);
}else {
dfs(ans,mid+1,end,isBegin,target,nums,beginIndex);
}
}
}
}
刷题记录:
- 2022-03-23 一遍过 太爽了 二分真的恶心
42:接雨水
原题地址
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
示例 2:
输入:height = [4,2,0,3,2,5]
输出:9
提示:
n == height.length
1 <= n <= 2 * 104
0 <= height[i] <= 105
java代码
public static int rain(int[] nums) {
int current = 0;
int ans = 0;
Deque<Integer> stack = new LinkedList<>();
while (current < nums.length) {
while (!stack.isEmpty() && nums[current] > nums[stack.peek()]) {
// 弹出上一个小值
int top = stack.pop();
// 前面没人承接他
if (stack.isEmpty()){
break;
}
// 找到前面承接它的值
int weight =current - stack.peek() -1;
// 前面承接它的值 和 后面承接它的值 找一个最小的 ----- 差值
int height = Math.min(nums[current],nums[stack.peek()]) - nums[top];
ans += weight * height;
}
stack.push(current++);
}
return ans;
}
刷题记录:
- 2022-03-25 在上班 看题解 刷的
46:全排列
原题地址
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:
输入:nums = [0,1]
输出:[[0,1],[1,0]]
示例 3:
输入:nums = [1]
输出:[[1]]
提示:
1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums 中的所有整数 互不相同
Java代码
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> data = new ArrayList<>(nums.length);
boolean[] used = new boolean[nums.length];
dfs(ans,data,nums,used);
return ans;
}
private static void dfs(List<List<Integer>> ans,List<Integer> data,int[] nums,boolean[] used){
if (data.size() == nums.length){
ans.add(new ArrayList<>(data));
}
for (int i = 0; i < nums.length; i++) {
if (!used[i]){
data.add(nums[i]);
used[i] = true;
dfs(ans,data,nums,used);
// 这一块没想出来 这是回退的操作
used[i] = false;
data.remove(data.size()-1);
}
}
}
刷题记录:
- 2022-03-25 上班期间刷题 没有一遍过 回退的思想没掌握
48:图像旋转
原题地址
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]
示例 2:
输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]
提示:
n == matrix.length == matrix[i].length
1 <= n <= 20
-1000 <= matrix[i][j] <= 1000
特别清晰的解题思路
Java代码
public void rotate(int[][] matrix) {
// https://leetcode-cn.com/problems/rotate-image/solution/48-xuan-zhuan-tu-xiang-chao-jian-ji-yi-d-nuau/
// 对角线旋转
int len = matrix.length;
for (int i = 0; i < len; i++) {
for (int j = 0; j < i; j++) {
int temp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = temp;
}
}
// 再中心对称旋转
for (int i = 0; i < len; i++) {
for (int j = 0,k=len-1; j < k; j++,k--) {
int temp = matrix[i][k];
matrix[i][k] = matrix[i][j];
matrix[i][j] = temp;
}
}
}
刷题记录:
- 2022-03-28 看题解 一刷
200:岛屿数量(今天业务上有这样的需求)
原题地址
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
输入:grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
输出:1
示例 2:
输入:grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
输出:3
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j] 的值为 '0' 或 '1'
package xyz.fudongyang.le;
/**
* @author: vfudongyang
* @createTime: 2022年03月17日 10:36:00
* @Description:
*/
public class Le_200 {
private final static char[][] META_DATA = {
{'1', '1', '0', '0', '0'},
{'1', '1', '0', '0', '0'},
{'0', '0', '1', '0', '0'},
{'0', '0', '0', '1', '1'}
};
private final static char[][] META_DATA_2 = {
{'1', '1', '1'},
{'0', '1', '0'},
{'1', '1', '1'}
};
private final static char CHECKED = '2';
private final static char UNCHECK = '1';
private final static char NOTHING = '0';
public static void main(String[] args) {
System.out.println(numIslands(META_DATA));
System.out.println(numIslands(META_DATA_2));
}
public static int numIslands(char[][] grid) {
int length = grid.length;
int line = grid[0].length;
int ans = 0;
for (int i = 0; i < length; i++) {
for (int j = 0; j < line; j++) {
// for 循环每一个元素如果需要遍历 记录位置 作为开端
if (grid[i][j] == UNCHECK) {
System.out.println("i: " + i + " j: " + j);
infected(grid, i, j);
ans++;
}
}
}
System.out.println("ans = " + ans);
return ans;
}
private static void infected(char[][] chars, int i, int j) {
// 遇到1之后 就去感染 感染规则就是 向四周感染 1就置为2 0不变
if (i - 1 >= 0 && chars[i - 1][j] == UNCHECK) {
chars[i - 1][j] = CHECKED;
infected(chars, i - 1, j);
}
if (i + 1 < chars.length && chars[i + 1][j] == UNCHECK) {
chars[i + 1][j] = CHECKED;
infected(chars, i + 1, j);
}
if (j - 1 >= 0 && chars[i][j - 1] == UNCHECK) {
chars[i][j - 1] = CHECKED;
infected(chars, i, j - 1);
}
if (j + 1 < chars[0].length && chars[i][j + 1] == UNCHECK) {
chars[i][j + 1] = CHECKED;
infected(chars, i, j + 1);
}
}
}
刷题记录:
- 2022.03.17 直接一遍过,我记得我看到老师讲的视频这个方法