【ONE·基础算法 || 双指针】

news2024/9/24 11:31:51

在这里插入图片描述

总言

  主要内容:编程题举例,理解双指针思想。
  
  

  
  

1、双指针

  
  总言:常见的双指针有两种形式,一种是对撞指针,一种是左右指针。 (PS:在以数组形式为基础的结构中,这里的指针可以简化为数组元素下标。实际上,这里的双指针只是一个思想,在不同情景下可以用不同形式表示,并不一定要局限于指针变量本身。)
  

在这里插入图片描述对撞指针概述:

  对撞指针: 一般用于顺序结构中,也称左右指针。对撞指针从两端向中间移动。一个指针从最左端开始,另一个从最右端开始,然后逐渐往中间逼近。
  对撞指针的终止条件: 一般是两个指针相遇或者错开 (也可能在循环内部找到结果,直接跳出循环),也就是:

left == right (两个指针指向同⼀个位置)
left > right (两个指针错开)

  
  

在这里插入图片描述快慢指针概述:

  快慢指针: ⼜称为⻳兔赛跑算法,其基本思想就是使⽤两个移动速度不同的指针在数组或链表等序列结构上移动。
  常用实现方式: 在一次循环中,每次让慢的指针向后移动一位,而快的指针往后移动两位,实现一快一慢。(此外还有其它实现方式,看情况而定。)
  场景举例: 这种方法对于处理环形链表数组非常有用。此外,若要研究的问题出现循环往复的情况时,均可考虑使用快慢指针的思想。

  
  
  

2、零移动(easy)

2.1、思路分析

  题源:链接

在这里插入图片描述

  
  1)、思路说明
  此类题属于数组划分,即根据⼀种划分方式(题目条件),将数组的内容分成左右两部分,不同区间具有不同属性。这种类型的题,⼀般就是使⽤「双指针」来解决。两指针划分区间,指针移动过程中,保持各区间属性不变。

  
在这里插入图片描述

  如上图:两个指针dest、cur。

  cur指针:从左到右遍历数组,在扫描的过程中,根据遇到的情况分类处理,实现数组的划分。此题中:

[0,cur-1]:该区间内元素属于已经处理完成的部分(满足左侧非零元素右侧零元素);
[cur,n):该区间内元素等待被处理。知道cur遍历到数组末尾,所有元素处理完毕。

  dest指针:数组按条件划分的分界点。在此题中,以零元素将区间分为两段,因此dest指针用来记录非零数序列的最后⼀个位置。

[0,dest]:该区间内元素均为非零元素。
[dest+1,cur-1]:该区间内元素均为零元素。

  
  
  2)、如何判断移动?

  初始时: cur = 0 ,用于遍历数组; dest = -1 指向非零元素序列的最后⼀个位置。(满足三区间属性:非零元素区、零元素区、待处理区)
  cur从左到右遍历: 对元素排查处理,会遇到两种情况。但无论遇到哪种情况,cur++(处理非零元素、零元素的任务是由desc指针来做的)。

cur遍历遇到0,没desc的事,cur++;
cur遍历遇到非0,要将该元素划分入desc指针左侧(此部操作由desc指针和cur指针置换完成),cur++;

  desc处理分界线操作: 始终要记住这里 [0, dest] 的元素全部都是非零元素, [dest + 1, cur - 1] 的元素全是零。

当cur遇到一个非零元素,意味着desc左侧范围要新增一个位置,由于这里是进行元素交换,因此需要先让desc+1

在这里插入图片描述

  
  
  

2.2、题解

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int dest,cur;
        dest = -1; cur = 0;

        while(cur < nums.size())
        {
            if(nums[cur])//cur遍历过程中,遇到符合情况的元素,才会改动dest分界线
            {
                swap(nums[dest++ + 1], nums[cur]);//先交换数据,再让dest++。可分开成两条语句来写。
            }
            cur++;//而cur本身作为遍历的指针,无论哪种情况(0/非0)都要向后挪动排查寻找,故可统一来写。
        }
    }
};

  再次精简版:

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        for (int cur = 0, dest = -1; cur < nums.size(); cur++)
            if (nums[cur]) // 处理⾮零元素
                swap(nums[++dest], nums[cur]);
    }
};

  
  
  
  
  
  
  

3、复写零(easy)

  题源:链接。

在这里插入图片描述

  

3.1、题解

  实际原地复写是在异地复写的基础上演变而来的。若是异地复写,则直接遍历数组,用一个新的vector按照要求进行尾插即可。
  
  在原地复写中,若如果从前向后进⾏原地复写操作的话,由于 0 的出现会复写两次,导致没有复写的数被覆盖掉。因此可以选择从后往前的复写策略。但是从后向前复写时,需要找到最后⼀个复写的数。
  流程如下:
  Ⅰ、双指针从前往后遍历, 找到最后⼀个复写的数(只找数,不做修改);

如果 arr[cur] 不为0,当前元素不需要复制, dest 只增加1。
如果 arr[cur]0,当前元素需要复写, dest 增加2

  Ⅱ、判断边界情况。在遍历过程中,如果 dest 超过了数组的最大索引 n-1,则说明没有足够的空间来放置所有的0。在这种情况下,将数组的最后一个元素设置为0,并将 dest 和 cur 指针相应地向后移动。

  Ⅲ、 从后向前进⾏复写操作。
  

class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        // 1. 找到最后一次复写数
        int cur = 0, dest = -1, n = arr.size();
        while (cur < n) {
            if (arr[cur])//cur指向非零数
                dest++;
            else//cur指向零,dest复写(这里只找位置)
                dest += 2;
            if (dest >= n - 1)//判断 dest 是否已经到结束位置
                break;
            cur++;
        }
        // 2. 处理⼀下边界情况
        if (dest == n) {
            arr[n - 1] = 0;
            cur--;
            dest -= 2;
        }
        // 3. 从后向前完成复写操作
        while (cur >= 0) {
            if (arr[cur]) //cur不为零时
                arr[dest--] = arr[cur--];
            else {  //cur为零时,需要复写
                arr[dest--] = 0;
                arr[dest--] = 0;
                cur--;
            }
        }
    }
};

  
  
  

4、快乐数(medium)

4.1、思路分析

  题源:链接。

在这里插入图片描述
  
  1)、思路说明
  根据题目可知,对于该正整数,无论最后结果是否为快乐数,⼀定会进入死循环。只不过快乐数的环为1 → 1 → 1 → 1…(元素始终为1);不快乐数的环中含有各种数字。

在这里插入图片描述
  既然该变化的过程最终会形成一个圈,对于此类问题可以用快慢指针来解决。快慢指针的特性: 在⼀个圆圈中,快指针总是会追上慢指针的,也就是说他们总会相遇在⼀个位置上。 因此,若相遇位置的值是 1 ,那么这个数⼀定是快乐数;如果相遇位置不是 1 的话,那么就是不快乐数。
  
  
  2)、扩展:证明无论如何都会相遇(鸽巢原理)
在这里插入图片描述

  题目说明: 1 < = n < = 2 31 − 1 1 <= n <= 2^{31} - 1 1<=n<=2311 ,即 2,147,483,648 共10位数,要求对 n n n 的每位数上的数字进行平方和,我们选该位数下最大的数来验证。
  假设 n = 9999999999 n = 9999999999 n=9999999999,则 9 2 + 9 2 + 9 2 + … … = 9 2 × 10 = 810 9^2 +9^2+9^2+……=9^2×10 =810 92+92+92+……=92×10=810,即 n n n 范围内,新数所得计算结果 [ 1 , 810 ] [1, 810] [1,810] 之间。

  以最坏的情况来假设(变化了810次所获得结果均无重复),根据鸽巢原理,当变化超过810次后(即 ≥ 811 ≥ 811 811),所获得的结果仍旧在 [ 1 , 810 ] [1, 810] [1,810] 范围内,则必然会有一个数重复,如此构成循环,因此可以⽤快慢指针来解决。
  
  
  

4.2、题解

  如下:

#include <math.h>
class Solution {
public:
    int fun(int n) { //用于计算数的平方:注意这里数学函数使用,也可以直接计算。
        int ret = 0;
        while (n) {
            ret += pow(n % 10, 2);
            n /= 10;
        }
        return ret;
    }

    bool isHappy(int n) {
        int slow = n;
        int fast = fun(n);

        while (slow != fast) {
            slow = fun(slow);
            fast = fun(fun(fast));
        }

        return slow == 1 && fast == 1;
    }
};

  
  
  
  
  
  

5、盛水最多的容器(medium)

  题源:链接。

在这里插入图片描述
  

  

5.1、暴力求解(穷举所有情况)

  说明: 最先能想到的是应该是穷举所有情况(暴力解法),列出能构成的所有容器情况,找出其中容积最⼤的值,该法下时间复杂的是 O ( n 2 ) O(n^2) O(n2) ,在OJ题中存在超时可能。

class Solution {
public:
    int maxArea(vector<int>& height) {
        int n = height.size();
        int ret = 0;
        // 两层 for 枚举出所有可能出现的情况
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                // 计算容积,找出最⼤的那⼀个
                ret = max(ret, min(height[i], height[j]) * (j - i));
            }
        }
        return ret;
    }
};

  在此基础上优化,看看有没有其它解法。
  
  
  
  

5.2、单调性+双指针

  1)、思路分析

  说明: 设两指针 i , j ,指向的水槽板高度分别为 h[i] , h[j] ,此状态下水槽面积为 S(i,j) 。由于可容纳水的高度由两板中的 短板 决定,因此可得面积公式 : S ( i , j ) = m i n ( h [ i ] , h [ j ] ) × ( j − i ) ,即 S = 高度 × 宽度。 S(i,j)=min(h[i],h[j])×(j−i),即S=高度×宽度。 S(i,j)=min(h[i],h[j])×(ji),即S=高度×宽度。
在这里插入图片描述

  对长度: 在每个状态下,无论长板或短板向中间收窄一格,都会导致水槽 底边宽度 −1变短。
  对高度:
  若向内移动长板 ,会出现两种情况,①新板比当前短板高,此时水槽高度不变;②新板比当前短板矮,此时水槽高度减小。最终计算的水槽高度 min(h[i],h[j])不变或变小,根据计算公式,水槽的面积一定变小 。
  若向内移动短板 ,新板有可能比当前短板高,此时水槽高度 min(h[i],h[j])可能变大,因此下个水槽的面积可能增大 。
在这里插入图片描述

  
  
  2)、题解
  PS:要注意体积的计算和这里下标的关系。该写法时间复杂的是 O ( n ) O(n) O(n)

class Solution {
public:
    int maxArea(vector<int>& height) {
        int max = 0; // 用于记录每次获取到的容器最大体积
        int left = 0; //左侧板下标
        int right = height.size()-1; //右侧板下标

        while(left < right) //结束条件:两板相遇(两指针相遇)
        {
            int volume = minvol(height[left],height[right])*(right-left); //计算当前指向的容积大小
            max = max > volume ? max: volume;//判断是否容积最大
            //其它写法:max = max(max,volume);

            if(left < right && height[left] < height[right]) //向内移动短板
                left++;
            else
                right--;
        }
        return max;
    }
};

  
  
  
  
  
  
  

6、有效三角形的个数(medium)

  题源:链接。

在这里插入图片描述
  判断三条边能组成三角形的条件为: 任意两边之和大于第三边,任意两边之差小于第三边。
  
  

6.1、暴力求解

  说明: 使用三层循环枚举所有情况。时间复杂度为 O ( n 3 ) O(n^3) O(n3),可能会超时。

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        // 1. 排序
        sort(nums.begin(), nums.end());
        int n = nums.size(), ret = 0;
        // 2. 从⼩到⼤枚举所有的三元组
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                for (int k = j + 1; k < n; k++) {
                    // 当最⼩的两个边之和⼤于第三边的时候,统计答案
                    if (nums[i] + nums[j] > nums[k])
                        ret++;
                }
            }
        }
        return ret;
    }
};

  
  
  
  
  

6.2、单调性+双指针

  1)、思路说明

  ①排序,优化判断三角形成立的条件: 根据题目,对于任意a、b、c,要满足三角形意味着任意两边之和大于第三边,则代表要判断三次(a+b>c、a+c>b、b+c>a),所以可以先给数组排个序再来判断。

  ②双指针扫描,确定三边: 固定⼀个最长边,然后在比这条边小的有序数组中找出⼀个⼆元组,使这个⼆元组之和大于这个最长边。
在这里插入图片描述
  此时可能出现的情况:
   a、nums[left] + nums[right] < nums[i] 。①若此时right向左移动,则nums[left] + nums[right]的值只会更小。②只有left向右移动,nums[left] + nums[right]的值才有可能变大。③综上,该情况下,left 位置的元素可以舍去, left++ 进⼊下轮循环。

  b、nums[left] + nums[right] > nums[i] 。 ①若此时left向右移动,则nums[left] + nums[right] > nums[i]仍旧成立,意味着 [left, right - 1] 区间上的所有元素均可以与 nums[right] 构成比nums[i] 大的⼆元组,可直接计算存在的总数:b - a 。②此时 right 位置的元素的所有情况相当于全部考虑完毕, right-- ,进⼊下⼀轮判断。
  
  
  
  2)、题解

class Solution {
public:
    int triangleNumber(vector<int>& nums) {

        sort(nums.begin(), nums.end()); // 排序

        int sum = 0; // 用于记录总数
        for (int i = nums.size() - 1; i >= 2; --i) {
            // 在nums[i]左区间内用双指针遍历寻找符值
            int left = 0;
            int right = i - 1;
            while (left < right) {
                if (nums[left] + nums[right] > nums[i]) // 满足条件
                {
                    sum += right - left; // 统计出当前区间段内满足条件的总次数
                    right--; // 进⼊下轮循环
                } else {
                    left++; // 不满足条件,让left++增大,进⼊下轮循环
                }
            }
        }

        return sum;
    }
};

  
  
  
  
  
  
  
  
  

7、查找总价格为目标值的两个商品(easy)

  题源:链接。

在这里插入图片描述

  
  此题解法有多种,这里仅作部分举例。
  
  
  

7.1、暴力求解

  说明: 双层循环,定义num1、num2,判断 num1+num2 = target 即可。该解法时间复杂度为 O ( n 2 ) O(n^2) O(n2),但相比之下没有利用到给定数组有序这一条件。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int n = nums.size();
        for (int i = 0; i < n; i++) { // 第⼀层循环从前往后列举第⼀个数
            for (int j = i + 1; j < n; j++) 
            { // 第⼆层循环从 i 位置之后列举第⼆个数
                if (nums[i] + nums[j] == target) // 两个数的和等于⽬标值,说明我们已经找到结果了
                    return {nums[i], nums[j]};
            }
        }
        return {-1, -1};
    }
};

  
  
  
  

7.2、二分查找

  说明: num1 + num2 = targetnum1 = target - num2。如此,只需要固定其中一位数,再在有序数组中二分查找另一位数即可。这种做法下,优化了查找速度,时间复杂度为 O ( n ∗ l o g 2 n ) O(n*log_2n) O(nlog2n)

class Solution {
public:
    vector<int> twoSum(vector<int>& price, int target) {

        // 固定其中一个价格
        for (int i = 0; i < price.size(); ++i) {
            // 在给定值中,用二分查找一个价格
            int num = target - price[i];

            int left = 0;
            int right = price.size() - 1;
            while (left < right) {
                int mid = (left + right) / 2;
                if (price[mid] >= num) 
                    right = mid;
                else  
                    left = mid + 1;
            }
            if(price[left] == num)
                return {price[i],price[left]};
        }
        return {};
    }
};

  
  
  

7.3、单调性+双指针

在这里插入图片描述

class Solution {
public:
    vector<int> twoSum(vector<int>& price, int target) {
        // 定义双指针:这里是数组下标
        int num1 = 0;
        int num2 = price.size() - 1;
        while (num1 < num2) {
            int sum = price[num1] + price[num2];
            if (sum < target) // 说明需要将sum值增大,升序数组,只能让左指针右移
                num1++;
            else if (sum > target) // 说明需要将sum值减小,升序数组,只能让右指针左移
                num2--;
            else 
                return {price[num1] , price[num2]};
        }
        return {};//找不到的情况
    }
};

  
   扩展延伸: 上述三种方法一步步走来,我们解决的是如何找→如何快速找到 这一过程,实际上题目只要求找满足条件的一组值,扩展一下,若是要找满足条件的所有组合,又该如何做?

   由于数组有序,只需要继续向内部寻找即可。实际下述的例题就需要如此处理。
在这里插入图片描述

  
  
  
  

8、三数之和(medium)

  题源:链接。

在这里插入图片描述

  

8.1、单调性+双指针

  1)、思路分析

  说明: 此题仍旧可以使用暴力解法,先排个序,再枚举出所有情况,由于题目要求三元组不重复,因此可以考虑使用set等容器去重处理,其时间复杂度为 O ( n 3 ) O(n^3) O(n3)
  
  
  这里我们使用双指针思想进行优化。实际上本题为上一题的加强版,num1 + num2 + num3 = 0num1 + num2 = - num3,如此基本思想和上题类似:
  Ⅰ. 先排序;
  Ⅱ. 然后固定⼀个数 num3;
  Ⅲ. 在该数后的区间内,使用「双指针算法」快速找到两个数之和等于 - num3 即可。

  但需要注意两点:
  1)、如何找全所有组合? 在找到一组后,继续缩小区间范围寻找。
  2)、如何去重? 与上一题不同的是,这里需要考虑组合数重复的情况,即[-1,0,1][0,1,-1]是同一组合。
在这里插入图片描述

  
  
  2)、题解

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

        vector<vector<int>> ret;

        // 2、找三元组
        for (int i = 0; i < nums.size(); ) {

			if(nums[i] > 0) break;//一个小优化:当固定数为整数时,其右侧区间内找不到任意两数和为负数
			
            // 准备工作
            int left = i + 1; // 左指针
            int right = nums.size() - 1;// 右指针
            int target = -nums[i]; // 两数相加等于相反数

            // 找数
            while (left < right) {
                int sum = nums[left] + nums[right];
                if (sum < target) {left++;} 
                else if (sum > target) {right--; } 
                else {
                    // 当前固定数下找到一组,存放当前组合,继续找。
                    ret.push_back({nums[i], nums[left], nums[right]});
                    left++;
                    right--;
                    // (注意跳过重复数:先让left、right向内移动,再来去重,这种写法相对简便)
                    while (left < right && nums[left] == nums[left - 1]) { left++; };
                    while (left < right && nums[right] == nums[right + 1]) { right--; };
                }
            }
            // 当前固定值的情况找全,去重,寻找下一轮固定数。
            ++i;
            while (i < nums.size() && nums[i] == nums[i - 1]) ++i;
        }

        return ret;
    }
};

  
  
  
  
  
  
  
  
  

9、四数之和(medium)

  题源:链接。

在这里插入图片描述

  
  

9.1、单调性+双指针

  此题核心思想与上一题相同,只是在原先基础上多加了一层。nums[a] + nums[b] + nums[c] + nums[d] = targetnums[b] + nums[c] + nums[d] = target - nums[d],转换一下,四数就变成了原来的三数。
  
  Ⅰ、 依次固定⼀个数 a ;
  Ⅱ、 在这个数 a 的后面区间上,利用「三数之和」找到三个数,使这三个数的和等于 target - a 即可。
  这里仍旧需要去重+找全。
在这里插入图片描述

  

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {

        vector<vector<int>> ret;
        //1、排序
        sort(nums.begin(),nums.end());
        //2、找组合数
        int n = nums.size();

        for(int a = 0; a < n ; )
        {
            for(int b = a + 1; b < n; )
            {
                int c = b+1;
                int d = n-1;
                long long aim = (long long)target -nums[a] -nums[b];

                while(c < d)//双指针找数
                {
                    int sum = nums[c] +nums[d];
                    if(sum > aim) { d--;}
                    else if(sum < aim) { c++; }
                    else
                    {
                        //找到符合条件的一组
                        ret.push_back({nums[a],nums[b],nums[c],nums[d]});
                        c++; d--;
                        //去重
                        while(c < d && nums[c] == nums[c-1]) { c++; }
                        while(c < d && nums[d] == nums[d+1]) { d--; }
                    }
                }
                //当前b指向的值找完,去重后继续下一轮
                b++;
                while(b < n && nums[b] == nums[b-1]) b++;
            }
            //当前a指向的值找完,去重后继续下一轮
            a++;
            while(a < n && nums[a] == nums[a-1]) a++;
        }
        return ret;
    }
};


  
  
  
  
  
  
  

Fin、共勉。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

82407太赫兹扩频模块系列

82407太赫兹扩频模块系列 简述&#xff1a; 82407扩频模块系列产品主要为信号/频谱分析仪进行毫米波扩频测量而设计。该系列扩频模块还可用作毫米波信号接收机的前端。 82407扩频模块系列输入接口采用相应频段的标准矩形波导&#xff0c;其中82407/NB/A/B/C/D小型化产品本振输…

安装算法依赖时版本报错,依赖之间对应版本

困惑了很久&#xff0c;毕竟不是计算机专业专业出身&#xff0c;才知道安装深度学习算法各个依赖之间是有版本对应关系的。 &#xff08;本文使我随笔记录&#xff0c;无价值&#xff09; 比如&#xff1a; 再比如&#xff1a; 由于我第一步安装cuda时就和其他博主不一致&…

仓储仓库广播-仓储仓库无线应急广播对讲智能管理系统建设重点解析

仓储仓库广播-仓储仓库无线应急广播对讲智能管理系统建设重点解析 北京海特伟业科技有限公司任洪卓发表于 2024年3月5日 一、仓储仓库无线应急对讲广播系统客户需求 中央储备粮北京某某直属库&#xff0c;为国有粮食仓储企业&#xff0c;具备30多个粮食储备仓和8个成品油储备…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Scroll容器组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Scroll容器组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Scroll容器组件 可滚动的容器组件&#xff0c;当子组件的布局尺寸超过父组件…

JavaScript观察者模式:实现对象间的事件通信!

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

CDC控制仿真--综合实验,模型+代码

一、工况设置 加减速&#xff1a;从0开始加速--匀速40Km/h--减速至0&#xff1b; 转向&#xff1a;在道路65m~70m之间&#xff0c;有一个转向变道再回来的避障动作。 路面设置&#xff1a;C级路面&#xff0c;300m处&#xff0c;设置高30mm的减速带。 二、实验目标 1、对比车…

VBA技术资料MF126:移动或复制文件到新创建的文件夹

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

前端- 基础 表单标签 - 使用场景及组成

大家都有到银行去办理业务的时候&#xff0c;大多数情况下会填一些 纸质的表之类的东西如下图 而我们在网页中也会经常遇到 像现实生活中在银行填表那样的情景&#xff0c;如下图 &#xff1a; 上示就是 网页中的表单的使用场景了 表单标签 &#xff1a; 为什么需要表单 …

PMP和NPDP持证人员福利大曝光!全国这些城市都可…

近年来&#xff0c;各个城市之间为争夺人才竞争更加激烈。为了吸引具备国际职业资格认证的人才&#xff0c;各大城市纷纷制定了相关优惠和福利政策。这些政策旨在鼓励重点行业领域的用人单位采取更加优化的方式吸引海外高端人才。目前&#xff0c;已经有多个城市针对项目管理及…

【优选算法】前缀和

前缀和思想其实就是一种简单的dp思想&#xff0c;也就是动态规划 什么时候用到前缀和&#xff1f;当要快速求出数组中某一个区间的和 前缀和模板 暴力解法 定义一个指针从左向右遍历&#xff0c;并且累加值即可&#xff0c;这里就不过多赘述&#xff0c;主要还是来看前缀和…

MyBatis3源码深度解析(三)Connnection

文章目录 前言2.3 Connnection2.3.1 JDBC驱动程序的类型2.3.1.1 JDBC-ODBC Bridge Driver2.3.1.2 Native API Driver2.3.1.3 HDBC-Net Driver2.3.1.4 Native Protocol Driver 2.3.2 java.sql.Driver2.3.2.1 静态代码块加载驱动类2.3.2.2 SPI机制加载驱动类 2.3.3 DriverManager…

06 - ip route和route -n的区别

1 ip route和route -n的区别 ip route 和 route -n 都是用于查看和管理Linux系统路由表的命令。但下面是它们的区别&#xff1a; ip route&#xff1a;是Linux系统中的现代工具&#xff0c;它属于iproute2套件&#xff1b;它提供了更多的选项&#xff0c;可以更精确地控制路由表…

详细分析Linux内存知识并释放内存

目录 前言1. 基本知识1.1 free1.2 cat /proc/meminfo1.3 slabtop 2. 清空内存 前言 本篇文章主要分析内存 如果是磁盘空间&#xff0c;推荐阅读&#xff1a;服务器出现根目录磁盘满了解决方法 1. 基本知识 在Linux系统中&#xff0c;查看内存的基本知识包括以下几个方面&…

解决手机连接校园网同一设备老是需要重复认证的问题(+解决原理)

相信大家平时在使用校园网的时候总会遇到同一设备隔三岔五就要重复认证绑定的问题&#xff0c;这里直接附上解决方案。 打开手机的wifi-->连接校园网然后进入设置-->在隐私选项选择“使用设备MAC” 如下图&#xff0c;问题解决了&#xff01;如果想知道原理的可以继续往…

RN开发搬砖经验之-Android平台下处理后退按钮事件

基本接口 利用RN 针对Android平台提供的接口 BackHandler BackHandler需要区分类组件跟函数组件的场景&#xff0c;主要是两个组件一个基于组件生命周期的&#xff0c;一个是基于hook的&#xff0c;即注册BackHandler的事件监听与移除时机写法不同。 类组件 示例代码 impor…

24/03/05总结

easyx: #include "iostream" #include "easyx.h" #include "cstdio" using namespace std; int main() {initgraph(800, 600);setorigin(400, 300);setaspectratio(1, -1);//绘制多边形:polygon(const POINT *points,int num);//points 是一个P…

Python从0到100(二):Python语言介绍及第一个Pyhon程序

前言&#xff1a; 零基础学Python&#xff1a;Python从0到100最新最全教程。 想做这件事情很久了&#xff0c;这次我更新了自己所写过的所有博客&#xff0c;汇集成了Python从0到100&#xff0c;共一百节课&#xff0c;帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…

输出X^N对233333取模的结果。

对任意正整数N&#xff0c;求XN%233333的值。 要求运算的时间复杂度为O(logN)。 例如X30 X15*X15X15X7*X7*XX7X3*X3*XX3X*X*X共7次乘法运算完毕。输入输出格式 输入描述: 输入两个整数X和N&#xff0c;用空格隔开&#xff0c;其中X,N<10^9。 输出描述: 输出X^N对233333取模…

【排序】详解选择排序

一、思想 选择排序的原理与思想非常直观和简单&#xff0c;它通过不断地选择未排序部分的最小&#xff08;或最大&#xff09;元素&#xff0c;并将其放到已排序部分的末尾来实现排序。 具体来说&#xff0c;选择排序的过程可以分解为以下几个步骤&#xff1a; 寻找最小&…

Android m/mm/mmm/make编译模块

一.编译成模块的前置条件 Android编译环境初始化完成后&#xff0c;我们就可以用m/mm/mmm/make命令编译源代码了。lunch命令其实是定义在build/envsetup.sh文件中的函数lunch提供的。与lunch命令一样&#xff0c;m、mm和mmm命令也分别是由定义在build/envsetup.sh文件中的函数…