20240415(就一道困难题,还一直没懂)
76.最小覆盖子串
Leetcode链接:
https://leetcode.cn/problems/minimum-window-substring/description/?envType=study-plan-v2&envId=top-100-liked
public String minWindow(String s, String t) {
if(s==null||s.isEmpty()||t==null||t.isEmpty()||s.length()<t.length())
return "";
Map<Character,Integer> need=new HashMap<>();
Map<Character,Integer> window=new HashMap<>();
for(char c:t.toCharArray())
need.put(c,need.getOrDefault(c,0)+1);
int left=0,right=0;
int valid=0;
int start=0,minLen=Integer.MAX_VALUE;
while(right<s.length()){
char r=s.charAt(right);
right++;
if(need.containsKey(r)){
window.put(r,window.getOrDefault(r,0)+1);
if(window.get(r).equals(need.get(r))) valid++;
}
while(valid==need.size()){
if(right-left<minLen){
start=left;
minLen=right-left;
}
char l=s.charAt(left);
if(need.containsKey(l)){
window.put(l,window.get(l)-1);
if(window.get(l)<need.get(l)) valid--;
}
left++;
}
}
return minLen==Integer.MAX_VALUE?"":s.substring(start,start+minLen);
}
class Solution {
public String minWindow(String s, String t) {
if (s == null || s == "" || t == null || t == "" || s.length() < t.length()) {
return "";
}
//维护两个数组,记录已有字符串指定字符的出现次数,和目标字符串指定字符的出现次数
//ASCII表总长128
int[] need = new int[128];
int[] have = new int[128];
//将目标字符串指定字符的出现次数记录
for (int i = 0; i < t.length(); i++) {
need[t.charAt(i)]++;
}
//分别为左指针,右指针,最小长度(初始值为一定不可达到的长度)
//已有字符串中目标字符串指定字符的出现总频次以及最小覆盖子串在原字符串中的起始位置
int left = 0, right = 0, min = s.length() + 1, count = 0, start = 0;
while (right < s.length()) {
char r = s.charAt(right);
//说明该字符不被目标字符串需要,此时有两种情况
// 1.循环刚开始,那么直接移动右指针即可,不需要做多余判断
// 2.循环已经开始一段时间,此处又有两种情况
// 2.1 上一次条件不满足,已有字符串指定字符出现次数不满足目标字符串指定字符出现次数,那么此时
// 如果该字符还不被目标字符串需要,就不需要进行多余判断,右指针移动即可
// 2.2 左指针已经移动完毕,那么此时就相当于循环刚开始,同理直接移动右指针
if (need[r] == 0) {
right++;
continue;
}
//当且仅当已有字符串目标字符出现的次数小于目标字符串字符的出现次数时,count才会+1
//是为了后续能直接判断已有字符串是否已经包含了目标字符串的所有字符,不需要挨个比对字符出现的次数
if (have[r] < need[r]) {
count++;
}
//已有字符串中目标字符出现的次数+1
have[r]++;
//移动右指针
right++;
//当且仅当已有字符串已经包含了所有目标字符串的字符,且出现频次一定大于或等于指定频次
while (count == t.length()) {
//挡窗口的长度比已有的最短值小时,更改最小值,并记录起始位置
if (right - left < min) {
min = right - left;
start = left;
}
char l = s.charAt(left);
//如果左边即将要去掉的字符不被目标字符串需要,那么不需要多余判断,直接可以移动左指针
if (need[l] == 0) {
left++;
continue;
}
//如果左边即将要去掉的字符被目标字符串需要,且出现的频次正好等于指定频次,那么如果去掉了这个字符,
//就不满足覆盖子串的条件,此时要破坏循环条件跳出循环,即控制目标字符串指定字符的出现总频次(count)-1
if (have[l] == need[l]) {
count--;
}
//已有字符串中目标字符出现的次数-1
have[l]--;
//移动左指针
left++;
}
}
//如果最小长度还为初始值,说明没有符合条件的子串
if (min == s.length() + 1) {
return "";
}
//返回的为以记录的起始位置为起点,记录的最短长度为距离的指定字符串中截取的子串
return s.substring(start, start + min);
}
}
20240416
53.最大子数组和
https://leetcode.cn/problems/maximum-subarray/?envType=study-plan-v2&envId=top-100-liked
public int maxSubArray(int[] nums) {
int len=nums.length;
int[] dp=new int[len];
dp[0]=nums[0];
for(int i=1;i<len;i++){
if(dp[i-1]>0){
dp[i]=dp[i-1]+nums[i];
}else{
dp[i]=nums[i];
}
}
int res=dp[0];
for(int i=1;i<len;i++){
res=Math.max(res,dp[i]);
}
return res;
}
分治法
public class Solution {
public int maxSubArray(int[] nums) {
int len = nums.length;
if (len == 0) {
return 0;
}
return maxSubArraySum(nums, 0, len - 1);
}
private int maxCrossingSum(int[] nums, int left, int mid, int right) {
// 一定会包含 nums[mid] 这个元素
int sum = 0;
int leftSum = Integer.MIN_VALUE;
// 左半边包含 nums[mid] 元素,最多可以到什么地方
// 走到最边界,看看最值是什么
// 计算以 mid 结尾的最大的子数组的和
for (int i = mid; i >= left; i--) {
sum += nums[i];
if (sum > leftSum) {
leftSum = sum;
}
}
sum = 0;
int rightSum = Integer.MIN_VALUE;
// 右半边不包含 nums[mid] 元素,最多可以到什么地方
// 计算以 mid+1 开始的最大的子数组的和
for (int i = mid + 1; i <= right; i++) {
sum += nums[i];
if (sum > rightSum) {
rightSum = sum;
}
}
return leftSum + rightSum;
}
private int maxSubArraySum(int[] nums, int left, int right) {
if (left == right) {
return nums[left];
}
int mid = left + (right - left) / 2;
return max3(maxSubArraySum(nums, left, mid),
maxSubArraySum(nums, mid + 1, right),
maxCrossingSum(nums, left, mid, right));
}
private int max3(int num1, int num2, int num3) {
return Math.max(num1, Math.max(num2, num3));
}
}
56.合并区间
public int[][] merge(int[][] intervals) {
if(intervals.length==0){
return new int[0][2];
}
Arrays.sort(intervals,new Comparator<int[]>(){
public int compare(int[] interval1,int[] interval2){
return interval1[0]-interval2[0];
}
});
List<int[]> merged=new ArrayList<int[]>();
for(int i=0;i<intervals.length;++i){
int L=intervals[i][0],R=intervals[i][1];
if(merged.size()==0||merged.get(merged.size()-1)[1]<L){
merged.add(new int[]{L,R});
}else{
merged.get(merged.size()-1)[1]=Math.max(merged.get(merged.size()-1)[1],R);
}
}
return merged.toArray(new int[merged.size()][]);
}
public int[][] merge(int[][] intervals) {
List<int[]> res=new LinkedList<>();
Arrays.sort(intervals,(x,y)->Integer.compare(x[0],y[0]));
int start=intervals[0][0];
int rightmostRightBound=intervals[0][1];
for(int i=1;i<intervals.length;i++){
if(intervals[i][0]>rightmostRightBound){
res.add(new int[]{start,rightmostRightBound});
start=intervals[i][0];
rightmostRightBound=intervals[i][1];
}else{
rightmostRightBound=Math.max(rightmostRightBound,intervals[i][1]);
}
}
res.add(new int[]{start,rightmostRightBound});
return res.toArray(new int[res.size()][]);
}
视频讲解:https://www.bilibili.com/video/BV1wx4y157nD/?spm_id_from=333.337.search-card.all.click&vd_source=4bff775c9c6da7af76663b10f157a21f
189.轮转数组
https://leetcode.cn/problems/rotate-array/?envType=study-plan-v2&envId=top-100-liked
先整体反转,在k前个反转,在k后面反转
class Solution {
public void rotate(int[] nums, int k) {
k%=nums.length;
reverse(nums,0,nums.length-1);
reverse(nums,0,k-1);
reverse(nums,k,nums.length-1);
}
public void reverse(int[] nums,int start,int end){
while(start<end){
int temp=nums[start];
nums[start]=nums[end];
nums[end]=temp;
start+=1;
end-=1;
}
}
}
238.除自身以外数组的乘积
左右数组,最后左右数组相乘。空间时间复杂度都是O(n)
public int[] productExceptSelf(int[] nums) {
int length=nums.length;
int[] L=new int[length];
int[] R=new int[length];
int[] answer=new int[length];
L[0]=1;
for(int i=1;i<length;i++){
L[i]=nums[i-1]*L[i-1];
}
R[length-1]=1;
for(int i=length-2;i>=0;i--){
R[i]=nums[i+1]*R[i+1];
}
for(int i=0;i<length;i++){
answer[i]=L[i]*R[i];
}
return answer;
}
空间复杂度为O(1)的方法·
public int[] productExceptSelf(int[] nums) {
int length=nums.length;
int[] answer=new int[length];
answer[0]=1;
for(int i=1;i<length;i++){
answer[i]=nums[i-1]*answer[i-1];
}
int R=1;
for(int i=length-1;i>=0;i--){
answer[i]=answer[i]*R;
R*=nums[i];
}
return answer;
}
20240417
73.矩阵置零
时间复杂度o(mn),空间复杂度O(m+n)
public void setZeroes(int[][] matrix) {
int m=matrix.length,n=matrix[0].length;
boolean[] row=new boolean[m];
boolean[] col=new boolean[n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(matrix[i][j]==0){
row[i]=col[j]=true;
}
}
}
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(row[i]||col[j]){
matrix[i][j]=0;
}
}
}
}
时间复杂度O(MN),空间复杂度O(1)
public void setZeroes(int[][] matrix) {
int m=matrix.length,n=matrix[0].length;
boolean flagCol0=false;
for(int i=0;i<m;i++){
if(matrix[i][0]==0){
flagCol0=true;
}
for(int j=1;j<n;j++){
if(matrix[i][j]==0){
matrix[i][0]=matrix[0][j]=0;
}
}
}
for(int i=m-1;i>=0;i--){
for(int j=1;j<n;j++){
if(matrix[i][0]==0||matrix[0][j]==0){
matrix[i][j]=0;
}
}
if(flagCol0){
matrix[i][0]=0;
}
}
}
54.螺旋矩阵
时间复杂度O(MN),空间复杂度O(1)
public List<Integer> spiralOrder(int[][] matrix) {
if(matrix.length==0)
return new ArrayList<Integer>();
int l=0,r=matrix[0].length-1,t=0,b=matrix.length-1,x=0;
Integer[] res=new Integer[(r+1)*(b+1)];
while(true){
for(int i=l;i<=r;i++) res[x++]=matrix[t][i];
if(++t>b) break;
for(int i=t;i<=b;i++) res[x++]=matrix[i][r];
if(l>--r) break;
for(int i=r;i>=l;i--) res[x++]=matrix[b][i];
if(t>--b) break;
for(int i=b;i>=t;i--) res[x++]=matrix[i][l];
if(++l>r) break;
}
return Arrays.asList(res);
}
该方法是将数组转化成List集合的方法。
List list = Arrays.asList(“a”,“b”,“c”);
注意:
(1)该方法适用于对象型数据的数组(String、Integer…)
(2)该方法不建议使用于基本数据类型的数组(byte,short,int,long,float,double,boolean)
(3)该方法将数组与List列表链接起来:当更新其一个时,另一个自动更新
(4)不支持add()、remove()、clear()等方法
59.螺旋矩阵2
https://leetcode.cn/problems/spiral-matrix-ii/
public int[][] generateMatrix(int n) {
int l=0,r=n-1,t=0,b=n-1;
int[][] mat=new int[n][n];
int num=1,tar=n*n;
while(num<=tar){
for(int i=l;i<=r;i++) mat[t][i]=num++;
t++;
for(int i=t;i<=b;i++) mat[i][r]=num++;
r--;
for(int i=r;i>=l;i--) mat[b][i]=num++;
b--;
for(int i=b;i>=t;i--) mat[i][l]=num++;
l++;
}
return mat;
}
48.旋转图像
https://leetcode.cn/problems/rotate-image/solutions/1228078/48-xuan-zhuan-tu-xiang-fu-zhu-ju-zhen-yu-jobi/?envType=study-plan-v2&envId=top-100-liked
先上下翻转,再左对角线翻转
public void rotate(int[][] matrix) {
int n=matrix.length;
for(int i=0;i<n/2;i++){
for(int j=0;j<n;j++){
int tmp=matrix[i][j];
matrix[i][j]=matrix[n-1-i][j];
matrix[n-1-i][j]=tmp;
}
}
for(int i=0;i<n;i++){
for(int j=0;j<i;j++){
int tmp=matrix[i][j];
matrix[i][j]=matrix[j][i];
matrix[j][i]=tmp;
}
}
}
20240418
240.搜索二维矩阵II
https://leetcode.cn/problems/search-a-2d-matrix-ii/?envType=study-plan-v2&envId=top-100-liked
public boolean searchMatrix(int[][] matrix, int target) {
int i=matrix.length-1,j=0;
while(i>=0&&j<matrix[0].length){
if(matrix[i][j]>target) i--;
else if(matrix[i][j]<target) j++;
else return true;
}
return false;
}
时间复杂度 O(M+N) :其中,N 和 M 分别为矩阵行数和列数,此算法最多循环 M+N 次。
空间复杂度 O(1) : i, j 指针使用常数大小额外空间。
160.相交链表
双指针法
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA==null||headB==null) return null;
ListNode pA=headA,pB=headB;
while(pA!=pB){
pA=pA==null?headB:pA.next;
pB=pB==null?headA:pB.next;
}
return pA;
}
260.反转链表
1.迭代(双指针)时间复杂度O(n),空间复杂度O(1)
好熟悉的题,可惜我还是不会
public ListNode reverseList(ListNode head) {
ListNode cur=head,pre=null;
while(cur!=null){
ListNode tmp=cur.next;
cur.next=pre;
pre=cur;
cur=tmp;
}
return pre;
}
2.递归
时间复杂度、空间复杂度O(n)
class Solution {
public ListNode reverseList(ListNode head) {
return recur(head,null);
}
private ListNode recur(ListNode cur,ListNode pre){
if(cur==null) return pre;
ListNode res=recur(cur.next,cur);
cur.next=pre;
return res;
}
}
234.回文链表
1.先将值插入数组,再双指针.时间空间复杂度O(n)
public boolean isPalindrome(ListNode head) {
List<Integer> vals=new ArrayList<Integer>();
ListNode currentNode=head;
while(currentNode!=null){
vals.add(currentNode.val);
currentNode=currentNode.next;
}
int front=0;
int back=vals.size()-1;
while(front<back){
if(!vals.get(front).equals(vals.get(back))){
return false;
}
front++;
back--;
}
return true;
}
2.快慢指针
整个流程可以分为以下五个步骤:
找到前半部分链表的尾节点。
反转后半部分链表。
判断是否回文。
恢复链表。
返回结果。
时间复杂度O(N),空间O(1)
class Solution {
public boolean isPalindrome(ListNode head) {
if(head==null){
return true;
}
ListNode firstHalfEnd=endOfFirstHalf(head);
ListNode secondHalfStart=reverseList(firstHalfEnd.next);
ListNode p1=head;
ListNode p2=secondHalfStart;
boolean result=true;
while(result&&p2!=null){
if(p1.val!=p2.val){
result=false;
}
p1=p1.next;
p2=p2.next;
}
firstHalfEnd.next=reverseList(secondHalfStart);
return result;
}
private ListNode reverseList(ListNode head){
ListNode pre=null;
ListNode curr=head;
while(curr!=null){
ListNode nextTemp=curr.next;
curr.next=pre;
pre=curr;
curr=nextTemp;
}
return prev;
}
private ListNode endOfFirstHalf(ListNode head){
ListNode fast=head;
ListNode slow=head;
while(fast.next!=null&&fast.next.next!=null){
fast=fast.next.next;
slow=slow.next;
}
return slow;
}
}
141.环形链表
public boolean hasCycle(ListNode head) {
if(head==null||head.next==null){
return false;
}
ListNode slow=head;
ListNode fast=head.next;
while(slow!=fast){
if(fast==null||fast.next==null){
return false;
}
slow=slow.next;
fast=fast.next.next;
}
return true;
}
142.环形链表2
时间复杂度O(n),空间复杂度O(1)
public ListNode detectCycle(ListNode head) {
ListNode fast=head,slow=head;
while(true){
if(fast==null||fast.next==null) return null;
fast=fast.next.next;
slow=slow.next;
if(fast==slow) break;
}
fast=head;
while(slow!=fast){
slow=slow.next;
fast=fast.next;
}
return fast;
}
20240420
21.合并两个有序链表
1.递归,时间空间复杂度都是O(m+n)
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;
}
}
2.迭代
时间复杂度O(m+n),空间O(1)
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=l2;
l2=l2.next;
}else{
pre.next=l2;
l2=l2.next;
}
prev=prev.next;
}
prev.next=l1==null?l2:l1;
return prehead.next;
}