代码编织梦想

一:滑动窗口

1. 无重复字符的最长子串

题目

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s.length()==0) return 0; //0,1情况
        if (s.length()==1) return 1;
        
        Set<Character> set = new HashSet<>();
        int left = 0;  int right = 0;//窗口左右端
        int sum=1; //记录窗口大小

        //滑动窗口
         while (right<s.length()) {
            if (!set.contains(s.charAt(right))) {
                set.add(s.charAt(right));
                sum=Math.max(sum, set.size());
                right++;
            }else{
                set.remove(s.charAt(left));
                left++;
            }
         }
        return sum ;
    }
}

2.找到字符串中所有字母异位词

找异位词:子串转26位数组(或强转为字符串)

哈希表存储子串,所含字母和出现的次数相同,即为异位词

//字符转26位数组(存为字符),数组值为字符的出现次数,再强转为字符串
    String encode(String s){
        char[] chars=new char[26];
        for (char i : s.toCharArray()) {
            int idCount=i-'a'; //记录每个字符出现的次数
            chars[idCount]++;
        }
        return new String(chars);
    }
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int pLen=p.length(); int sLen =s.length();
        //s,母串, p,子串
        //结果列表
        List<Integer> result =new ArrayList<>();
        if (sLen < pLen) return result;
        //存储子串--count函数
        //Set<int []> map=new HashSet<>();
        //map.add(count(p));
        int left=0; int right =pLen-1;
        while (right<sLen) {
            String sSub=s.substring(left, right+1);
            if (Arrays.equals(count(p), count(sSub)) ) {
                result.add(left);    
            }
            left++; right++;
        }  
        
        return result;
    }

     //字符转26位数组,数组值为字符的出现次数,

     int[] count(String s){
        int[] counts=new int[26];
        for (int i = 0; i < s.length(); i++) {
            int idCount=s.charAt(i)-'a'; //记录每个字符出现的次数
            counts[idCount]++;
        }
        return counts;
     }
}

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int pLen=p.length(); int sLen =s.length();
        //s,母串, p,子串
        //结果列表
        List<Integer> result =new ArrayList<>();
        if (sLen < pLen) return result;
        //存储子串--count函数
        Set<String> map=new HashSet<>();
        map.add(encode(p));
        int left=0; int right =pLen-1;
        while (right<sLen) {
            String sSub=s.substring(left, right+1);
            if (map.contains(encode(sSub)) ) {
                result.add(left);    
            }
            left++; right++;
        }  
        
        return result;
    }

    //字符转26位数组(存为字符),数组值为字符的出现次数,再强转为字符串
    String encode(String s){
        char[] chars=new char[26];
        for (char i : s.toCharArray()) {
            int idCount=i-'a'; //记录每个字符出现的次数
            chars[idCount]++;
        }
        return new String(chars);
    }
     
     
}

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int n = s.length();
        int m = p.length();
        if (n < m) {
            return new ArrayList<>();
        }

        char[] pArr = new char[26];
        char[] tempArr = new char[26];
        List<Integer> result = new ArrayList<>();
        int right = 0;

        //初始化指针
        for (int i = 0; i < m; i++) {
            pArr[p.charAt(i) - 'a']++;
            tempArr[s.charAt(i) - 'a']++;
            right++;
        }

        //
        if (Arrays.equals(pArr, tempArr)) {
            result.add(0);
        }

        for (int i = 1; i <= n - m; i++) {
            tempArr[s.charAt(i - 1) - 'a']--;
            tempArr[s.charAt(right) - 'a']++;
            right++;   
            
            if (Arrays.equals(pArr, tempArr)) {
                result.add(i);
            }
        }

        return result;

    }
     
     
}

8ms

二:子串

1.和为 K 的子数组

前缀和,求前缀和的差为k的个数

class Solution {
    public int subarraySum(int[] nums, int k) {
        int preSum=0;  // 记录前缀和
        int count=0;  //记录前缀和的差为k的个数
        // key值为前缀和,value为出现的次数(集合set元素要唯一)
        HashMap<Integer,Integer> map =new HashMap<>();
        //先存储(0,1)进去,和为0的次数先有一次
        map.put(0, 1);

        for (int num : nums) {
            preSum+=num;
            //边记录前缀和,边找有没有差为k的
            count+=map.getOrDefault(preSum-k,0);
            //把每一次得到的前缀和加入到map中
            int midValue=map.getOrDefault(preSum, 0);
            map.put(preSum, midValue+1);
        }  
        return count ;
    }
}

2.滑动窗口最大值

补充知识:Java队列Queue

offer是队列满了,添加不了,出现异常

pull()返回false,表示删除失败,remove()抛出异常

peek()返回null,表示查不到,element()抛出异常

补充知识:Java队列Deque

Deque是一个双端队列接口,继承自Queue接口Deque的实现类是LinkedList、ArrayDeque、LinkedBlockingDeque,其中LinkedList是最常用的

Deque是一个线性collection,支持在两端插入和移除元素。名称 deque 是“double ended queue(双端队列)”的缩写,通常读为“deck”。大多数 Deque 实现对于它们能够包含的元素数没有固定限制,但此接口既支持有容量限制的双端队列,也支持没有固定大小限制的双端队列。

思路:

3.最小覆盖子串

public String minWindow(String s, String t) {
        // 参数校验
        if (s == null || t == null || s.length() < t.length()) {
            return "";
        }
        // 保存t字符出现的次数,表示每个字符待需要的数量
        Map<Character, Integer> tMap = new HashMap<>(t.length());
        for (int i = 0; i < t.length(); i++) {
            tMap.put(t.charAt(i), tMap.getOrDefault(t.charAt(i), 0) + 1);
        }
        // 判断窗口是否包含了t的所有元素,避免每次去遍历tMap,增加耗时
        int needCnt = t.length();
        // 定义最小结果集时的左右边界,初始化边界长度为最大值
        int[] minResult = new int[]{0, Integer.MAX_VALUE};
        int length = s.length();
        // 定义滑动窗口左右边界,右边界开始移动
        for (int i = 0, j = 0; j < length; j++) {
            char c = s.charAt(j);
            // 包含t,待需要的数量减一
            if (tMap.getOrDefault(c, 0) > 0) {
                needCnt--;
            }
            // 同时map需要的字符数量减一
            tMap.put(c, tMap.getOrDefault(c, 0) - 1);
            // 都包含了,此时右边界j不动,开始移动左边界,尝试缩小窗口
            if (needCnt == 0) {
                
                while (true) {
                    c = s.charAt(i);
                    // 左边界字符已经满足了,不再需要了,则退出循环,没办法继续缩小了
                    // 否则继续缩小,那么会执行下面的+1,所需要的字符又增加了
                    if (tMap.get(c) == 0) {
                        break;
                    }
                    // 左边界字符
                    // map还有多余数量可以缩小
                    // 缩小左边界,该字符所需要的数量+1
                    tMap.put(c, tMap.getOrDefault(c, 0) + 1);
                    i++;
                }
                // 左边界也更新完了,这时该更新结果了,覆盖res
                if (j - i < minResult[1] - minResult[0]) {
                    minResult[1] = j;
                    minResult[0] = i;
                }
                c = s.charAt(i);
                //将左边界右移,执行下一个窗口
                // 由于左边界是t需要的字符,右移后,需要更新tMap和needCnt,表示还需要增加一个字符
                tMap.put(c, tMap.getOrDefault(c, 0) + 1);
                needCnt++;
                i++;
            }
        }
        // 超过边界,返回空串
        if (minResult[1] > length) {
            return "";
        } else {
            return s.substring(minResult[0], minResult[1] + 1);
        }
    }

三:普通数组

1.最大子数组和

思路:

代码

[-2,1,-3,4,-1,2,1,-5,4]
class Solution {
    public int maxSubArray(int[] nums) {
        int foreMax=0; //记录不包含当前第i个的 子数组 和
        int resultMax=Integer.MIN_VALUE; //包含第 i个 的 子数组 和
        for ( int i = 0; i < nums.length; i++) {
            //判断加上i项是否比第i项还小
            //是,重置,丢弃前面的i-1项
            //(因为求的是连续子数组和)
            // if (foreMax+nums[i]<nums[i]) {
            //     foreMax=nums[i];
            // }else foreMax+=nums[i];
            foreMax=foreMax+nums[i]<nums[i]?nums[i]:foreMax+nums[i]
            //判断之前记录的子数组和与当前新找的大小
            //更新最大子数组和
            resultMax=Math.max(resultMax, foreMax);
        }
        return resultMax;
    }
}

2.合并区间

 补充知识:Java中Arrays.sort()自定义排序

当sort()传入参数只是一个数组时,默认将数组按升序排列。

对数组的部分排序,传入起始位置和终止位置(不包括)

实现Comparator接口有两种方式,第一种是上面所示的匿名内部类实现。

第二种是新建一个类然后实现Comparator接口,再new一个类传入sort函数中。

Comparator的参数必须是泛型

将集合转为数组的方法:

merged.toArray(new int[merged.size()][]);

思路:

先按左端点排序

然后前一个区间的右端点大于下一区间的左端点,则可以合并--合并后的右端点取这两个区间右端点的最大值

小于左端点无法合并---这个操作是在新的记录合并后的新的 区间数组里进行的

自定义排序:按照区间的左端点从小到大排序

Arrays.sort(intervals, new Comparator<int[]>() {
            public int compare(int[] interval1, int[] interval2) {
                return interval1[0] - interval2[0];
            }
        });

lamda正则表达式

// 先按照区间起始位置排序
        Arrays.sort(intervals, (v1, v2) -> v1[0] - v2[0]);

代码:

class Solution {
    public int[][] merge(int[][] intervals) {
        // 所有区间按照左端点从小到大排序
        //lamda正则表达式
        Arrays.sort(intervals, (v1,v2)->v1[0]-v2[0]);
        //记录保存合并后的区间List,int不能改数组长度
        List<int[]> result=new ArrayList<>();
        //用于合并区间时,先把第一个区间放进result里,然后新来的再和这个比较
        //看是否能合并
        int index=-1; //用以记录result的索引,get(index)
        //也可以用result.size()记录,每次加的在result的尾端

        //合并区间
        for (int i = 0; i < intervals.length; i++) {
            int L=intervals[i][0]; int R=intervals[i][1];
            if (index==-1 || result.get(index)[1]<L) {
                index++;
                result.add(new int[]{L,R});
            }else {
                result.get(index)[1]=Math.max(R,result.get(index)[1]);
            }
        }

        return result.toArray(new int[index][]);
    }
}

3.轮转数组

思路:

3步翻转

先整体翻转,然后前k个翻转,右边再翻转

翻转函数:

左右两端交换位置,第一个元素与最后一个元素交换位置

public void reverse(int [] nums, int i,int j){
        while (i<=j) {
            int mid=nums[i];
            nums[i]=nums[j];
            nums[j]=mid;
            i++;j--;
        }
    }

代码:

class Solution { 
    public void rotate(int[] nums, int k) {
        if (k>nums.length) {
            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 i,int j){
        while (i<=j) {
            int mid=nums[i];
            nums[i]=nums[j];
            nums[j]=mid;
            i++;j--;
        }
    }

}

4.除自身以外数组的乘积

思路1:复杂度:O(n),O(n)

复杂度:O(n),O(n)

class Solution {
    public int[] productExceptSelf(int[] nums) {
         //定义左,右乘机与返回值 数组
        int[] L =new int[nums.length];
        int[] R =new int[nums.length];
        int[] ans =new int[nums.length];

        L[0]=1;//L数组赋值
        for (int i = 1; i < L.length; i++) {
            L[i]=L[i-1]*nums[i-1];
        }

        R[R.length-1]=1;//L数组赋值
        for (int i =R.length-2; i >=0; i--) {
            R[i]=R[i+1]*nums[i+1];
        }        

        for (int i = 0; i < ans.length; i++) {
            ans[i]=L[i]*R[i];
        }

        return ans;
    }
}

思路2:复杂度:O(n),空间复杂度O(1)

只用返回值数组ans

先在ans中记录i左侧的乘机

对于右侧的乘积,从最后一个元素开始遍历,边找右侧的,边乘到ans中

即完成

class Solution {
    public int[] productExceptSelf(int[] nums) {
        //返回值 数组
        int[] ans =new int[nums.length];

        ans[0]=1;//ans数组先记录左侧的乘积
        for (int i = 1; i < ans.length; i++) {
            ans[i]=ans[i-1]*nums[i-1];
        }

        int R=1;//动态记录右侧的值,并同时给ans
        for (int i =ans.length-1; i >=0; i--) {
            ans[i]=ans[i]*R;
            R=R*nums[i];  
        }        

        return ans;
    }
}

(运行上差不多)

5.缺失的第一个正数

思路1:O(n),空间复杂度O(n)

class Solution {
    public int firstMissingPositive(int[] nums) {
        int len = nums.length;

        Set<Integer> hashSet = new HashSet<>();
        for (int num : nums)  hashSet.add(num);


        for (int i = 1; i <= len ; i++) {
            if (!hashSet.contains(i)) return i;
        }

        return len + 1;
    }
}

思路2:O(n),空间复杂度O(1)

代码:

矩阵置零

四:矩阵

1.矩阵置零

思路:

class Solution {
    public void setZeroes(int[][] matrix) {
        int m=matrix.length;
        int n=matrix[0].length;
        //标记第1行,列是否有0
        boolean rawFlag =false ;
        boolean colFlag=false;

        //标记第1列是否有0
        for (int i = 0; i < m; i++) {if (matrix[i][0]==0) colFlag=true;}
        //标记第1行是否有0
        for (int j = 0; j < n; j++) {if (matrix[0][j]==0) rawFlag=true;}

        //用第1行,列标记矩阵的i,j位有0
        //从1开始
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (matrix[i][j]==0) {matrix[0][j]=0;matrix[i][0]=0;}
            }
        }

        //置0
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (matrix[i][0]==0||matrix[0][j]==0) 
                {matrix[i][j]=0;}
            }    
        }

        //第1行置0,//第1列置0

        if (colFlag) {for (int i = 0; i < m; i++) matrix[i][0]=0;}
        if (rawFlag) {for (int j = 0; j < n; j++) matrix[0][j]=0;}   
       

    }
}

2.螺旋矩阵

思路:

直接看代码,很好懂,

代码:

class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> res=new ArrayList<>();//返回值
        int up=0;int low=matrix.length-1; //low行
        int left=0;int right=matrix[0].length-1;//right 列
        
        while (true) {
            for (int j = left; j <= right; j++) res.add(matrix[up][j]);
            if (++up>low) break;
            for (int i = up; i <= low; i++) res.add(matrix[i][right]);
            if (--right<left) break;
            for (int j = right; j >= left; j--) res.add(matrix[low][j]);
            if (--low<up) break;
            for (int i = low; i >= up; i--) res.add(matrix[i][left]);
            if (++left>right) break;
        }
        return res;
    
    }
}

3:旋转图像

第1行变最后1列

思路:

中间矩阵,赋值过去,空间复杂度O(n2),

原地变换,一次4个元素一起换(用中间矩阵是两个元素一起换)

一次换4个,原地换两个,会被覆盖

换的思路:

第i行换到 倒数第i列(第n-1-i列,10个,倒数第2个,正数(10-2)个)

第j列换到 正数第j行

第一个元素A(i,j)   第二个元素B(j,n-1-i)

第三个元素C(n-1-i,n-1-j),第4个元素D(n-1-j,n-1-(n-1-i))

注意不是所有元素都换,否则会回到原矩阵,

n为偶数,前n/2行和n/2列的元素----4,(2,2)--

i  n/2,  j  n/2与 (n+1)/2等价   (4+1)/2=2

n为奇数前n/2行和(n+1)/1列的元素

i  n/2        j(n+1)/2  

代码

class Solution {
    public void rotate(int[][] matrix) {
        int n=matrix.length;
        for (int i = 0; i < n/2; i++) {
            for (int j = 0; j < (n+1)/2; j++) {
                int temp=matrix[i][j];
                // matrix[i][j]=matrix[j][n-1-i];  -逆时针,转置
                // matrix[j][n-1-i]=matrix[n-1-i][n-1-j];
                // matrix[n-1-i][n-1-j]=matrix[n-1-j][i];
                // matrix[n-1-j][i]=temp;

                matrix[i][j]=matrix[n-1-j][i];
                matrix[n-1-j][i]=matrix[n-1-i][n-1-j];
                matrix[n-1-i][n-1-j]=matrix[j][n-1-i];
                matrix[j][n-1-i]=temp;
            }
        }
    }
}

4.搜索二维矩阵 II

返回值为true,false

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/leeyayai_xixihah/article/details/137065106

力扣-牛客刷题第一天-爱代码爱编程

从排序数组中删除重复值 给定 nums = [0,0,1,1,1,2,2,3,3,4], 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。 你不需要考虑数组中超出新

力扣刷题day2_ade-my,自由的博客-爱代码爱编程

977 有序数组的平方 题目:力扣 第一反应: 先把每个数组的平方数计算出来,再进行排序 class Solution { public int[] sortedSquares(int[] nums) { for(int i=0; i<nums.length; i++){ nums[i] =

python力扣刷题|day02-有序数组的平方 &长度最小子数组 &螺旋矩阵_夜猫子不秃的博客-爱代码爱编程

力扣题目977.有序数组的平方 题目链接:https://leetcode.cn/problems/squares-of-a-sorted-array/ 题目描述:给你一个按 非递减顺序 排序的整数数组 nums,返回

我的力扣刷题顺序(参考代码回忆录)_金州饿霸的博客-爱代码爱编程

数组 数组过于简单,但你该了解这些!数组:二分查找数组:移除元素数组:序数组的平方数组:长度最小的子数组数组:螺旋矩阵II数组:总结篇 链表 关于链表,你该了解这些!链表:移除链表元素链表:设计链表链表:翻转链表链表:两两交换链表中的节点链表:删除链表的倒数第 N 个结点链表:链表相交链表:环形链表链表:总结篇! 哈希表 关于哈希表,你该了解这些!哈

力扣刷题day2 | 数组:滑动窗口 螺旋矩阵-爱代码爱编程

力扣题目:209. 长度最小的子数组 刷题时长:暴力法30min,滑动窗口30min 问题总结 审题不清就开始用贪心思路排序,求最短子数组变成了求最少子元素。暴力法两层for循环,超出题目时限。滑动窗口思路好理解,写码过程流畅,主要时间耗费在程序调试。bug出在了收缩窗口的条件判断上,此处应用while而不是if。f

力扣算法刷题day39|动态规划:不同路径 i&ii-爱代码爱编程

力扣题目:#62.不同路径 刷题时长:参考题解后10min 解题方法:动规 复杂度分析 时间O(m*n)空间O(m*n) 问题总结 初始化二维数组的python语法:i 对应 m,j 对应n二维遍历顺序,从上到下从左到右通过两层for循环实现,其中startindex应为1 本题收获 动规思路 确定dp数组及下标的含义:dp[i][j

【力扣hot100】刷题笔记day6-爱代码爱编程

前言 今天是社畜开工第一天,刷题刷题刷题💪 53. 最大子数组和 - 力扣(LeetCode) 贪心法 连续子数组累加和小于零就重新累加,【代码随想录】刷题笔记Day34-CSDN博客 class Solution: def maxSubArray(self, nums: List[int]) -> in

力扣刷题之滑动窗口_力扣 滑动窗口-爱代码爱编程

        滑动窗口是解决一类特定问题的有效技术,特别是涉及到数组和字符串的最长/最短子串/子数组问题。这种方法通过维护一个窗口来减少不必要的计算,使得时间复杂度通常降低到线性时间,即 O(N)。 滑动窗口解题思维框架 初始化维护变量:根据问题的需求,初始化一些变量来维护窗口内的特定信息,如计数、总和、最大/最小值等。 窗口的首尾端初始化:定义

leetcode hot 100刷题总结_hot100刷题-爱代码爱编程

文章目录 1 哈希1.1 1.两数之和🟢1.2 49.字母异位词分组🟡1.3 128.最长连续序列🟡 2 双指针2.1 283.移动零🟢2.2 15.三数之和🟡2.3 11.盛最多水的容器🟡2.4 42

python世界:力扣29题两数相除算法实践-爱代码爱编程

Python世界:力扣29题两数相除算法实践[ 任务背景实现思路模拟思路编码实现 本文小结 任务背景 本问题来自于力扣29题,在做完大数相乘后,顺带也看下两数相除。 给定两个整

leetcode面试经典150题-爱代码爱编程

看本文之前,强烈建议大家看一下我的另一篇文章,那个文章也是面试经典150题之一: Leetcode面试经典150题-114.二叉树展开为链表-CSDN博客 实现一个二叉搜索树迭代器类BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器: BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个

【每日一题】leetcode 2332.坐上公交的最晚时间(数组、双指针、二分查找、排序)-爱代码爱编程

【每日一题】LeetCode 2332.坐上公交的最晚时间(数组、双指针、二分查找、排序) 题目描述 给你一个下标从 0 开始长度为 n 的整数数组 buses ,其中 buses[i] 表示第 i 辆公交车的出发时间。

c++速通leetcode中等第3题-爱代码爱编程

双指针法:两个指针分别指向左右边界,记录最大面积,由于面积由短板决定,两个指针中较短的短指针向内移动一格,再次记录最大面积, 直到两指针相遇,得出答案。 class Solution { public: int maxArea(vector<int>& height) { int i = 0, j = he