力扣[LeetCode]hot100Java代码实现
文章目录
前言
25.4.18一刷hot100,分享一下每题的多种解法,不定时更新。
一、哈希
1.两数之和
力扣题目链接1.两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。
你可以按任意顺序返回答案。
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] res = new int[2];
for(int i =0;i<nums.length;i++){
int number = target-nums[i];
res[0]=i;
for(int j =0;j<nums.length;j++){
if(number==nums[j]&&i!=j){
res[1]=j;
return res;
}
}
}
return res;
}
}
2.字母异位词分组
力扣题目链接49.字母异位词分组
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
输入: strs = [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”]
输出: [[“bat”],[“nat”,“tan”],[“ate”,“eat”,“tea”]]
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
HashMap<String, List<String>> res = new HashMap<>();
if(strs.length==0||strs==null){
return new ArrayList<>();
}else{
for(String str:strs){
char[] chars = str.toCharArray();
Arrays.sort(chars);
String key = new String(chars);
if(!res.containsKey(key)){
res.put(key,new ArrayList<>());
}
res.get(key).add(str);
}
return new ArrayList<>(res.values());
}
}
}
3.最长连续序列
力扣题目链接128.最长连续序列
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
class Solution {
public int longestConsecutive(int[] nums) {
Set<Integer> numSet = new HashSet<>();
for (int num : nums) {
numSet.add(num);
}
int max = 1;
int current;
if(nums.length==0){
return 0;
}
for(Integer num: numSet){
if(!numSet.contains(num-1)){
current=num;
int res=1;
while(numSet.contains(current+1)){
res++;
current++;
}
max=(max>res)?max:res;
}
}
return max;
}
}
二、双指针
4.移动零
力扣题目链接283.移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
class Solution {
public void moveZeroes(int[] nums) {
int nozeroIndex=0;
int count =0;
for(int i =0;i<nums.length;i++){
if(nums[i]!=0){
nums[nozeroIndex]=nums[i];
nozeroIndex++;
}else{
count++;
}
}
for(int i =nums.length-1;i>nums.length-1-count;i--){
nums[i] = 0;
}
}
}
5.乘最多水的容器
力扣题目链接11.乘最多水的容器
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
class Solution {
public int maxArea(int[] height) {
int h=0;
int w=0;
int maxArea=0;
int i=0;
int j=height.length-1;
while(i<j){
h=(height[i]<height[j])?height[i]:height[j];
w=j-i;
maxArea=(maxArea>h*w)?maxArea:h*w;
if(height[i]<height[j]){
i++;
}else{
j--;
}
}
return maxArea;
}
}
6.三数之和
力扣题目链接15.三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
for(int i = 0;i<nums.length;i++){
int j=i+1;
int last = nums.length-1;
while(j<last){
int sum = nums[i]+nums[j]+nums[last];
if(sum==0){
ArrayList<Integer> list=new ArrayList<>();
list.add(nums[i]);
list.add(nums[j]);
list.add(nums[last]);
res.add(list);
last--;
j++;
}else if(sum>0){
last--;
}else{
j++;
}
}
}
return res;
}
}
7.接雨水
力扣42.接雨水
给定 n 个非负整数表示每个宽度为 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 个单位的雨水(蓝色部分表示雨水)。
class Solution {
public int trap(int[] height) {
int total=0;
int left=0;
int right=height.length-1;
int maxLeft=0;
int maxRight=0;
while(left<right){
if(height[left]<=height[right]){
total+=Math.max(0,maxLeft-height[left]);
maxLeft=Math.max(maxLeft,height[left]);
left++;
}else{
total+=Math.max(0,maxRight-height[right]);
maxRight=Math.max(maxRight,height[right]);
right--;
}
}
return total;
}
}
三、滑动窗口
8.无重复字符的最长子串
力扣3.无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
class Solution {
public int lengthOfLongestSubstring(String s) {
HashMap<Character, Integer> res = new HashMap<>();
int len=0;
int left=0;
for(int i = 0;i<s.length();i++){
char c = s.charAt(i);
if(res.containsKey(c)&&left<=res.get(c)){
left=res.get(c)+1;
}
res.put(c,i);
len=Math.max(len,i-left+1);
}
return len;
}
}
9.找到字符串中所有字母异位词
力扣438.找到字符串中所有字母异位词
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
输入: s = “cbaebabacd”, p = “abc”
输出: [0,6]
解释:
起始索引等于 0 的子串是 “cba”, 它是 “abc” 的异位词。
起始索引等于 6 的子串是 “bac”, 它是 “abc” 的异位词。
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> res = new ArrayList<>();
if(p.length()>s.length()){
return res;
}
HashMap<Character, Integer> pMap = new HashMap<>();
for (char c : p.toCharArray()) {
pMap.put(c,pMap.getOrDefault(c,0)+1);
}
HashMap<Character, Integer> windowMap = new HashMap<>();
for(int i =0;i<p.length()-1;i++){
windowMap.put(s.charAt(i),windowMap.getOrDefault(s.charAt(i),0)+1);
}
for(int i =p.length()-1;i<s.length();i++){
windowMap.put(s.charAt(i),windowMap.getOrDefault(s.charAt(i),0)+1);
if(windowMap.equals(pMap)){
res.add(i-p.length()+1);
}
windowMap.put(s.charAt(i-p.length()+1),windowMap.getOrDefault(s.charAt(i-p.length()+1),0)-1);
if(windowMap.get(s.charAt(i-p.length()+1))==0){
windowMap.remove(s.charAt(i-p.length()+1));
}
}
return res;
}
}
10.和为K的子数组
力扣560.和为K的子数组
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。
子数组是数组中元素的连续非空序列。
输入:nums = [1,1,1], k = 2
输出:2
public class Solution {
public int subarraySum(int[] nums, int k) {
int count = 0;
HashMap<Integer,Integer> pre = new HashMap<>();
pre.put(0,1);
int currentSum = 0;
for(int num: nums){
currentSum+=num;
if(pre.containsKey(currentSum-k)){
count+=pre.get(currentSum-k);
}
pre.put(currentSum, pre.getOrDefault(currentSum, 0)+1);
}
return count;
}
}
11.滑动窗口最大值
239.滑动窗口最大值
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
优先队列:
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int[] res = new int[nums.length-k+1];
//放nums[i]和i
PriorityQueue<int[]> queue = new PriorityQueue<>(
//大的num在前面,同样的num是i大的在前面
(a,b)->(a[0]!=b[0]?b[0]-a[0]:b[1]-a[1])
);
//先放k个
for(int i=0;i<k;i++){
queue.offer(new int[]{nums[i],i});
}
res[0] = queue.peek()[0];
for(int i=k;i<nums.length;i++){
queue.offer(new int[]{nums[i],i});
//队列放k个,i-k是窗口的左边界
while(i-k>=queue.peek()[1]){
queue.poll();
}
//队首放着窗口最大的num
res[i-k+1] = queue.peek()[0];
}
return res;
}
}
双端队列:
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
int[] res = new int[nums.length-k+1];
//放没有被移除的i
Deque<Integer> queue = new LinkedList<>();
//先放k个
for(int i=0;i<k;i++){
//从后往前遍历queue里面的num,如果新的num比他们大,就移除这些
while(!queue.isEmpty()&&nums[i]>=nums[queue.peekLast()]){
queue.pollLast();
}
queue.offerLast(i);
}
res[0] = nums[queue.peekFirst()];
for(int i=k;i<nums.length;i++){
while(!queue.isEmpty()&&nums[i]>=nums[queue.peekLast()]){
queue.pollLast();
}
queue.offerLast(i);
//i-k是窗口的左边界
while(i-k>=queue.peekFirst()){
queue.pollFirst();
}
//队首放着窗口最大的num的i
res[i-k+1] = nums[queue.peekFirst()];
}
return res;
}
}
12.最小覆盖子串
76.最小覆盖子串
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。
public String minWindow(String s, String t) {
int left=0,right=0;
int minlen = s.length()+1;
int ansL=-1,ansR=0;
int form = 0;//保存当前已经匹配的字符数量
//A-Z,6个其他字符,a-z,26+6+26=58
int[] tList = new int[58];
for(char c:t.toCharArray()){
tList[c-'A']++;
}
while(right<s.length()&&left<=right){
int cs = s.charAt(right)-'A';
if(tList[cs]>0){
form++;
}
tList[cs]--;
right++;
//匹配到了
while(form==t.length()){
//更新最小值
if(right-left<minlen){
ansL = left;
ansR = right;
minlen = right-left;
}
//缩小左边界
int index = s.charAt(left)-'A';
tList[index]++;
if(tList[index]>0){
form--;
}
left++;
}
}
return ansL==-1 ? "" : s.substring(ansL, ansR);
}