双指针算法专题

这篇具有很好参考价值的文章主要介绍了双指针算法专题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

双指针算法入门,干就完了

下面的题目都是来自灵神的基础算法精讲,有思路不清晰的地方,可以去看讲解。

灵茶山艾府的个人空间-灵茶山艾府个人主页-哔哩哔哩视频 (bilibili.com)

相向双指针

1.两数之和

题目链接:167. 两数之和 II - 输入有序数组 - 力扣(LeetCode)

题目描述

给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1]numbers[index2] ,则 1 <= index1 < index2 <= numbers.length

以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1index2

你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。

你所设计的解决方案必须只使用常量级的额外空间。

示例 1:

输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。

示例 2:

输入:numbers = [2,3,4], target = 6
输出:[1,3]
解释:2 与 4 之和等于目标数 6 。因此 index1 = 1, index2 = 3 。返回 [1, 3] 。

示例 3:

输入:numbers = [-1,0], target = -1
输出:[1,2]
解释:-1 与 0 之和等于目标数 -1 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。

提示:

  • 2 <= numbers.length <= 3 * 104
  • -1000 <= numbers[i] <= 1000
  • numbers非递减顺序 排列
  • -1000 <= target <= 1000
  • 仅存在一个有效答案

代码

C++
class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int n = numbers.size();
        int left = 0;
        int right = n - 1;
        while (left < right) {
            int s = numbers[left] + numbers[right];
            if (s == target) {
                break;
            } else if (s > target) {
                right -= 1;
            } else {
                left += 1;
            }
        }
        return {left + 1, right + 1};
    }
};
Java
class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int n = numbers.length;
        int[] res = new int[2]; // 定义数组,用于最终答案的存储
        int left = 0;
        int right = n - 1;
        while (left < right) {
            int s = numbers[left] + numbers[right];
            if (s == target) {
                res[0] = left + 1;
                res[1] = right + 1;
                break;
            } else if (s > target) {
                right -= 1;
            } else {
                left += 1;
            }
        }
        return res;
    }
}

2.三数之和

题目链接:15. 三数之和 - 力扣(LeetCode)

题目描述

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

**注意:**答案中不可以包含重复的三元组。

示例 1:

输入: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] 。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:

输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。

示例 3:

输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。

提示:

  • 3 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

代码

C++
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(), nums.end()); // 对nums数组排序
        vector<vector<int>> ans;        // 最终结果
        int n = nums.size();            // nums数组的长度
        for (int i = 0; i < n - 2; i++) { // i < n - 2是要给left、right留下位置
            int x = nums[i];
            if (i && x == nums[i - 1])
                continue; // 跳过重复数字
            if (x + nums[i] + nums[i + 1] > 0)
                break; // 因为nums是排好序的,数组的前三个数相加都大于0了,那么后面的数字相加不可能等于0
            if (x + nums[n - 1] + nums[n - 2] < 0)
                continue; // 如果当前的x与最大的两个数相加小于0,那么x与中间的任意数字相加都小于0,直接可以枚举下一个x了。
            int left = i + 1;
            int right = n - 1;
            while (left < right) {
                int s = x + nums[left] + nums[right]; // 先计算出三者相加的结果
                if (s > 0)
                    right--; // s > 0的话,代表右边的数大了,所以右指针左移
                else if (s < 0)
                    left++; // s < 0的话,代表左边的数小了,所以左指针右移
                else {
                    ans.push_back({x, nums[left], nums[right]});
                    left++;
                    while (left < right &&
                           nums[left] == nums[left - 1]) { // 跳过重复数字
                        left++;
                    }
                    right--;
                    while (right > left &&
                           nums[right] == nums[right + 1]) { // 跳过重复数字
                        right--;
                    }
                }
            }
        }
        return ans;
    }
};
Java
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums); // 数组排序
        List<List<Integer>> ans = new ArrayList<>(); // 最终结果
        int n = nums.length; // 数组长度
        for (int i = 0; i < n - 2; i++) { // 遍历数组,留出位置给左右指针
            int x = nums[i];
            if (i > 0 && x == nums[i - 1])
                continue; // 跳过重复数字
            if (x + nums[i + 1] + nums[i + 2] > 0)
                break; // 如果前三个数相加已经大于0,后面的数字相加不可能等于0
            if (x + nums[n - 1] + nums[n - 2] < 0)
                continue; // 如果当前数字与最大的两个数字相加小于0,直接枚举下一个数字
            int left = i + 1;
            int right = n - 1;
            while(left < right){
                int s = x + nums[left] + nums[right];
                if(s > 0) right--; // 右指针左移
                else if(s < 0) left++; // 左指针右移
                else{
                    ans.add(List.of(x,nums[left],nums[right]));
                    left++;
                    while (left < right && nums[left] == nums[left - 1]) {
                        left++; // 跳过重复数字
                    }
                    right--;
                    while (right > left && nums[right] == nums[right + 1]) {
                        right--; // 跳过重复数字
                    }
                }
            }
        }
        return ans;
    }
}

3.最接近的三数之和

题目链接:16. 最接近的三数之和 - 力扣(LeetCode)

题目描述

给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。

返回这三个数的和。

假定每组输入只存在恰好一个解。

示例 1:

输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。

示例 2:

输入:nums = [0,0,0], target = 1
输出:0

提示:

  • 3 <= nums.length <= 1000
  • -1000 <= nums[i] <= 1000
  • -104 <= target <= 104

优化来自灵神,不得不说,灵神yyds,下面为灵神的题解

16. 最接近的三数之和 - 力扣(LeetCode)

代码

C++
class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        sort(nums.begin(),nums.end());
        int n = nums.size();
        int minDiff = INT_MAX;
        int ans = 0;
        for (int i = 0; i < n - 2; i++) {
            int x = nums[i];
            // 优化三
            if(i > 0 && x == nums[i - 1]){
                continue;
            }

            // 优化一
            int res = x + nums[i+1] + nums[i+2];
            if(res > target){ // 后面无论怎么选,选出的三个数的和不会比res小
                if(res - target < minDiff){
                    ans = res;
                }
                break;
            }

            // 优化二
            res = x + nums[n - 1] + nums[n - 2];
            if(res < target){ // x 加上后面任意两个数都不超过 s,所以下面的双指针就不需要跑了
                if(target - res < minDiff){
                    minDiff = target - res;
                    ans = res;
                }
                continue;
            }

            // 双指针
            int left = i + 1, right = n - 1;
            while (left < right) {
                int res = x + nums[left] + nums[right];
                if (res == target) {
                    return target;
                } else if (res < target) {
                    if (target - res < minDiff) { // 找到更接近 target 的数
                        minDiff = target - res;
                        ans = res;
                    }
                    left++;
                } else {// res > target
                    if (res - target < minDiff) { // 找到更接近 target 的数
                        minDiff = res - target;
                        ans = res;
                    }
                    right--;
                }
            }
        }
        return ans;
    }
};
Java
class Solution {
    public int threeSumClosest(int[] nums, int target) {
        Arrays.sort(nums);
        int n = nums.length;
        int minDiff = Integer.MAX_VALUE;
        int ans = 0;

        for (int i = 0; i < n - 2; i++) {
            int x = nums[i];

            // 优化三
            if (i > 0 && x == nums[i - 1]) {
                continue;
            }

            // 优化一
            int res = x + nums[i + 1] + nums[i + 2];
            if (res > target) { // 后面无论怎么选,选出的三个数的和不会比res小
                if (res - target < minDiff) {
                    ans = res;
                }
                break;
            }

            // 优化二
            res = x + nums[n - 1] + nums[n - 2];
            if (res < target) { // x 加上后面任意两个数都不超过 s,所以下面的双指针就不需要跑了
                if (target - res < minDiff) {
                    minDiff = target - res;
                    ans = res;
                }
                continue;
            }

            // 双指针
            int left = i + 1, right = n - 1;
            while (left < right) {
                res = x + nums[left] + nums[right];
                if (res == target) {
                    return target;
                } else if (res < target) {
                    if (target - res < minDiff) { // 找到更接近 target 的数
                        minDiff = target - res;
                        ans = res;
                    }
                    left++;
                } else { // res > target
                    if (res - target < minDiff) { // 找到更接近 target 的数
                        minDiff = res - target;
                        ans = res;
                    }
                    right--;
                }
            }
        }
        return ans;
    }
}

4.盛最多水的容器

题目链接:11. 盛最多水的容器 - 力扣(LeetCode)

题目描述

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0)(i, height[i])

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

**说明:**你不能倾斜容器。

示例 1:

双指针算法专题,算法学习,算法,双指针,leetcode

输入:[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

代码

C++
class Solution {
public:
    int maxArea(vector<int>& height) {
        int n = height.size();
        int left = 0;
        int right = n - 1;
        int res = 0,area = 0;
        while(left < right){
            area = (right - left) * min(height[left],height[right]);
            res = max(res,area);
            if(height[left] < height[right]){
                left++;
            } else {
                right--;
            }
        }
        return res;
    }
};
Java
class Solution {
    public int maxArea(int[] height) {
        int n = height.length;
        int left = 0;
        int right = n - 1;
        int res = 0;
        while(left < right){
            int area = Math.min(height[left],height[right]) * (right - left);
            res = Math.max(res,area);
            if(height[left] < height[right]){
                left++;
            }else{
                right--;
            }
        }
        return res;
    }
}

5.接雨水

题目链接:42. 接雨水 - 力扣(LeetCode)

题目描述

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例 1:

双指针算法专题,算法学习,算法,双指针,leetcode

输入: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

代码

C++
class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size();
        int left = 0, right = n - 1;
        int pre_max = 0, suf_max = 0;
        int ans = 0;
        while (left < right) {
            pre_max = max(pre_max, height[left]); // 更新前缀最大值
            suf_max = max(suf_max, height[right]); // 更新后缀最大值
            if (pre_max < suf_max) { // 柱子雨水高度是该柱子前后缀最高柱子的较小者
                ans += pre_max - height[left]; // 当前位置雨水高度减去柱子高度
                left++; // 左指针右移
            } else {
                ans += suf_max - height[right]; // 当前位置雨水高度减去柱子高度
                right--; // 右指针左移
            }
        }
        return ans;
    }
};
Java
class Solution {
    public int trap(int[] height) {
        int n = height.length;
        int left = 0, right = n - 1;
        int pre_max = 0, suf_max = 0;
        int ans = 0;
        while (left < right) {
            pre_max = Math.max(pre_max, height[left]); // 更新前缀最大值
            suf_max = Math.max(suf_max, height[right]); // 更新后缀最大值
            if (pre_max < suf_max) { // 柱子雨水高度是该柱子前后缀最高柱子的较小者
                ans += pre_max - height[left]; // 当前位置雨水高度减去柱子高度
                left++; // 左指针右移
            } else {
                ans += suf_max - height[right]; // 当前位置雨水高度减去柱子高度
                right--; // 右指针左移
            }
        }
        return ans;
    }
}

同向双指针(滑动窗口)

1.长度最小的子数组

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

题目描述

给定一个含有 n 个正整数的数组和一个正整数 target

找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度**。**如果不存在符合条件的子数组,返回 0

示例 1:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2:

输入:target = 4, nums = [1,4,4]
输出:1

示例 3:

输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

提示:

  • 1 <= target <= 109
  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 105

思路

滑动窗口文章来源地址https://www.toymoban.com/news/detail-819451.html

代码

C++
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int n = nums.size();
        int ans = n + 1;
        int left = 0, s = 0;
        for (int right = 0; right < n; right++) {
            s += nums[right];
            while (s >= target) {
                ans = min(ans, right - left + 1);
                s -= nums[left];
                left++;
            }
        }
        return ans <= n ? ans : 0;
    }
};
Java
class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int n = nums.length;
        int ans = n+1;
        int left = 0, s = 0;
        for(int right = 0;right < n; right++){
            s += nums[right];
            while(s >= target){
                ans = Math.min(ans, right - left +1);
                s -= nums[left];
                left++;
            }
        }
        return ans <= n ? ans : 0;
    }
}

2.乘积小于K的子数组

题目链接:713. 乘积小于 K 的子数组 - 力扣(LeetCode)

题目描述

给你一个整数数组 nums 和一个整数 k ,请你返回子数组内所有元素的乘积严格小于 k 的连续子数组的数目。

示例 1:

输入:nums = [10,5,2,6], k = 100
输出:8
解释:8 个乘积小于 100 的子数组分别为:[10]、[5]、[2],、[6]、[10,5]、[5,2]、[2,6]、[5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于 100 的子数组。

示例 2:

输入:nums = [1,2,3], k = 0
输出:0

提示:

  • 1 <= nums.length <= 3 * 104
  • 1 <= nums[i] <= 1000
  • 0 <= k <= 106

思路

滑动窗口

代码

C++
class Solution {
public:
    int numSubarrayProductLessThanK(vector<int>& nums, int k) {
        int n = nums.size();
        int ans = 0; // 结果
        int left = 0;
        int s = 1;

        if(k <= 1){
            return 0;
        }

        for(int right = 0; right < n; right++){
            s *= nums[right];
            while( s >= k){
                s /= nums[left];
                left++;
            }
            ans += right - left + 1;
        }
        return ans;
    }
};
Java
class Solution {
    public int numSubarrayProductLessThanK(int[] nums, int k) {
        int n = nums.length;
        int ans = 0;
        int s = 1;
        int left = 0;

        if(k <= 1){
            return 0;
        }

        for(int right = 0;right < n; right++){
            s *= nums[right];
            while(s >= k){
                s /= nums[left];
                left++;
            }
            ans += right - left + 1;
        }
        return ans;
    }
}

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

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

题目描述

给定一个字符串 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 由英文字母、数字、符号和空格组成

思路

滑动窗口

代码

C++
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int n = s.length();
        int ans = 0, left = 0;
        unordered_set<char> window; // 维护当前子串的字符集合
        for (int right = 0; right < n; right++) {
            char c = s[right];
            while (window.count(c)) {
                window.erase(s[left]);
                left++;
            }
            window.insert(c);
            ans = max(ans, right - left + 1);
        }
        return ans;
    }
};
Java
class Solution {
    public int lengthOfLongestSubstring(String s) {
        int n = s.length();
        int ans = 0, left = 0;
        HashSet<Character> window = new HashSet<>();
        for (int right = 0; right < n; right++) {
            char c = s.charAt(right);
            while (window.contains(c)) { // 判断是否重复
                window.remove(s.charAt(left));
                left++;
            }
            window.add(c);
            ans = Math.max(ans, right - left + 1);
        }
        return ans;
    }
}

到了这里,关于双指针算法专题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【算法专题】双指针

    常见的双指针有两种形式,⼀种是对撞指针,⼀种是左右指针。 对撞指针:⼀般用于顺序结构中,也称左右指针。 对撞指针从两端向中间移动。⼀个指针从最左端开始,另⼀个从最右端开始,然后逐渐往中间逼近。 对撞指针的终止条件⼀般是两个指针相遇或者错开(也可能

    2024年02月05日
    浏览(33)
  • 双指针算法专题

    双指针算法入门,干就完了 下面的题目都是来自灵神的基础算法精讲,有思路不清晰的地方,可以去看讲解。 灵茶山艾府的个人空间-灵茶山艾府个人主页-哔哩哔哩视频 (bilibili.com) 题目链接:167. 两数之和 II - 输入有序数组 - 力扣(LeetCode) 题目描述 给你一个下标从 1 开始

    2024年01月23日
    浏览(45)
  • 【算法优选】双指针专题——壹

    常⻅的双指针有两种形式,⼀种是 对撞指针 ,⼀种是 左右指针 对撞指针 :⼀般⽤于顺序结构中,也称左右指针。 对撞指针从两端向中间移动。⼀个指针从最左端开始,另⼀个从最右端开始,然后逐渐往中间逼近。 对撞指针的终⽌条件⼀般是两个指针相遇或者错开(也可能

    2024年02月08日
    浏览(46)
  • 【算法优选】双指针专题——叁

    常⻅的双指针有两种形式,⼀种是 对撞指针 ,⼀种是 左右指针 对撞指针 :⼀般⽤于顺序结构中,也称左右指针。 对撞指针从两端向中间移动。⼀个指针从最左端开始,另⼀个从最右端开始,然后逐渐往中间逼近。 对撞指针的终⽌条件⼀般是两个指针相遇或者错开(也可能

    2024年02月08日
    浏览(45)
  • 【算法专题突破】双指针 - 移动零(1)

    目录 写在前面 1. 题目解析 2. 算法原理 3. 代码编写 写在最后: 在进行了剑指Offer和LeetCode hot100的毒打之后, 我决心系统地学习一些经典算法,增强我的综合算法能力。 题目链接:283. 移动零 - 力扣(Leetcode) 读完题目大概就能明白他的意思, 就是在不改变其他数字的情况下

    2024年02月11日
    浏览(37)
  • 【算法专题突破】双指针 - 快乐数(3)

    目录 1. 题目解析 2. 算法原理 3. 代码编写 写在最后: 题目链接:202. 快乐数 - 力扣(Leetcode) 这道题的题目也很容易理解, 看一下题目给的示例就能很容易明白, 但是要注意一个点,最后有可能无限循环无法到达1。 这个时候我们就要想一下怎么判断他是无线循环呢? 实际

    2024年02月11日
    浏览(32)
  • 【优选算法】专题1 -- 双指针 -- 复写0

    前言: 补充一下前文没有写到的双指针入门知识:专题1 -- 双指针 -- 移动零 目录 基础入门知识: 1. 复写零(easy) 1. 题⽬链接:1089.复习0 - 力扣(LeetCode) 2. 题⽬描述: 3.算法原理: 异地操作 本地操作 【从后向前的复写过程】 整体思路: 🎯1.先找到最后一个“复写”的

    2024年04月17日
    浏览(32)
  • 【算法专题突破】双指针 - 复写零(2)

    目录 1. 题目解析 2. 算法原理 3. 代码编写 写在最后: 题目链接:1089. 复写零 - 力扣(Leetcode) 我先来读题, 题目的意思非常的简单,其实就是, 遇到 0 就复制一个写进数组,然后右边的元素就右移一位, 看一眼例子可以很容易理解题意。  一般像这种需要移动数组的元素

    2024年02月11日
    浏览(45)
  • 【算法专题】双指针—盛最多水的容器

      分析这个题目不难得出一个 容积公式   解法一:暴力枚举(超时) 套用上述的容积公式,使用 两个for循环 来枚举出所有可能的情况,再挑出最大值即可,但是这种写法会超时,导致不通过。时间复杂度是O(n^2) 可以自己去尝试一下。  解法二:双指针  设两个指针 left,

    2024年02月06日
    浏览(49)
  • 【算法专题突破】双指针 - 四数之和(8)

    目录 1. 题目解析 2. 算法原理 3. 代码编写 写在最后: 题目链接:18. 四数之和 - 力扣(Leetcode)  这道题跟三数之和也是一样的, 题目很好理解,就是四个数的和等于target的情况, 且这四个数不能重复。 首先还是暴力解法: 排序 + 暴力枚举 + set去重 我们当然是用优化的解法

    2024年02月09日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包