双指针训练

1.原理

双指针是一种解题常用方法,常用于将数组按照某种要求进行分块/划分,这里的指针对于数组来说,可以替换成下标(毕竟使用下标实际上就是用了指针)。

1.1.划分区间

通常将这两个指针命名位dest/cur(或者begin/end),利用这两个指针将区间划分为三个不同性质的区间[0, dest-1][dest, cur-1][cur, n-1],在cur移动的时候,保持三个区间的性质不变。

实际上这也是快速排序中的前后指针、三路划分中使用的原理。仔细回想一下,快速排序的算法逻辑中,实际上也是将下标[left, right]之间的数据划分为[left, key-1][key, key][key+1, right],并且让这三个区间保持某种性质不变(在快排中是第一个区间比key小/大,最后一个区间比key大/小)。

具体可以看一下这一道:[2.1.[283. 移动零]](##2.1.[283. 移动零])。

1.2.覆盖重写

利用双指针可以达到覆盖数组的目的,这里可以看看:[2.2.[1089. 复写零]](##2.2.[1089. 复写零])。

1.3.快慢指针

经典的应用就是判断链表是否带环,可以看看:[2.3.[202. 快乐数]](##2.3.[202. 快乐数])。

1.4.单调性

有些时候将序列排序后使用双指针会有很高效的解法,可以看看:[2.5.[611. 有效三角形的个数]](##2.5.[611. 有效三角形的个数])、[2.6.[LCR 179. 查找总价格为目标值的两个商品]](##2.6.[LCR 179. 查找总价格为目标值的两个商品])

2.题目

2.1.283. 移动零

这道题目的解法有很多,这里我写一个常见的解法:使用cur扫描/遍历数组,使用dest来作为分界线,在划分成的[0,dest-1][dest, cur-1][cur, n-1]中,三个区间的性质是:[0,dest-1]是已经处理过确认非0的区间,[dest, cur-1]是确认为0的区间,[cur, n-1]是待处理的部分,然后接下来一直移动cur直至[cur, n - 1]为空,区间一直保持以上的性质。

class Solution
{
public:
    void moveZeroes(vector<int>& nums)
    {
        size_t dest = 0;   
        size_t cur = 0;     
        while(cur < nums.size())
        {
            if(nums[cur] != 0)
            {
                std::swap(nums[cur], nums[dest]);
                dest++;
            }
            cur++;
        }
    }
};

时间复杂度为 O ( n ) O(n) O(n),因为只需要遍历一次即可解决问题
空间复杂度为 O ( 1 ) O(1) O(1),因为不计输入数据,只用到了两个有限的变量

2.2.1089. 复写零

首先最容易想到的就是使用另外一个vector<int> ret顺序表来存储复写后的数组,并且拷贝回vector<int> arr即可。

class Solution
{
public:
    void duplicateZeros(vector<int>& arr)
    {
        std::vector<int> ret;
        for(auto it : arr)
        {
            ret.push_back(it);
            if(it == 0)
            {
                ret.push_back(0);
            }
        }
        int i = 0;
        for(auto& it : arr)
        {
            it = ret[i]; 
            i++;
        }
    }
};

虽然可以过,但是忽略了“就地修改”这个规则,这又该怎么办呢?

我们可以定义一个size指向“复写操作后数组”的最后一个元素,cur指向“复写操作前数组”的最后一个元素,也就是说:对于arr=[1,0,2,3,0,4,5,0]经过复写操作后为ret=[1,0,0,2,3,0,0,4]。因此cur下标指向arr[5]=4的位置,假设size下标指向arr[7]=0的位置,然后从右往左判断覆盖(非0arr[7]=arr[5]0则复写两个0),重复操作即可。

但是怎么找到“复写操作后数组”的最后一个元素的下标呢?依旧可以使用双指针寻找,你么看错,就是双指针里面套双指针。

  1. 我们假设size=0, cur=0然后让cur遍历整个数组,只要遇到0就让size+=2, cur++,如果是遇到非0就让size++, cur++
  2. 也就是说size实时记录了“复写操作后数组”的大小
  3. 最后遍历完整个数组,此时的cur一定指向复写操作后数组的最后一个元素的后面
  4. 然后执行之前说的“从右向左覆盖”操作即可
class Solution
{
public:
    void duplicateZeros(vector<int>& arr)
    {
        //1.寻找复写操作后,数组的最后一个元素
        int cur = 0;//寻找“复写操作后数组”的最后元素的下标
        int size = 0;//记录“复写操作后数组”的大小

        //[例子1]
        //复写前:[1,5,2,0,6,8,0,6,0]
        //复写后:[1,5,2,0,0,6,8,0,0]
        //[例子2]
        //复写前:[5,2,1,3,0]
        //复写后:[5,2,1,3,0]0 会溢出一个0
        while (size < (int)arr.size())
        {
            if (arr[cur] == 0)
            {
                size += 2;
            }
            else
            {
                size += 1;
            }
            cur++;
        }
        cur--;
        size--;

        //2.从右往左做开始复写
        //[例子1]
        //复写前:[1,5,2,0,6,8,0,6,0],cur->arr[5]=4, size->arr[7]=0
        //复写后:[1,5,2,0,0,6,8,0,0]
        //[例子2]
        //复写前:[5,2,1,3,0],cur->arr[4]=0, size->arr[5] 越界了 
        //复写后:[5,2,1,3,0]0 会溢出一个0

        if (arr[cur] == 0 && size >= arr.size())
        //当出现最后一个元素为0的时候很特殊,有两种可能:
        //1.复写操作后溢出了一个0,这种情况只复写一次即可
        //2.复写操作后刚好最后两个元素为0,这种情况正常复写即可
        {
            size--;
            arr[size] = arr[cur];
            size--;
            cur--;
        }
        while (cur != -1)
        {
            if (arr[cur] != 0)
            {
                arr[size--] = arr[cur--];
            }
            else
            {
                arr[size--] = arr[cur];
                arr[size--] = arr[cur];
                cur--;
            }
        }
    }
};

时间复杂度为 O ( n ) O(n) O(n),两次循环就是 O ( n + n ) = O ( 2 n ) O(n+n)=O(2n) O(n+n)=O(2n),也就是 O ( n ) O(n) O(n)
空间复杂度为 O ( 1 ) O(1) O(1),只用到了常数个有限的变量

2.3.202. 快乐数

实际上这是一道双链表判断是否回环的问题,可以使用快慢指针(双指针的一种使用方法)来判断链表是否存在回环点。本题最麻烦的问题就是如何知道是否会出现无限循环的情况。

image-20230929230111870
using namespace std;
class Solution
{
private:
    int _function(int number)//计算平方值
    {
        int add = 0;
        while (number)
        {
            add += (number % 10) * (number % 10);
            number /= 10;
        }
        return add;
    }
public:
    bool isHappy(int number)
    {
        //循环判断是否happy
        int p = number;//走一步的指针(慢指针)
        int curp = number;//走两步的指针(快指针)
        while (true)
        {
            p = _function(p);//得4,走一步...
            curp = _function(_function(curp));//得16,走两步...
            if (p == curp)//如果找到循环点,也就是快慢指针相遇,就进入if语句
            {
                if (p == 1)//如果相遇点处的值为1就返回true
                {
                    return true;
                }
                return false;//如果不是1就返回false
            }
        }
    }
};

但是这道题目实际上是出简单的了,题目已经提前告诉我们取平方后只有两种情况:

  1. 最后得到1(实际上也是循环)
  2. 最后是无限循环,一直在一个回环里循环

但是实际上,从简单的思考上,是否存在:无限计算下去得不到1,但是不存在环的可能?我们可以使用”鸽笼原理“:在n个鸽笼中放入n+1只鸽子,则至少有一个笼子,里面的鸽子数量大于1

我们设int的范围中最大的正数为(0111 1111 1111 1111 1111 1111 1111 1111)2=(21 4748 3647)10≈(2.1*10^9)2

假设有一个比int更大的数(99 9999 9999)10,则经过平方后得到9^2*10=810。因此[1, 99 9999 9999]范围内的数,经过平方后得到的数一定在[0, 810]的范围内,因此我们就有了810个鸽笼,假设最坏情况下一个属于[1, 99 9999 9999]数经过平方后得到1填充了一个笼,再次平方得到2填充第二个笼…一直到810个笼子全部填完(这里只是假设最坏情况,实际不太可能达到),则第811次平方后又得到了一个[0, 810]内的数,因此造成了循环。

那对于int范围内的数就更加会出现循环的情况了,毕竟int范围内的数平方得到的新数一定在[0, 810]的范围内,如果不循环,那么每一个数才放进小于810的笼子里根本不够用!

2.4.11. 盛最多水的容器

本题难度相较于前面会上升一些,首先最容易的解法就是:暴力穷举,直接上两个循环,枚举出所有的情况,找到最大的容积,但是使用这种方法一定会超时。

但是本题可以在遍历的时候作出节省,这是一种重要的思路:有的时候不得不使用枚举时,可以考虑是否剔除一些情况达到提高枚举效率的目的。

  1. 首先使用两个指针beginend指向数组heights[]的首尾处
  2. 计算高和宽height = min{heights[begin], heights[end]}width = end - begin,然后尾插到volumes
  3. 然后从heights[begin]heights[end]选择中最小的那一个数的下标begin++或者end--
  4. 只要满足begin < end就一直循环1~3即可
  5. 最后找到volumes中最大的体积返回即可
class Solution 
{
public:
    int maxArea(vector<int>& heights) 
    {
        int begin = 0, end = (int)heights.size() - 1;

        vector<int> volumes;
        
        int width = 0;
        int height = 0;
        while (begin < end)
        {
            //1.取得“最大的宽”
            width = end - begin;

            //2.取得“最小的高”
            height = heights[begin];
            if (height > heights[end])
            {
                height = heights[end--];
            }
            else
            {
                begin++;
            }
            //3.计算区间内最大面积
            volumes.push_back(width * height);
        }
        return *std::max_element(volumes.begin(), volumes.end());;
    }
};

时间复杂度是 O ( n ) O(n) O(n),由于是两个指针在遍历数组,因此就相当于遍历整个数组,因此数据复杂度就是 O ( n ) O(n) O(n)

空间复杂度是 O ( 1 ) O(1) O(1),这点没什么好解释的

2.5.611. 有效三角形的个数

一般而言,判断三个数abc能否构成三角形的依据是:
{ a + b > c a + c > b b + c > a \begin{cases} a+b>c\\ a+c>b\\ b+c>a \end{cases} a+b>ca+c>bb+c>a
但是,如果将abc三个数有序为a <= b <= c,就只需要判断一次a + b > c即可,因此就可以先把整个数组排序好后再来判断是否构成三角形。

因此暴力一点的话可以像下面这么写:

class Solution
{
    bool check(vector<int>& nums, int i, int j, int k)
    {
        return (nums[i] + nums[j] > nums[k]);
    }
public:
    int triangleNumber(vector<int>& nums)
    {
        sort(nums.begin(), nums.end());

        int count = 0;
        for (int i = 0; i < nums.size(); i++)
        {
            for (int j = i + 1; j < nums.size(); j++)
            {
                for (int k = j + 1; k < nums.size(); k++)
                {
                    if (check(nums, i, j, k))
                    {
                        count++;
                    }
                }
            }
        }
        return count;
    }
};

但是这只能过大部分的例子,还有一些情况没有办法过(数据量太大),因此还需要做优化。

首先数组已经是有序(具有单调性)的了,只需要判断a + b > c即可。

则从最后一个元素 x n x_{n} xn开始证明有 x 0 + x n − 1 > x n x_{0}+x_{n-1}>x_{n} x0+xn1>xn即可直接认为 x n − 2 x_{n-2} xn2后的数都可以和 x n − 1 x_{n-1} xn1以及 x n x_{n} xn构成三角形,无需判断…

class Solution
{
public:
    int triangleNumber(vector<int>& nums)
    {
        //1.排序数组
        sort(nums.begin(), nums.end());

        //2.通过三角形的性质结合单调性判断有多少种符合的情况
        int count = 0;//存储有多少种组合
        int key = nums.size() - 1, begin = 0, end = key - 1;//分为区间[begin, end]key
        while (key >= 2)
        {
            while (begin < end)
            {
                if (nums[begin] + nums[end] > nums[key])
                    //如果nums[begin] + nums[end] > key,
                    //那么nums[end]加上“nums[begin]...nums[end-1]”的任意一个数都大于key
                    //然后变成[begin, end--]key,也就是说:明确了和nums[end]结合有end - begin种情况
                {
                    count += (end - begin);
                    end--;
                }
                else//如果不是这样,那么就变成[begin++, end]key
                {
                    begin++;
                }
            }
            begin = 0;//最后重置一下左区间索引
            end = --key - 1;//最后重置一下右区间索引(并且修改key)
        }
        return count;
    }
};

时间复杂度: O ( n 2 ) O(n^{2}) O(n2)

2.6.LCR 179. 两数之和

这道题目也是利用单调性达到节省枚举次数的目的。

  1. 首先选定最后一个数price[end],和数组其他的数price[begin]相加
  2. 如果发现相加值等于目标值,则返回price[begin]price[end]
  3. 如果发现相加值小于目标值,则begin++
  4. 如果发现相加值大于于目标值,则说明不用继续移动begin了。因为此时的price[begin] + price[end] > target,那么根据单调性,price[begin~end-1] + price[end]都大于target。此时begin重置为0,然后end--,重复1~4步骤即可
class Solution
{
public:
    vector<int> twoSum(vector<int>& price, int target)
    {
        //1.创建要返回的顺序表
        vector<int> ret;

        //2.创建双指针begin和end
        int end = price.size() - 1;
        int begin = 0;

        //3.从最后一个指针开始循环,每次循环向前一步
        while (end != 0)
        {
            while (begin < end)
            {
                if (price[begin] + price[end] == target)//如果相等就找到了
                {
                    ret.push_back(price[begin]);
                    ret.push_back(price[end]);
                    return ret;
                }
                else if (price[begin] + price[end] < target)//如果小于就找比price[begin]更大的数
                {
                    begin++;
                }
                else//price[begin] + price[end] > target,这个时候已经大于目标值了,没有必要再循环让begin++了,直接跳出即可
                {
                    break;
                }
            }
            begin = 0;//重置begin
            end--;
        }
        return ret;
    }
};

或者简化为以下代码(上述代码还有些冗余):

class Solution
{
public:
    vector<int> twoSum(vector<int>& price, int target)
    {
        //1.创建要返回的顺序表
        vector<int> ret;

        //2.创建双指针begin和end
        int end = price.size() - 1;
        int begin = 0;

        //3.从最后一个指针开始循环,每次循环向前一步
        while (begin < end)
        {
            if (price[begin] + price[end] > target)//如果相等就找到了
            {
                end--;
            }
            else if (price[begin] + price[end] < target)//如果小于就找比price[begin]更大的数
            {
                begin++;
            }
            else
            {
                ret.push_back(price[begin]);
                ret.push_back(price[end]);
                break;
            }
        }

        return ret;
    }
};

时间复杂度:O(n)

2.7.LCR 007. 三数之和

class Solution
{
public:
    vector<vector<int>> threeSum(vector<int> nums)
    {
        //1.设置要返回的顺序表
        vector<vector<int>> ret;
        //2.排序原数据数组
        sort(nums.begin(), nums.end());
        //3.查找复合条件的三元组
        int n = nums.size();
        for (int i = 0; i < n; )
        {
            if (nums[i] > 0) break;

            int left = i + 1, right = n - 1, target = -nums[i];
            while (left < right)
            {
                int sum = nums[left] + nums[right];
                if (sum > target)
                {
                    right--;
                }
                else if (sum < target)
                {
                    left++;
                }
                else
                {
                    ret.push_back({ nums[i], nums[left], nums[right] });
                    left++, right--;

                    //去重
                    while (left < right && nums[left] == nums[left - 1]) left++;
                    while (left < right && nums[right] == nums[right + 1]) right--;
                }
            }
            i++;
            while (i < n && nums[i] == nums[i - 1]) i++;
        }
        return ret;
    }
};

这题比较难,以后再做然后补充。

时间复杂度: O ( n 2 ) O(n^{2}) O(n2)

2.8.18. 四数之和

class Solution
{
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target)
    {
        vector<vector<int>> ret;
        sort(nums.begin(), nums.end());

        int n = nums.size();
        for (int i = 0; i < n; )
        {
            for (int j = i + 1; j < n; )
            {
                int left = j + 1, right = n - 1;
                long long aim = (long long)target - nums[i] - nums[j];//防止溢出
                while (left < right)
                {
                    int sum = nums[left] + nums[right];
                    
                    if (sum < aim)
                    {
                        left++;
                    }
                    else if (sum > aim)
                    {
                        right--;
                    }
                    else
                    {
                        ret.push_back({ nums[i], nums[j], nums[left++], nums[right--] });
                        //去重
                        while (left < right && nums[left] == nums[left - 1]) left++;
                        while (left < right && nums[right] == nums[right + 1]) right--;
                    }
                }
                //去重
                j++;
                while (j < n && nums[j] == nums[j - 1]) j++;
            }
            //去重
            i++;
            while (i < n && nums[i] == nums[i - 1]) i++;
        }
        return ret;
    }
};

时间复杂度: O ( n ) = n 3 O(n) = n^{3} O(n)=n3

空间复杂度:主要消耗在排序那里

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

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

相关文章

AOP切入点表达式和使用连接点获取匹配到的方法信息

目录 第一种 execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?) 第二种 annotation(com.itheima.anno.Log 首先&#xff0c;自定义一个注解&#xff0c;可以自己随意命名&#xff1a; 第一种 execution(访问修饰符? 返回值 包名.类名.?方法名…

网络编程-认识套接字socket

文章目录 套接字概念端口号网络字节序 套接字类型流套接字数据报套接字 socket常见APIsocket函数bind函数listen函数accept函数connect函数sockaddr结构 套接字概念 socket套接字是进程之间一种通信机制&#xff0c;通过套接字可以在不同进程之间进行数据交流。在TCP/UDP中&…

【golang/g3n】3D游戏引擎G3N的windows安装与测试

目录 说在前面安装测试 说在前面 操作系统&#xff1a;win 11go version&#xff1a;go1.21.5 windows/amd64g3n版本&#xff1a;github.com/g3n/engine v0.2.0其他&#xff1a;找了下golang 3d相关的库&#xff0c;目前好像就这个比较活跃 安装 按照官方教程所说&#xff0c;…

Electron框架:构建跨平台桌面应用的终极解决方案

文章目录 一、Electron框架简介二、Electron框架的优势1. 开发效率高2. 跨平台性能好3. 易于维护4. 强大的原生能力 三、如何使用Electron框架快速开发跨平台桌面应用1. 安装Electron2. 创建项目文件夹3. 编写主进程代码4. 编写界面代码5. 运行应用 《Electron入门与实战》编辑…

nginx_rtmp_module 之 ngx_rtmp_mp4_module 的mp4源码分析

一&#xff1a;整体代码函数预览 static ngx_int_t ngx_rtmp_mp4_postconfiguration(ngx_conf_t *cf) {ngx_rtmp_play_main_conf_t *pmcf;ngx_rtmp_play_fmt_t **pfmt, *fmt;pmcf ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_play_module);pfmt ngx_ar…

极兔速递物流查询,用表格导出单号的每一条物流信息

批量查询极兔速递单号的物流信息&#xff0c;并以表格的形式导出单号的每一条物流信息。 所需工具&#xff1a; 一个【快递批量查询高手】软件 极兔速递单号若干 操作步骤&#xff1a; 步骤1&#xff1a;运行【快递批量查询高手】软件&#xff0c;第一次使用的朋友记得先注册…

HPV治疗期间如何预防重复感染?谭巍主任讲述具体方法

众所周知&#xff0c;人乳头瘤病毒(HPV)是一种常见的性传播疾病&#xff0c;感染后可能会引起生殖器疣、宫颈癌等疾病。在治疗期间&#xff0c;预防重复感染非常重要。今日将介绍一些预防HPV重复感染的方法。 1. 杜绝不洁性行为 在治疗期间&#xff0c;患者应该避免与感染HPV…

CT成像技术—20231210

本文要说的是扇束重排&#xff0c;对于扇束及锥束直接重建公式&#xff0c;可以看我做的PDF https://github.com/leslielee619/CTRec/blob/main/重建公式.pdf 在说重排之前&#xff0c;我还想对那个文件内容补充两点&#xff1a; 1、FDK算法或Feldkamp算法&#xff0c;出自Fel…

linux下开放端口的方法

为了辅助我们查看端口状态&#xff0c;本文采用nmap扫描端口 目标机&#xff1a;192.168.241.1&#xff0c;本文的目的是开启22端口 我们可以根据端口状态&#xff08;filtered&#xff09;看出&#xff0c;端口处于过滤状态&#xff0c;即防火墙过滤了该端口 PS&#xff1a;…

PCB设计规则中的经验公式_笔记

PCB设计规则中的经验公式 规则1 - 临界长度规则2 - 信号带宽与上升时间规则3- 时钟信号带宽规则4-信号传输速度规则5- 集肤 (效应) 深度规则6 - 50Ω传输线电容规则7 - 50Ω传输线电感规则8 - 回流路径电感规则9 - 地弹噪声规则10- 串行传输比特率与信号带宽规则11- PCB走线直流…

JAVA序列化(创建可复用的 Java 对象)

JAVA 序列化(创建可复用的 Java 对象) 保存(持久化)对象及其状态到内存或者磁盘 Java 平台允许我们在内存中创建可复用的 Java 对象&#xff0c;但一般情况下&#xff0c;只有当 JVM 处于运行时&#xff0c;这些对象才可能存在&#xff0c;即&#xff0c;这些对象的生命周期不…

Linux---文本搜索命令

1. grep命令的使用 命令说明grep文本搜索 grep命令效果图: 2. grep命令选项的使用 命令选项说明-i忽略大小写-n显示匹配行号-v显示不包含匹配文本的所有行 -i命令选项效果图: -n命令选项效果图: -v命令选项效果图: 3. grep命令结合正则表达式的使用 正则表达式说明^以指…

STM32_HAL库—IWDG看门狗

一、CubeMX设置 1、晶振配置&#xff08;72M&#xff09; 2、数据配置 超时时间 Tout prv / LSI * rlv (s) 其中prv是预分频器寄存器的值&#xff0c;rlv是重装载寄存器的值&#xff0c;而LSI值默认是40kHz&#xff0c;如下所示。 3、代码实现 int main(){while(1){HAL_IW…

gitlab ci pages

参考文章 gitlab pages是什么 一个可以利用gitlab的域名和项目部署自己静态网站的机制 开启 到gitlab的如下页面 通过gitlab.ci部署项目的静态网站 # build ruby 1/3: # stage: build # script: # - echo "ruby1"# build ruby 2/3: # stage: build …

docker入门小结

docker是什么&#xff1f;它有什么优势&#xff1f; 快速获取开箱即用的程序 docker使得所有的应用传输就像我们日常通过聊天工具文件传输一样&#xff0c;发送方将程序传输到超级码头而接收方也只需通过超级码头进行获取即可&#xff0c;就像一只鲸鱼拖着货物来回运输一样。…

pl_vio线特征·part II

pl_vio线特征part II 0.引言4.线段残差对位姿的导数4.1.直线的观测模型和误差4.2.误差雅克比推导4.3.误差雅可比求导简洁版(不含imu坐标系转换)4.4.相关代码 0.引言 pl_vio线特征part I 现在CSDN有字数限制了&#xff0c;被迫拆分为两篇文章。 4.线段残差对位姿的导数 这一小…

盛最多水的容器

给定一个长度为 n 的整数列表 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。 说明&#xff1a;你不能倾斜容器。 示例1&…

ARM I2C通信

1.概念 I2C总线是PHLIPS公司在八十年代初推出的一种串行的半双工同步总线&#xff0c;主要用于连接整体电路2.IIC总线硬件连接 1.IIC总线支持多主机多从机&#xff0c;但是在实际开发过程中&#xff0c;大多数采用单主机多从机模式 2.挂接到IIC总线上&#xff0c;每个从机设备都…

一键批量改名,将西班牙语文件轻松转换为中文!

你是否曾经遇到过需要将大量西班牙语文件批量转换为中文文件的问题&#xff1f;这可能会让你感到头疼和繁琐。但是&#xff0c;现在有了我们的批量改名工具&#xff0c;你可以轻松解决这个问题&#xff01; 首先&#xff0c;进入文件批量改名高手的主页面&#xff0c;并在板块…

dockerfile,Docker镜像的创建

dockerfile&#xff1a;创建镜像&#xff0c;创建自定义的镜像。包括配置文件&#xff0c;挂载点&#xff0c;对外暴露的端口。设置环境变量。 docker的创建镜像的方式&#xff1a; 1、基于已有镜像进行创建。根据官方提供的镜像源&#xff0c;创建镜像&#xff0c;然后拉起容…