滑动窗口算法专题

滑动窗口简介  

滑动窗口就是利用单调性,配合同向双指针来优化暴力枚举的一种算法。 

 该算法主要有四个步骤

1. 先进进窗口

2. 判断条件,后续根据条件来判断是出窗口还是进窗口

3. 出窗口

4.更新结果,更新结果这个步骤是不确定的,应题目要求判断是在进窗口前更新结果还是出窗口前更新结果。

1. 长度最小的子数组

题目链接:209. 长度最小的子数组 - 力扣(LeetCode)

题目细节信息:

1.数组中都是正数   2.在数组中找到长度最小的子数组且子数组中的元素和小于target值 

解法:滑动窗口

我们先定义一个left指针和right指针,left和right之间就是一个窗口。在定义一个变量sum来记录[left,right]区间的和。

根据题目要求,我们先确定第一个sum大于等于target值得右边界,所以我们先让right不断进窗口。

以题目中的实例1为例

接着我们根据sum的值来判断是否进窗口还是出窗口。sum的值无非会遇到两种情况。

当sum小于等于target的时候,我们先要更新最小长度和sum的值,接着在进窗口,即让left++ 

如下图: 

之前left指向的元素已经不在窗口里面了,这也是理解出窗口的一个图解。

当sum小于target时,我们进窗口进行了,即right++ 

滑动窗口的正确性

为什么能判断滑动窗口是对的呢?

因为数组中的数据都是正数,当right找到第一个边界使sum大于等于target的值时,我们就没必要让right继续向后走了,因为数组中都是正数,right继续走下去,sum会变大,但是len也会变长,此时len肯定就不是我们要的结果了。所以不让right向后走,就避免了其他不符合题意情况的枚举了。 

代码实现

代码一:我写的形式

    public int minSubArrayLen(int target, int[] nums) {
        int n=nums.length;
        int left=0,right=0;
        int ret=Integer.MAX_VALUE;
        int sum=nums[right];
        while(right<n){
            if(sum<target){
                right++;//进窗口
                if(right<n){
                    sum+=nums[right];
                }
            }else{
                ret=Math.min(ret,right-left+1);//更新长度最小值
                sum-=nums[left];//更新sum值
                left++;//出窗口
            }
        }
        return ret==Integer.MAX_VALUE?0:ret;
    }

代码二:

    public int minSubArrayLen(int target, int[] nums) {
        int n=nums.length;
        int len=Integer.MAX_VALUE;
        int sum=0;
        for(int left=0,right=0;right<n;right++){//进窗口
            sum+=nums[right];
            while(sum>=target){//这里要用while循环
                len=Math.min(len,right-left+1);//更新最小长度
                sum-=nums[left];//更新sum的值
                left++;//出窗口
            }
        }
        return len==Integer.MAX_VALUE?0:len;
    }

2.无重复字符的最长字串 

题目链接:3. 无重复字符的最长子串 - 力扣(LeetCode) 

题目解析:在字符串中找到一个连续且无重复字符的子字符串,并返回其长度。

解法一:暴力枚举(会超时)

枚举从字符串的第一个字符开始往后,无重复字符的子串可以到什么位置,找出一个最大长度的即可。

因此,我们可以通过一个哈希表来记录往后枚举的过程中,字符是否出现重复的情况。

//时间复杂度O(n^2)
//空间复杂度O(1)
import java.util.*;
class Solution {
    public int lengthOfLongestSubstring(String s) {
        int len = s.length();
        int ret = 0;
        for(int i = 0; i < len; i++) {
            int[] hash = new int[128];
            for(int j = i; j < len; j++) {
                hash[s.charAt(j)]++;
                if(hash[s.charAt(j)] > 1) {
                    break;
                }
                ret = Math.max(ret, j - i + 1);

            }
        }
        return ret;
    }
}

解法二:滑动窗口

此时,我们可以通过滑动窗口来优化上面的暴力枚举。

首先,先通过一种枚举的情况来分析,如下图

当我们枚举时,当right遇到第一个重复的字符a时,我们就不必要让right继续往后走了,因为前面的left的位置没变,当right继续往后走,left和right之间是一定有重复的字符的。

所以,此时,我们可以让right先固定在原地,先让left指针往后走,直到left指针跳过重复的字符,right才能继续完后走。

但是,当left往后走的时候,right没必要往回退到和left一样的位置,因为当left没有跳过第一个重复的字符时,right撑死只能走到第二个a的位置,且left往后走了,此时子串的长度肯定是比left没有往后走时短的。

两个指针没有回退,这时就可以使用滑动窗口了。

这里先解释下上面的hash数组的意思

这里hash数组的下标为字符串中字符的ASCII值,hash数组中的数据是该字符的出现次数。 

滑动川口的解题步骤:

1.进窗口:让字符进入hash表中

2.判断和出窗口:判断该字符是否重复出现,如果重复出现,则出窗口,即将该字符从hash表中删除。

3.更新结果:这里是先进行判断后,才执行更新的操作。

代码实现:

时间复杂度:O(n)
空间复杂度:O(n)
    public int lengthOfLongestSubstring(String ss) {
        int n=ss.length();
        char[] s=ss.toCharArray();
        int[] hash=new int[128];
        int left=0,right=0,ret=0;
        while(right<n){
            hash[s[right]]++;//进窗口
            while(hash[s[right]]>1){//判断字符是否重复出现
                hash[s[left++]]--;//出窗口
            }
            ret=Math.max(ret,right-left+1);//更新结果
            right++;
        }
        return ret;        
    }

3. 最大连续1的个数

题目链接:1004. 最大连续1的个数 III - 力扣(LeetCode)

题目解析:最多可以将数组中的k个0翻转为1,返回数组经过翻转后,数组中连续1的最大个数。

虽然题目中是要求我们翻转0,但是如果我们遇到0就将其翻转为1的话,接着进行新的枚举的时候,就又要将翻转过的0,重新翻转为0,此时,代码就会很难写,且很复杂。

其实,我们可以转换为求区间的长度,只要该区间的0的个数没有大于k个就行了。

解法一:暴力枚举+zero计数器

定义一个left和right指针,我们可以让left为起点向后枚举,定义一个zero变量来保存枚举过程中遇到0的个数 ,如果zero的值大于k,此时我们就可以让right指针停在该位置了,因为此时right如果继续走下去,left和right区间就是不符合题目要求了。

也就是说,此时以left为起点的枚举,得到的连续1的长度就是一个最优解了,此时就可以更新结果

所以,我们要换一个起点进行枚举,既让left++,接着让right回到left的位置,以新的left为起点继续,right继续向后枚举,重复上面的步骤。

解法二:滑动窗口

再暴力枚举上进一步优化,当我们让left++的时候,没必要将right重新指向left的位置,因为当zero的值大于k时,right撑死也时只能走到刚才停止的位置,只有我们left跳过一个0的时候,让zero减一,让zero的值小于k时,此时right才可以继续完后枚举。

此时,发现,left和right指针是同向运行,且不会退,我们就可以使用滑动窗口算法来解决该问题。

步骤一:进窗口

当right完后枚举时,遇到1就忽略,如果遇到0,就让zero的值加1

步骤二:判断+出窗口

当zero的值大于k时,我们就出窗口,也就时让left++,如果left遇到1就忽略,如果遇到0,就

让zero的值减1

步骤三:更新结果

此时,更新结果的步骤是在判断的步骤之后。 

代码实现:

//时间复杂度:O(n)
//空间复杂度:O(1) 
   public int longestOnes(int[] nums, int k) {
        int left=0,right=0,zero=0;
        int n=nums.length;
        int ret=0;
        while(right<n){
            if(nums[right]==1){
                right++;
            }else{
                right++;
                zero++;//出窗口
            }
            while(zero>k){//判断
                if(nums[left]==0){
                    zero--;//进窗口
                }
                left++;
            }
            ret=Math.max(ret,right-left);//更新结果
        }
        return ret;
    }

注意:这里更新结果为什么不是right-left+1呢?因为我是再right++之后再更新长度,而不是再right++之前更新长度。 

更简练版本:

    public int longestOnes(int[] nums, int k) {
         int ret = 0;
         for(int left = 0, right = 0, zero = 0; right < nums.length; right++){
                 if(nums[right] == 0) zero++; // 进窗⼝
                 while(zero > k) // 判断
                 if(nums[left++] == 0) zero--; // 出窗⼝
                 ret = Math.max(ret, right - left + 1); // 更新结果
          }
               return ret;
 }

这个版本就是再right++之前更新长度,所以更新长度时是right-left+1 

 4.将x减到0的最小操作数

题目链接:1658. 将 x 减到 0 的最小操作数 - 力扣(LeetCode)

题目解析:

1.我们每次进行一次删减操作时,我们都要将数组最左边或最右边的元素删去,供下一次删减操作时使用

2.数组里面的都是正数

在该题中,只有数组里面全是正数,我们才能使用滑动窗口解决

分析:因为当数组里面有负数或者为0的数据时,当right到达第一个停止的位置时,当我们的tmp减去一个负数或者数据为0的数时,由于负数或0的缘故,会导致[left,right]区间的和有可能是等于或大于tmp的,所以,此时进行新的枚举时,right有可能会向前退,也有可能继续向后退。 

解题思路转换:解决该题的时候,我们有时候会删去最左边的元素,有时候会删去最右边的元素,但是这种情况太复杂了,我们要转换思路。

正难则反:

题目要求我们找到数组两边使x值减为0的最小个数,我们就可以转换为求中间和为sum-x的最长子串。

解法一:暴力枚举

套两层for循环,遍历每一个子数组的情况,根据子数组的和是否等于target的值,我们就更新结果,接着break跳出一层循环。

解法二:滑动窗口

我们通过滑动窗口来优化枚举,我们每进行一次新的遍历的时候,我们发现没必要每次都让right回到left的位置,因为数组里面都是正数,当left++之后,[left,right]区间的和肯定是小于target的。

此时,发现left和right指针都不回退,此时就可以使用滑动窗口。

1.进窗口:tmp+=nums[right]

2.判断+出窗口:判断tmp的值是否大于target,如果大于出窗口,即nums-=nums[left++]

3.更新结果:当tmp的值等于target的时候,我们就可以更新结果。

//时间复杂度:O(n)
//空间复杂度:O(1)
    public int minOperations(int[] nums, int x) {
        int sum=0;
        for(int i=0;i<nums.length;i++){
            sum+=nums[i];
        }
        int ret=-1;
        int target=sum-x;
        //处理细节,
        if(target<0) return -1;
        for(int left=0,right=0,tmp=0;right<nums.length;right++){
            tmp+=nums[right];//进窗口
            while(tmp>target){//判断
                tmp-=nums[left++];//出窗口
            }
            if(tmp==target){
                ret=Math.max(ret,right-left+1);//更新结果
            }
        }
        if(ret==-1) return -1;//此时没有找到子数组和为target的子数组
        else return nums.length-ret;
    }

 5.水果成篮(从这里开始比较认真)

题目连接:904. 水果成篮 - 力扣(LeetCode) 

题目分析:有一个fruit数组,在数组中,不同元素的值代表不同的水果种类,相同元素的值代表水果的种类相同,我们有两个篮子,每个篮子只能装一种水果,一个篮子中装的水果数量没有限制。如果摘水果的过程中,一旦遇到与篮子中水果种类不同的水果树,就停止采摘,求这两个篮子能装的最大水果数量。 

以上可以总结为一句话:找一个连续的最长子数组,该子数组中的水果种类不超出两种。 

解法一:暴力枚举+哈希表 

我们可以将每一个子数组的情况枚举出来,枚举的过程中,我们可以借助一个hash表来保存枚举过程中采摘水果的种类和数量。

我们用left为外层循环的标志,right为内层循环的标志

枚举的小细节:

1. 为了防止在[1,2,1]的情况下,right指针会造成数组越界,所以我们每次进行一次内循环时,要对right指针进行判断,是否越界。

2.当篮子中水果的种类也超出两种时,也应该跳出该内层循环

3.在每一次采摘水果之前,必须判断水果种类为2种时,下次采摘的水果是否为第三种水果,如果成立,则跳出内部循环

4.每次进行一次新的外部枚举时,我们也要将hash表中上次枚举保存水果的情况删掉。

代码

//空间复杂度:O(n)
//时间复杂度:O(n^2)
    public int totalFruit(int[] f) {
        Map<Integer, Integer> hash = new HashMap<Integer, Integer>();
        int ret = 0;
        for (int left = 0; left < f.length; left++) {
            for (int right = left; right < f.length; right++) {
                if (right == f.length || hash.size() > 2) {//判断数组是否越界和水果种类的数量
                    break;
                }
                int in = f[right];
                if (hash.size() == 2 && !hash.containsKey(in)) {//摘水果之前的判断
                    break;
                }
                hash.put(in, hash.getOrDefault(in, 0) + 1);
                ret = Math.max(ret, right - left + 1);
            }
            hash.clear();//在下次枚举之前,删除此次枚举的情况
        }
        return ret;
    }

 解法二:滑动窗口

当我们进行新的left的枚举时,我们没必要让right重新回到left的位置,因为当left进行新的枚举时(left往后走),如果right重新返回到left指针的位置,往后枚举的时候,right会出现两种情况,第一种情况:right会停在在上一次left枚举时,right的最后枚举的位置或者超过right最后枚举的位置也就是说,以新的left为新的起点,进行right的枚举时,right是一定会经过之前的枚举停下的位置的,所以,我们就不必要将right返回到left的位置。

通过上面的分析,发现left和right指针是同向双指针,这时我们就可以使用滑动窗口来解决这个问题。 

解题步骤

1.定义left=0,right=0

2.进窗口:让hash[f[right]]++

3.判断+出窗口(一个循环):

        当hash.size>2时,让hash[f[left]]--,同时让left++

4.更新长度 

用哈希表来实现:

//时间复杂度:O(n)
//空间复杂度:O(n)
    public int totalFruit(int[] f) {
        Map<Integer,Integer> hash=new HashMap<Integer,Integer>();
        int ret=0;
        for(int left=0,right=0;right<f.length;right++){
            int in=f[right];
            hash.put(in,hash.getOrDefault(in,0)+1);//进窗口
            while(hash.size()>2){//判断
                int out=f[left];
                hash.put(out,hash.get(out)-1);//出窗口
                if(hash.get(out)==0){
                    hash.remove(out);
                }
                left++;
            }
            ret=Math.max(ret,right-left+1);//更新长度
        }
        return ret;
    }

用数组来实现: 

    public int totalFruit(int[] f) {
        int n=f.length;
        int[] hash=new int[n+1];
        int ret=0;
        for(int left=0,right=0,kind=0;right<n;right++){
            int in=f[right];
            if(hash[in]==0){
                kind++;
            }
            hash[in]++;//进窗口
            while(kind>2){//判断
                int out=f[left];
                hash[out]--;//出窗口
                if(hash[out]==0){
                    kind--;
                }
                left++;
            }
            ret=Math.max(ret,right-left+1);//更新长度
        }
        return ret;
    }

 6.找出字符串中所有的异位字符串

题目链接:438. 找到字符串中所有字母异位词 - 力扣(LeetCode) 

题目解析:再s字符串中找出p字符串中的所有异位字符串

异位字符串就是相同字符但是字符的顺序不一样组成的字符串,如abc、acb、bac、bca、cab和cba都是abc的异位字符串。 

解决思路:滑动窗口+哈希表

如何判断两个字符串互为异位字符串呢?

第一种方法:我们可以将两个字符串按字典的顺序进行排序,接着判断这两个字符串是否相同就行,如果相同,那么就是异位字符串,如果不同,则不是异位字符串。

第二种方法:借用两个哈希表,hash1来存储p字符串,hash2来存储s字符串,最终判断两个哈希表是否相同,如果相同,那么互为异位字符串,如果不同,则不是异位字符串。 

 以下图为例

我们在s中找p的异位字符串,我们使用暴力枚举时,第一次枚举时, 当right-left+1长度为p.length()时,第一次枚举就结束了,如下图

此时第一次枚举就结束了,进行第二次枚举,让left++,同时让right返回到left的所处的位置

进行第二次枚举时,right继续走向right-left+1=p.length()的位置,此时发现right依然会进过之前的字符b和字符a

所以,每一次进行新的枚举,没必要让right回到left的位置,这样left和right都是同向双指针,这时,我们就可以用滑动窗口来解决问题。

 进窗口:当right-left+1<=s.length时,hash2[in]++

 判断+出窗口

        如果right-left+1>p.length(),就出窗口,让hash2[out]--,同时让left++

更新结果

        要判断是异位字符串在更新结果

代码实现

//时间复杂度:O(n+m)
//空间复杂度:O(n+m)
    public List<Integer> findAnagrams(String ss, String pp) {
        List<Integer> ret=new ArrayList<>();
        char[] s=ss.toCharArray();
        char[] p=pp.toCharArray();
        int[] hash1=new int[26];
        int[] hash2=new int[26];
        for(char ch:p){
            hash1[ch-'a']++;
        }
        for(int left=0,right=0;right<s.length;right++){
            char in=s[right];
            hash2[in-'a']++;//进窗口
            if(right-left+1>p.length){//判断
                char out=s[left];
                hash2[out-'a']--;//出窗口
                left++;
            }
            //更新结果
            if(right-left+1==p.length){//判断是否互为异位字符串
                boolean flag=true;
                for(int i=0;i<26;i++){
                    if(hash1[i]!=hash2[i]){
                        flag=false;
                    }
                }
                if(flag==true)  ret.add(left);//更新结果
            }
        }
        return ret;
    }

进一步优化,此时我们可以在更新结果那里优化一下,因为在更新结果那里我们需要遍历两个哈希表,时间复杂度还是太大了,我们可以通过一个变量count统计窗口中的有效字符个数。

下图解释了什么是有效字符,一次类推 

前面我们用hash1统计了字符串p中的情况,用hash2统计了字符串s中的情况。

进窗口之后,我们对hash1[in]和hash2[in]进行比较,如果hash2[in]小于等于hash1[in],那么此时就可以认为该字符是一个有效字符,让count++

出窗口之前,我们对hash1[out]和hash2[out]进行比较,如果hash2[out]小于等于hash[out],此时就可以认为出去的字符是一个有效字符,让count--

最终在更新结果的时候,我们只要判断count是否等于p.length,如果等于p.length,就更新结果,如果不等于p.length,就不更新结果。

代码实现

       public List<Integer> findAnagrams(String ss, String pp) {
        List<Integer> ret=new ArrayList<>();
        char[] s=ss.toCharArray();
        char[] p=pp.toCharArray();
        int[] hash1=new int[26];
        int[] hash2=new int[26];
        for(char ch:p){
            hash1[ch-'a']++;
        }
        for(int left=0,right=0,count=0;right<s.length;right++){
            char in=s[right];
            hash2[in-'a']++;//进窗口
            if(hash2[in-'a']<=hash1[in-'a']) count++;//更新有效字符个数
            if(right-left+1>p.length){//判断
                char out=s[left];
                if(hash2[out-'a']<=hash1[out-'a']) count--;//更新有效字符个数
                hash2[out-'a']--;//出窗口
                left++;
            }
            //更新结果
            if(count==p.length) ret.add(left);
        }
        return ret;
    }

7.串联所有单词的子串 

题目链接:30. 串联所有单词的子串 - 力扣(LeetCode) 

 

我们如果将words的每一个字符串看成一个整体,在以这个整体为单位去在s中找串联所有单词的子串,这时就跟找异位字符串差不多了。如下图

 

 这道题的思路和第6题的解题思路差不多,这里讲不同点,假设words数组字符串的长度为m,数组的长度为len

1.right和left指针的位移

        right和left一次位移的长度为m

2.hash表的不同

        这里的哈希表存的是字符串的种类和数量,即Hash<String,Integer>

3.进行滑动窗口的次数

        次数为m次

这里对不同点3进行解释

 

 代码实现:

    public List<Integer> findSubstring(String s, String[] words) {
        Map<String,Integer> hash1=new HashMap<String,Integer>();//存储words
        List<Integer> ret=new ArrayList<Integer>();
        for(String str:words){
            hash1.put(str,hash1.getOrDefault(str,0)+1);
        }
        int len=words[0].length(), m=words.length;
        for(int i=0;i<len;i++){
            //执行滑动窗口
            Map<String,Integer> hash2=new HashMap<String,Integer>();//存储s
            for(int left=i,right=i,count=0;right+len<=s.length();right+=len){
                String in=s.substring(right,right+len);
                hash2.put(in,hash2.getOrDefault(in,0)+1);
                if(hash2.get(in)<=hash1.getOrDefault(in,0)) count++;
                //判断+出窗口
                while(right-left+1>m*len){
                    String out=s.substring(left,left+len);
                    if(hash2.get(out)<=hash1.getOrDefault(out,0)) count--;
                    hash2.put(out,hash2.getOrDefault(out,0)-1);
                    left+=len;
                }
                //更新结果
                if(count==m) ret.add(left);
            }
        }
        return ret;
    }

8.最小覆盖子串 

题目链接:76. 最小覆盖子串 - 力扣(LeetCode) 

 

题目分析:我们需要再字符串s中找一个子串,在这个子串中的字符种类,需要包含字符串t中所有字符类型,如果t中有了重复的字符,那么从s中找的子串的该字符的数量必须大于等于t中的重复字符的数量。

比如t为aba,那么在s中找的子串中,字符a的数量是不能小于t中a的字符数量。 

解题思路:

首先,我们还是想到暴力枚举+hash表,我们将字符串中s的字符都枚举一遍,在枚举的过程中,如果遇到符合题目要求的子串,此时,就更新结果,接着立刻换一个字符进行新的枚举,以此类推下去。

优化思路:滑动窗口

如下图 

 

此时,我们需要让left往后移动一步,当left++之后,left和right之间就会出现两种情况。

第一种情况:

如果left++之后,left和right之间的字符种类没有发生变化,当我们让right回到left的位置,进行新的枚举时,right还是会回到原来的位置。如下图 

 

第二种情况: 

如果left++之后,left和right之间的字符种类发生变化,当我们让right回到left的位置时,进行新的枚举,为了找到新的符合题目要求的子串,此时right肯定时跑到原来位置的后面。如下图: 

 

所以,我们发现没必要每次都要将right返回到原来left的位置,我们只要根据每次left移动后的情况,根据情况来让right不动或者往后移动。 

此时,发现left和right都是同向双指针,此时就可以使用滑动窗口。 

在使用滑动窗口前,我们要用到两个哈希表,一个用来存储s字符串的情况,另一个用来存储t字符串的情况。 

 进窗口:hash2[in]++

 判断+更新结果+出窗口:

        判断:在s中找的子串是否符合题目要求,也就是hash2与hash1对应字符的数量是否一 样

        跟新结果:由于我们这里找到一个符合题目要求的子串就要跟新结果,所以此处的跟新结果实在出窗口之前

        出窗口:hash2[out]--

在进一步优化:更新结果

在更新结果那里,我们需要遍历两个哈希表来判断找的子串是否符合题目要求,每个哈希表都要遍历一遍,时间复杂度还是太大了,此时,我们可以通过count变量来统计hash1和hash2中完全相同的字符数量。 

这里的完全相同是指数量和种类都相同。 

在进窗口之后,如果hash2[in]==hash1[in],那么,我们认为在in在hash1和hash2中是完全相同的字符,则让count++

出窗口之前,如果如果hash2[in]==hash1[in],那么,我们认为在in在hash1和hash2中是完全相同的字符,则让count-- 

此时判断条件就变为count==hash1.size().

代码实现:

    public String minWindow(String ss, String tt) {
        char[] s=ss.toCharArray();
        char[] t=tt.toCharArray();
        int[] hash1=new int[128];//存储t
        int[] hash2=new int[128];//存储s;
        int kinds=0;//记录t中有效字符的种类
        for(char ch:t){
            if(hash1[ch]==0) kinds++;
            hash1[ch]++;
        }
        int minlen=Integer.MAX_VALUE, begin=-1;
        for(int left=0,right=0,count=0;right<s.length;right++){
            //进窗口
            char in=s[right];
            hash2[in]++;
            if(hash2[in]==hash1[in]) count++;
            //判断+更新+出窗口
            while(kinds==count){
                //更新结果
                if(right-left+1<minlen){
                    minlen=right-left+1;
                    begin=left;
                }
                //出窗口
                char out=s[left];
                if(hash2[out]==hash1[out]) count--;
                hash2[out]--;//出窗口
                left++;//维护窗口
            }
        }
        if(begin==-1) return new String();
        else return ss.substring(begin,begin+minlen);
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/935795.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Prime2_解法二:openssl解密凭据

Prime2_解法二&#xff1a;openssl解密凭据 本博客提供的所有信息仅供学习和研究目的&#xff0c;旨在提高读者的网络安全意识和技术能力。请在合法合规的前提下使用本文中提供的任何技术、方法或工具。如果您选择使用本博客中的任何信息进行非法活动&#xff0c;您将独自承担全…

R语言的数据结构-向量

【图书推荐】《R语言医学数据分析实践》-CSDN博客 《R语言医学数据分析实践 李丹 宋立桓 蔡伟祺 清华大学出版社9787302673484》【摘要 书评 试读】- 京东图书 (jd.com) R语言编程_夏天又到了的博客-CSDN博客 在R语言中&#xff0c;数据结构是非常关键的部分&#xff0c;它提…

SpringBoot使用Nacos进行application.yml配置管理

Nacos是阿里巴巴开源的一个微服务配置管理和服务发现的解决方案。它提供了动态服务发现、配置管理和 服务管理平台。Nacos的核心功能包括服务发现、配置管理和动态服务管理&#xff0c;使得微服务架构下的服务治理 变得简单高效。 Nacos的设计基于服务注册与发现、配置管理、动…

【Java学习笔记】Collections 工具类

一、基本介绍 Collections 是一个操作 Set、List 和 Map 等集合的工具类Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作 二、排序操作&#xff1a;&#xff08;均为 static 方法) 三、查找、替换

【含开题报告+文档+PPT+源码】基于ssm框架的购物交流圈交流及市场服务平台

开题报告 随着互联网技术的迅猛发展&#xff0c;社交网络和电子商务已成为人们日常生活中不可或缺的一部分。特别是在移动互联网的普及下&#xff0c;人们越来越倾向于通过在线平台进行交流、分享和购物。这种趋势为基于 SSM&#xff08;Spring、SpringMVC、MyBatis&#xff0…

Datawhale AI 冬令营(第一期)定制你的第一个专属模型-学习笔记

最近我报名参加了Datawhale组织的主题为“动手学系列&#xff0c;人人都能应用的AI”的Datawhale AI冬令营&#xff08;第一期&#xff09;。 本次学习一共12天&#xff0c;从12月10日-12月21日&#xff0c;学习会包含【跑通速通手册】&#xff0c;【学习大模型微调&数据集…

GS-SLAM论文阅读--RGBDS-SLAM

前言 最近GS-SLAM领域的工作层出不穷&#xff0c;有很多不错的工作出现。接下来慢慢写一下相关博客。 文章目录 前言1.背景介绍2.关键内容2.1 3D多层次金字塔高斯喷溅2.2 紧密耦合多特征重构优化2.3总体流程 3.文章贡献4.个人思考 1.背景介绍 高保真重建是密集SLAM的关键。最…

使用EventLog Analyzer进行Apache日志监控和日志分析

一、什么是Apache日志分析 Apache日志分析是网站管理和维护的重要部分&#xff0c;通过分析Apache服务器生成的日志文件&#xff0c;可以了解网站的访问情况、识别潜在的安全问题、优化网站性能等。 二、Apache日志类型 Apache日志主要有两种类型&#xff1a;访问日志&a…

[计算机网络]IP地址推行的“书同文,车同轨”

硬件地址无法直接转换的故事 在很久很久以前&#xff0c;网络世界就像一个庞大的帝国&#xff0c;各个村落&#xff08;网络&#xff09;都有自己的语言&#xff08;硬件地址&#xff09;。每个村落都有自己的规则和习惯&#xff0c;村里的每户人家&#xff08;设备&#xff0…

鸿蒙NEXT开发案例:九宫格随机

【引言】 在鸿蒙NEXT开发中&#xff0c;九宫格抽奖是一个常见且有趣的应用场景。通过九宫格抽奖&#xff0c;用户可以随机获得不同奖品&#xff0c;增加互动性和趣味性。本文将介绍如何使用鸿蒙开发框架实现九宫格抽奖功能&#xff0c;并通过代码解析展示实现细节。 【环境准…

idea 配置 git .gitignore文件配置

.gitignore 内容 .idea/ *.iml target/ *.class *.log .iml在idea项目里面创建一个.gitignore名字的文件&#xff0c;然后把这个文件提交到git上。我一般是放到.idea同级目录。 我遇到了几种情况这个文件配置了但是不生效的情况 第一种 Git的缓存可能会导致配置不生效。尝试…

H.323音视频协议

概述 H.323是国际电信联盟&#xff08;ITU&#xff09;的一个标准协议栈&#xff0c;该协议栈是一个有机的整体&#xff0c;根据功能可以将其分为四类协议&#xff0c;也就是说该协议从系统的总体框架&#xff08;H.323&#xff09;、视频编解码&#xff08;H.263&#xff09;、…

快速掌握Quartz.Net计划任务调度框架,轻松实现定时任务

前言 Quartz.Net是一个开源的作业调度框架&#xff0c;可以用于管理计划任务和定期执行。Quartz.Net提供了丰富的作业计划选项&#xff0c;例如精确或模糊时间表达式、日期和时间限制等。Quartz.Net采用分布式架构&#xff0c;允许在多个计算机上运行任务。 Quartz.Net架构设…

【开源大屏】玩转开源积木BI,从0到1设计一个大屏

积木 BI 重磅推出免费大屏设计器&#xff01;功能超强大&#xff0c;操作超流畅&#xff0c;体验超酷炫。快来体验一下吧。 让我们一起来看一下如何从0到1设计一个大屏。 一、积木BI大屏介绍 积木BI可视化数据大屏 是一站式数据可视化展示平台&#xff0c;旨在帮助用户快速通…

【JVM】JVM基础教程(四)

上一章&#xff1a;【JVM】JVM基础教程&#xff08;三&#xff09;-CSDN博客 目录 自动垃圾回收 方法区的回收 方法区回收条件 手动触发回收 堆回收 如何判断堆上的对象可以回收&#xff1f; 可以给对象引用赋值null&#xff0c;切断引用 引用计数法 循环引用缺点 查…

数据结构(Queue队列)

前言&#xff1a; 在计算机科学中&#xff0c;数据结构是构建高效算法和程序的基础&#xff0c;而队列&#xff08;Queue&#xff09;作为一种经典的线性数据结构&#xff0c;具有重要的地位。与栈&#xff08;Stack&#xff09;不同&#xff0c;队列遵循“先进先出”&#xf…

DevExpress WPF中文教程:Grid - 如何移动和调整列大小?(一)

DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序&#xff0c;这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…

语言模型(序列模型)

终于快要毕业了&#xff0c;乘着还在还在研究室&#xff0c;把最后一章sequence模型也学完吧。 Sequence Model 一&#xff1a;基础知识1&#xff1a;符号的定义2&#xff1a;词典(Vocabulary) 与编码(Encoding) 二&#xff1a;RNN(Recurrent Neural Networks) 循环神经网络1&…

MySQL(表的约束)

目录 1. 空属性(NULL) 2. 默认值(default) 3. 列描述(comment) 4. zerofill 5. 主键(primary_key) 6. 自增长(auto_increment) 7. 唯一键(uniqie) 8. 外键(foreign key (字段名) references 主表(列) 表的约束&#xff1a;表中有各种类型&#xff0c;每个类型插入的数据…

笔记本电脑升级硬盘存储、Windows10系统安装及后续步骤(以联想ThinkPad X1 Carbon Gen10为例)

文章目录 1.前言2.材料准备3.Win10系统安装盘制作3.1 系统下载3.2 系统启动U盘刻录 4.拆机更换硬盘5.开机启动项修改6.系统安装&#xff08;以Win10为例&#xff09;7.系统安装后可能需要的步骤7.1 缺少WIFI等网络驱动7.2 系统激活7.3 办公软件安装 8.旧硬盘变废为宝参考文献 1…