算法沉淀——双指针算法(leetcode真题剖析)

在这里插入图片描述

算法沉淀——双指针算法

    • 01.移动零
    • 02.复写零
    • 03.快乐数
    • 04.盛最多水的容器
    • 05.有效三角形的个数
    • 06.和为s的两个数字
    • 07.三数之和
    • 08.四数之和

双指针算法(Two Pointer Algorithm)是一种常用于数组(或链表)操作的算法技巧。它的核心思想是通过维护两个指针,在数组中高效地解决一些问题,这里的指针不一定是真实的指针,是一种抽象的概念,比如数组的下标,C++的迭代器等等。这两个指针可以分别指向数组的不同位置,也可以分别指向数组的开始和结束。

常见的双指针算法有两种类型:快慢指针和左右指针。

  1. 快慢指针:
    • 用于解决一些查找或判断问题,比如判断链表是否有环、找到链表的中间节点等。
    • 快指针每次移动两步,慢指针每次移动一步。
    • 当两个指针相遇时,说明存在环,或者慢指针所指位置即为中间节点。
  2. 左右指针(又称对撞指针):
    • 用于解决一些区间或子数组的问题,比如在有序数组中找到两个数使其和等于目标值、判断一个字符串是否为回文等。
    • 左指针和右指针分别指向数组的两端,根据问题的要求,移动其中一个或两个指针。
    • 根据移动规则,可以有效地缩小问题的搜索范围,提高算法的效率。

下面我们通过实际算法题来进行讲解

01.移动零

题目链接:https://leetcode.cn/problems/move-zeroes/description/

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

示例 1:

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

示例 2:

输入: nums = [0]
输出: [0]

思路

核心思路是使用两个指针,一个指针(cur)用于遍历数组,另一个指针(dest)用于指示当前非零元素的插入位置。通过遍历数组,当发现非零元素时,将其与dest位置的元素进行交换,同时递增dest指针,确保非零元素的相对顺序保持不变。

下面是算法的详细流程:

  1. 初始化两个指针:curdest,初始值为0和-1,因为cur指针先走。
  2. 从左到右遍历数组,用cur指针指向当前元素。
  3. 如果当前元素不为零,将其与dest位置的元素进行交换,并递增dest指针。
  4. 继续遍历,重复步骤3,直到数组遍历完毕。
  5. 此时,所有非零元素已经移动到数组的前面,而dest指针的位置之后都是零。因为dest初始值为-1,所以dest + 1即为第一个零的位置。
  6. 遍历完成后,数组中所有零已经被移动到数组的末尾。

这个算法的优点在于它只需要遍历一次数组,并且在原地进行操作,不需要额外的空间。这使得它在处理大规模数组时非常高效,时间复杂度只有O(n)。

以下是对应的C++代码:

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]);
        }
    }
};

02.复写零

题目链接:https://leetcode.cn/problems/duplicate-zeros/

给你一个长度固定的整数数组 arr ,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。

注意:请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改,不要从函数返回任何东西。

示例 1:

输入:arr = [1,0,2,3,0,4,5,0]
输出:[1,0,0,2,3,0,0,4]
解释:调用函数后,输入的数组将被修改为:[1,0,0,2,3,0,0,4]

示例 2:

输入:arr = [1,2,3]
输出:[1,2,3]
解释:调用函数后,输入的数组将被修改为:[1,2,3]

思路

这里我们能够想到最简单的一种算法就是,我们再创建一个容器数组逐个遍历,进行零的重写,最后直接覆盖或交换原数组,但是这种写法花费的时间和特别是空间代价太大了,所以我们这种时候利用双指针的算法可以很好的解决这个问题。

算法的核心思想是通过两个步骤实现对数组中的零的复制:

  1. 统计零的个数: 通过遍历数组,统计其中零的个数,并在遍历过程中确定是否需要额外的位置来容纳复制的零。
  2. 从后向前复制零: 从数组的末尾开始向前复制元素,遇到非零元素直接复制,遇到零则复制两个零。这样的方式确保了零的复制不会影响到原数组中的元素。

下面是算法的详细步骤:

  1. 初始化变量 count 表示数组中零的个数,变量 i 表示当前遍历的位置。
  2. 第一次遍历数组,统计零的个数,并在遍历过程中判断是否需要额外的位置。同时记录遍历结束的位置 i
  3. 初始化变量 r 表示从后向前复制元素的位置,如果复制零后的数组长度超过了原数组长度,补充一个零到最后。
  4. 从位置 i 开始,从后向前遍历数组,根据数组中的元素值进行复制操作,确保零被复制两次,非零元素直接复制。
  5. 完成从后向前的复制后,原数组 arr 中的零已经被复制一份,实现了题目的要求。

以下是对应的C++代码:

class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        int n=arr.size(),count=0,i=0;
        for(i=0;i<n;i++){
            if(arr[i]==0) count++;
            if(i+count+1>=n) break;//题目规定不能超过数组固定长度
        }
        
        int r=n-1;
        if(i+count+1>n){ //处理边界情况,算出复写后位置大于n就说明最后那个位置一定是0
            arr[r--]=0;
            i--;
        } 
        
        //从后向前通过i和r的下标相对位置完成复写0 
        while(i>=0){
            if(arr[i]) arr[r--]=arr[i--];
            else{
                arr[r--]=0;
                arr[r--]=0;
                i--;
            }
        }
    }
};

03.快乐数

题目链接:https://leetcode.cn/problems/happy-number/description/

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果这个过程 结果为 1,那么这个数就是快乐数。

如果 n快乐数 就返回 true ;不是,则返回 false

示例 1:

输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

示例 2:

输入:n = 2
输出:false

思路

这道题其实我们可以根据一个数学知识知道如果经过不断计算无线循环的话,其中是一定会有重复的数字的,其实这里最好的解法应该是使用哈希思想,但是在这里我们使用双指针也可以有很简单的写法,我们先来看看双指针的写法

解法一:快慢指针

  1. 定义一个函数 nextSum,该函数用于计算一个数每位数字的平方和。通过对输入数字每位取模得到个位数,然后累加每位数字的平方。
  2. 使用两个指针 slowfast,初始时都指向输入的数 n。在循环中,slow每次计算一次 nextSum,而 fast每次计算两次 nextSum,这就相当于在一个链表中 slow 每次走一步,fast 每次走两步。
  3. 如果存在一个循环,那么 slowfast 最终都会进入这个循环,并相遇。如果 slow 最终等于1,则这个数是快乐数;如果 slowfast 相遇但不等于1,说明存在循环,这个数不是快乐数。

以下是对应的C++代码:

class Solution {
public:
    int nextSum(int n){
        int sum=0;
        while(n){
            int tmp=n%10;
            sum+=tmp*tmp;
            n/=10;
        }
        return sum;
    }
    bool isHappy(int n) {
        int slow=n,fast=nextSum(n);
        while(slow!=fast){
            slow=nextSum(slow);
            fast=nextSum(nextSum(fast));
        }
        return slow==1;
    }
};

解法二:哈希

这个算法也是通过计算每位数字的平方和,然后不断迭代,检测是否能够最终得到1。不同之处在于,这个算法使用了一个哈希集合 unordered_set 来记录已经出现过的数字,以便检测循环。

以下是对这个算法的核心思想和流程的详细解释:

  1. 初始化: 创建一个空的哈希集合 seen,用于记录已经出现过的数字。
  2. 循环检测: 在一个循环中,检测当前的数字是否等于1,并且是否已经在 seen 集合中出现过。如果是,则说明这个数是快乐数,直接返回 true
  3. 迭代计算: 如果当前数字不等于1且未出现在 seen 中,那么将当前数字加入到 seen 中,并计算下一个数字的平方和。这个过程通过迭代计算每位数字的平方和,然后将结果赋值给 n
  4. 循环继续: 继续在循环中执行上述步骤,直到 n 等于1或者 n 已经在 seen 中出现过。
  5. 返回结果: 最终判断 n 是否等于1,如果是则返回 true,否则返回 false

这个算法使用了哈希集合来检测循环,避免了在链表中进行双指针遍历的方式。这样的实现在空间上相对较高效,因为哈希集合的查找和插入操作都是平均 O(1) 的时间复杂度。算法的时间复杂度取决于是否存在循环,最坏情况下是 O(logN)。

以下是对应的C++代码:

class Solution {
public:
    bool isHappy(int n) {
        // 使用一个集合来存储已经出现过的数字,以检测循环
        unordered_set<int> seen;
        
        while (n != 1 && seen.find(n) == seen.end()) {
            seen.insert(n);
            int sum = 0;
            while (n > 0) {
                int digit = n % 10;
                sum += digit * digit;
                n /= 10;
            }
            n = sum;
        }
        
        return n == 1;
    }
};

04.盛最多水的容器

题目链接:https://leetcode.cn/problems/container-with-most-water/

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

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

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

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

示例 1:
在这里插入图片描述

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

思路

首先看到这种题的第一种想法可能都是暴力枚举每一种情况,但是这样做的复杂度太大,是会超时的,其实这里我们可以通过对撞指针的方式不断改变一边的高度,进而更新最大容量,直至指针相遇。

核心思想和流程的详细解释:

  1. 初始化: 使用两个指针 leftright 分别指向数组的起始和结束位置,同时初始化变量 ret 为0,用来记录最大的容器面积。
  2. 循环迭代: 在一个循环中,计算当前 leftright 指向的两个线段之间的容器面积,并将其与 ret 中记录的最大面积进行比较。然后,将 leftright 中较小的那个指针向内移动一步。
  3. 移动指针: 移动指针的规则是选择两个线段中较短的那个向内移动。这是因为如果移动较长的线段,容器的宽度减小,而高度最大不会增加;而如果移动较短的线段,有可能找到更高的线段,从而提高容器的高度。
  4. 更新最大面积: 在每次计算完容器的面积后,更新 ret 的值为当前面积与 ret 中记录的最大面积的较大者。
  5. 循环继续: 重复上述步骤,直到 leftright 指针相遇。
  6. 返回结果: 返回 ret,即找到的最大容器面积。

这个算法的核心在于通过不断移动指针,选择较短的线段向内移动,以期望找到更高的线段来提高容器的高度。这样的策略保证了在寻找最大容器面积的过程中,不漏过可能的最优解。算法的时间复杂度是 O(n)。

以下是对应的C++代码:

class Solution {
public:
    int maxArea(vector<int>& height) {
        int left=0,right=height.size()-1,ret=0;
        while(left<right){
            int v=min(height[left],height[right])*(right-left);
            ret=max(ret,v);
            if(height[left]<height[right]) left++;
            else right--;
        }
        return ret;
    }
};

05.有效三角形的个数

题目链接:https://leetcode.cn/problems/valid-triangle-number/description/

给定一个包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数。

示例 1:

输入: nums = [2,2,3,4]
输出: 3
解释:有效的组合是: 
2,3,4 (使用第一个 2)
2,3,4 (使用第二个 2)
2,2,3

示例 2:

输入: nums = [4,2,3,4]
输出: 4

思路

这里使用暴力枚举同样会超时,所以这里同样可以使用双指针的方式来解决

核心思想是通过排序数组,然后使用双指针的方式,依次选择一个较大的元素作为三角形的最大边,然后在左边的子数组中使用双指针寻找满足三角形条件的组合。

以下是这个算法的详细解释:

  1. 排序数组: 首先对输入数组 nums 进行排序,以便于使用双指针的方式进行查找。
  2. 遍历最大边: 从数组的最后一个元素开始,即 i = n-1,向前遍历数组。这个元素作为三角形的最大边。
  3. 双指针查找: 使用两个指针 leftright 分别指向当前最大边的前两个元素,即 left = 0right = i-1
  4. 判断是否构成三角形: 在一个循环中,判断 nums[left] + nums[right] > nums[i] 是否成立。如果成立,说明当前的组合 (nums[left], nums[right], nums[i]) 可以构成一个三角形。此时,由于数组已经排序,那么左边的元素 nums[left] 和任何 nums[k](其中 left < k < right)都能够构成三角形。因此,ret += right - left 表示当前 left 对应的元素与 right 之间的元素均能够构成三角形,加到结果中。
  5. 更新指针: 根据当前判断的结果,更新指针的位置,即 right--left++
  6. 循环继续: 继续上述步骤,直到 left >= right,然后将 i 减一,进入下一轮循环。
  7. 返回结果: 最终得到的 ret 即为满足条件的三角形组合的数量。

以下是对应的C++代码:

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        
        int ret=0;
        int n=nums.size();
        for(int i=n-1;i>=2;i--){
            int left=0;
            int right=i-1;
            while(left<right){
                if(nums[left]+nums[right]>nums[i]){
                    ret+=right-left;
                    right--;
                }else left++;
            }
        }
        return ret;
    }
};

06.和为s的两个数字

题目链接:https://leetcode.cn/problems/he-wei-sde-liang-ge-shu-zi-lcof/description/

购物车内的商品价格按照升序记录于数组 price。请在购物车中找到两个商品的价格总和刚好是 target。若存在多种情况,返回任一结果即可。

示例 1:

输入:price = [3, 9, 12, 15], target = 18
输出:[3,15] 或者 [15,3]

示例 2:

输入:price = [8, 21, 27, 34, 52, 66], target = 61
输出:[27,34] 或者 [34,27]

思路

同样暴力枚举会超时,因为这里是升序数组,我们使用双指针的解法可以非常轻松解决

  1. 初始化指针: 使用两个指针 leftright 分别指向数组的第一个元素和最后一个元素。
  2. 循环查找: 在一个循环中,计算当前 leftright 指向的两个元素的和 sum。如果 sum > target,说明当前和过大,需要将 right 向左移动一步;如果 sum < target,说明当前和过小,需要将 left 向右移动一步;如果 sum == target,则找到了满足条件的两个元素,返回结果 {nums[left], nums[right]}
  3. 循环继续: 继续上述步骤,直到 left >= right,循环结束。
  4. 返回结果: 如果循环结束时仍未找到满足条件的两个元素,则返回一个空数组。

这个算法的核心思想是利用有序数组的特性,通过双指针在数组中夹逼的方式,不断调整指针的位置,直到找到满足条件的两个元素或者循环结束。

这个算法的时间复杂度是 O(n)。因为每次循环中,leftright 至少有一个向中间移动一步,所以总体的时间复杂度是线性的。

以下是对应的C++代码:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> ret;
        for(int left=0,right=nums.size()-1;left<right;){
            int sum=nums[left]+nums[right];
            if(sum>target) right--;
            else if(sum<target) left++;
            else return {nums[left],nums[right]};
        }
        return {};
    }
};

07.三数之和

给你一个整数数组 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 。

思路

其实这道题和两数之和解法类似,还是使用双指针的方式,只不过这里需要我们先排序,具体思路如下

  1. 排序: 对输入数组 nums 进行升序排序,这样可以使得相同的元素相邻,方便后续的去重操作。
  2. 遍历: 使用一个外层循环,固定一个数 nums[i] 作为三元组的第一个元素。
  3. 双指针: 在内层循环中,使用两个指针 leftright,分别指向当前数 nums[i] 的下一个元素和数组的最后一个元素。计算当前三个数的和 sum
  4. 判断和与目标值的关系:
    • 如果 sum > 0,说明右侧的数太大,将 right 向左移动一步。
    • 如果 sum < 0,说明左侧的数太小,将 left 向右移动一步。
    • 如果 sum == 0,说明找到了一个满足条件的三元组,加入结果集中,并进行去重操作。
  5. 去重操作: 在找到一个满足条件的三元组后,需要对 leftright 进行去重操作,即跳过相同的元素,避免结果中出现重复的三元组。
  6. 去重 i: 在外层循环中,找到一个满足条件的三元组后,也需要对 i 进行去重操作,即跳过相同的数,避免结果中出现相同的三元组。
  7. 返回结果: 最终返回所有满足条件的三元组组成的结果集。

以下是对应的C++代码:

class Solution
{
public:
	vector<vector<int>> threeSum(vector<int>& nums)
	{
		vector<vector<int>> ret;
		sort(nums.begin(), nums.end());
		int n = nums.size();
        
		for (int i = 0; i < n; ) 
		{
			if (nums[i] > 0) break; //没有负数或等于0的情况和不可能为0故直接跳出
			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--;
					// 去重操作 left 和 right
					while (left < right && nums[left] == nums[left - 1]) left++;
					while (left < right && nums[right] == nums[right + 1])
						right--;
				}
			}
			// 去重 i
			i++;
			while (i < n && nums[i] == nums[i - 1]) i++;
		}
		return ret;
	}
};

08.四数之和

题目链接:https://leetcode.cn/problems/4sum/description/

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • abcd 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target

你可以按 任意顺序 返回答案 。

示例 1:

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

示例 2:

输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]

思路

看到这个题目时,我们不难发现,可以用双指针的算法进行求解,这道题目还是使用类似三数之和的操作,只不过这里多了一个操作数,我们只需再加一层循环嵌套,以及一些去重优化即可,具体思路如下:

  1. 排序: 对输入数组 nums 进行升序排序,这样可以使得相同的元素相邻,方便后续的去重操作。
  2. 遍历: 使用两个外层循环,分别固定两个数 nums[i]nums[j] 作为四元组的前两个元素。
  3. 双指针: 在内层循环中,使用两个指针 leftright,分别指向当前数 nums[i]nums[j] 的下一个元素。计算当前四个数的目标和 aim
  4. 判断和与目标值的关系:
    • 首先这里不管是aim值还是sum值都要注意一个溢出的问题,所以我们这里都要使用long long进行类型的提升
    • 如果 sum > aim,说明右侧的数太大,将 right 向左移动一步。
    • 如果 sum < aim,说明左侧的数太小,将 left 向右移动一步。
    • 如果 sum == aim,说明找到了一个满足条件的四元组,加入结果集中,并进行去重操作。
  5. 去重操作: 在找到一个满足条件的四元组后,需要对 leftrightij 进行去重操作,即跳过相同的元素,避免结果中出现重复的四元组。
  6. 返回结果: 最终返回所有满足条件的四元组组成的结果集。
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(),nums.end());
        vector<vector<int>> ret;
        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){
                    long long sum=(long long)nums[left]+nums[right];
                    if(sum>aim) right--;
                    else if(sum<aim) left++;
                    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;
    }
};

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

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

相关文章

Kano模型

目录 1.介绍&#xff1a;2.Kano模型的作用&#xff1a;3.KANO模型使用场景&#xff1a;4.使用步骤&#xff1a;4.1设计问卷&#xff1a;4.2 数据分析4.2.1 KANO属性4.2.2 Better系数、Worse系数4.2.3 举例&#xff1a; 小结&#xff1a; 1.介绍&#xff1a; Kano模型是一种质量…

C#常见内存泄漏

背景 在开发中由于对语言特性不了解或经验不足或疏忽&#xff0c;往往会造成一些低级bug。而内存泄漏就是最常见的一个&#xff0c;这个问题在测试过程中&#xff0c;因为操作频次低&#xff0c;而不能完全被暴露出来&#xff1b;而在正式使用时&#xff0c;由于使用次数增加&…

【JavaScript 基础入门】02 JavaScrip 详细介绍

JavaScrip 详细介绍 目录 JavaScrip 详细介绍1. JavaScript 是什么2. JavaScript的作用3. HTML/CSS/JS 的关系4. 浏览器执行 JS 简介5. JavaScript 的组成6. JavaScript 的特点 1. JavaScript 是什么 JavaScript&#xff0c;通常缩写为 JS&#xff0c;是一种高级的&#xff0c;…

【SpringSpringBoot】概述

Spring&SpringBoot专题 【注】&#xff1a; 本专题围绕框架核心概念展开&#xff0c;渐进式深入总结学习、面试、开发经验&#xff0c;集中整理便于回顾 持续补充与施工中~~~~ 1.发展史 2.基本架构 Spring框架的基本架构是一个分层架构&#xff0c;包括多个模块&#x…

STL---stackqueue

一、stack 1.stack的介绍 stack介绍文档 https://legacy.cplusplus.com/reference/stack/stack/?kwstack 1. stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其删除只能从容器的一端进行元素的插入与提取操作。 2. stack是作为容器适…

AI技术大揭秘:探索人工智能的核心领域与必备技能

随着人工智能的不断进步&#xff0c;AI技术在各个领域都发挥着越来越关键的作用。想要成为AI领域的从业者&#xff0c;不仅需要对整体格局有清晰认识&#xff0c;更要掌握关键技术和必备技能。本文将深入解析AI的核心技术领域&#xff0c;以及在这个前沿领域里需要掌握的技能。…

Java_集合类

集合可以看作是一个容器&#xff0c;集合中的各个对象&#xff0c;很容易将其从集合中取出来&#xff0c;也很容易将其存放到集合中&#xff0c;还可以按照一定的顺序进行摆放。JAVA中提供了不同的集合类&#xff0c;这些类具有不同的存储对象的方式&#xff0c;同时提供了相应…

04-JVM虚拟机-课堂笔记

04-JVM虚拟机 1. JVM虚拟机概述 1.4 对象的创建流程与内存分配 1.4.1 创建流程 1.4.2 对象内存分配方式 内存分配的方法有两种&#xff1a;不同垃圾收集器不一样 指针碰撞(Bump the Pointer) 空闲列表(Free List) 分配方法说明收集器指针碰撞(Bump the Pointer)内存地址…

论机器生产内容MGC与新数字时代的两个世界

摘要&#xff1a;本文从新数字时代人类社会的两种存在形态&#xff1a;数字世界&#xff08;元宇宙&#xff09;与物理世界&#xff08;时空宇宙&#xff09;&#xff0c;以及新兴数字产业&#xff1a;机器生产内容MGC的发展、现状与未来出发&#xff0c;通过对新数字时代及两个…

【MIdjourne基础】 |MIdjourney基础参数全解析,各类辅助知识

文章目录 1 参数列表1.1 基础参数列表 2 基础参数详解2.1 模型版本选择2.2 模型出图模式选择2.3 基础生图参数2.3.1 --ar2.3.2 --stylize2.3.3 --no2.3.4 --chaos2.3.5 --quality2.3.6 --stop2.3.7 --hd2.3.8 --repeat 1 参数列表 1.1 基础参数列表 模型版本选择 目标参数作…

什么是微服务?(微服务的技术栈)

微服务是一种架构风格&#xff0c;它将一个单一的应用拆分为多个小型的服务&#xff0c;每个服务运行在自己的进程中&#xff0c;服务间采用轻量级的通信机制&#xff08;如HTTP/webservice等&#xff09;。这些服务围绕业务能力构建&#xff0c;并且可以全自动独立部署。微服务…

PageHelper 分页逻辑 源码解析

一、PageHelper PageHelper 是一个用于在 MyBatis 中进行分页查询的开源分页插件。它能够方便地帮助开发者处理分页查询的逻辑&#xff0c;简化代码&#xff0c;并提高开发效率。PageHelper 支持多种数据库&#xff0c;包括 MySQL、Oracle、PostgreSQL 等。 PageHelper 的实现…

了解OpenCV的数据类型

OpenCV是一个开源的计算机视觉库&#xff0c;广泛应用于图像和视频处理领域。在OpenCV中&#xff0c;数据类型扮演着非常重要的角色&#xff0c;它们决定了数据的存储方式和操作方式。本文将介绍OpenCV中常见的数据类型&#xff0c;包括图像数据类型、矩阵数据类型和轮廓数据类…

使用Python和ffmpeg旋转WebM视频并保存为MP4文件

简介: 在本篇博客中&#xff0c;我们将介绍如何使用Python编写一个程序&#xff0c;结合wxPython和ffmpeg模块&#xff0c;来旋转WebM视频文件并将其保存为MP4格式。我们将使用wxPython提供的文件选择对话框来选择输入和输出文件&#xff0c;并使用ffmpeg库来进行视频旋转操作。…

IS-IS:09 ISIS路由过滤

在IS-IS 网络中&#xff0c;有时需要使用 filter-policy 工具对 IS-IS 路由进行过滤。这里所说的过滤&#xff0c;是指路由器在将自己IS-IS 路由表中的某些 IS-IS 路由纳入进自己的 IP 路由表的过程&#xff0c;一些满足了过滤条件的 IS-IS 路由将被限制纳入 IP 路由表中。 需要…

程序员该懂的一些测试(四)测试覆盖率

测试覆盖率通常被用来衡量测试的充分性和完整性&#xff0c;从广义的角度来讲&#xff0c;测试覆盖率主要分 为两大类&#xff0c;一类是面向项目的需求覆盖率&#xff0c;另一类是更偏向技术的代码覆盖率。 需求覆盖率 需求覆盖率是指测试对需求的覆盖程度&#xff0c;通常的…

Linux 驱动开发基础知识——总线设备驱动模型(七)

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;Vir2021GKBS &#x1f43c;本文由…

C++ //练习 3.5 编写一段程序从标准输入中读入多个字符串并将它们连接在一起,输出连接成的大字符串。然后修改上述程序,用空格把输入的多个字符串分隔开来。

C Primer&#xff08;第5版&#xff09; 练习 3.5 练习 3.5 编写一段程序从标准输入中读入多个字符串并将它们连接在一起&#xff0c;输出连接成的大字符串。然后修改上述程序&#xff0c;用空格把输入的多个字符串分隔开来。 环境&#xff1a;Linux Ubuntu&#xff08;云服务…

.NET高级面试指南专题三【线程和进程】

在C#中&#xff0c;线程&#xff08;Thread&#xff09;和进程&#xff08;Process&#xff09;是多任务编程中的重要概念&#xff0c;它们用于实现并发执行和多任务处理。 进程&#xff08;Process&#xff09;&#xff1a; 定义&#xff1a; 进程是正在运行的程序的实例&…

ThinkPhp3.2(qidian)部署文档

宝塔环境部署 申请域名以及域名解析 具体配置&#xff0c;可百度之 在宝塔面板中创建网站 上传代码导入数据配置运行目录 注意&#xff1a;&#xff08;如果版本&#xff1a;thinkphp3.2 &#xff09;配置 运行目录要特别注意&#xff1a;运行目录要选择根目录“/”&#xff…