Python数据结构与算法篇(二)-- 数组常见题型与解题技巧

news2024/11/14 22:07:57

        数组和链表代表着计算机最基本的两种存储形式:顺序存储和链式存储,所以他俩可以算是最基本的数据结构。数组是一种基础数据结构,可以用来处理常见的排序和二分搜索问题,典型的处理技巧包括对双指针、滑动窗口等,数组是数据结构中的基本模块之一。因为字符串是由字符数组形成的,所以二者是相似的。

        双指针⼜分为中间向两端扩散的双指针、两端向中间收缩的双指针、快慢指针。此外,数组还有前缀和和差分数组也属于必知必会的算法技巧。

1 常用技巧

1.1 滑动窗口/双指针

1. 定义

        在计算机网络里经常用到滑动窗口协议(Sliding Window Protocol),该协议是 TCP协议 的一种应用,用于网络数据传输时的流量控制,以避免拥塞的发生。该协议允许发送方在停止并等待确认前发送多个数据分组。由于发送方不必每发一个分组就停下来等待确认。因此该协议可以加速数据的传输,提高网络吞吐量。

        滑动窗口算法其实和这个是一样的,只是用的地方场景不一样,可以根据需要调整窗口的大小,有时也可以是固定窗口大小。

        滑动窗口使用双指针解决问题,所以一般也叫双指针算法,因为两个指针间形成一个窗口。双指针也并不局限在数组问题,像链表场景的 “快慢指针” 也属于双指针的场景,其快慢指针滑动过程中本身就会产生一个窗口,比如当窗口收缩到某种程度,可以得到一些结论。

        什么情况适合用双指针呢?

  • 需要输出或比较的结果在原数据结构中是连续排列的,特别是数组或链表问题;
  • 每次窗口滑动时,只需观察窗口两端元素的变化,无论窗口多长,每次只操作两个头尾元素,当用到的窗口比较长时,可以显著减少操作次数;
  • 窗口内元素的整体性比较强,窗口滑动可以只通过操作头尾两个位置的变化实现,但对比结果时往往要用到窗口中所有元素。

        滑动窗口算法在一个特定大小的字符串或数组上进行操作,而不在整个字符串和数组上操作,这样就降低了问题的复杂度,从而也达到降低了循环的嵌套深度。其实这里就可以看出来滑动窗口主要应用在数组和字符串上。

2. 滑动窗口法的大体框架

        在介绍滑动窗口的框架时候,大家先从字面理解下:

  • 滑动:说明这个窗口是移动的,也就是移动是按照一定方向来的。
  • 窗口:窗口大小并不是固定的,可以不断扩容直到满足一定的条件;也可以不断缩小,直到找到一个满足条件的最小窗口;当然也可以是固定大小。

        为了便于理解,这里采用的是字符串来讲解。但是对于数组其实也是一样的。滑动窗口算法的思路是这样:

  1. 我们在字符串 S 中使用双指针中的左右指针技巧,初始化 left = right = 0,把索引闭区间 [left, right] 称为一个「窗口」。
  2. 我们先不断地增加 right 指针扩大窗口 [left, right],直到窗口中的字符串符合要求(包含了 T 中的所有字符)。
  3. 此时,我们停止增加 right,转而不断增加 left 指针缩小窗口 [left, right],直到窗口中的字符串不再符合要求(不包含 T 中的所有字符了)。同时,每次增加 left,我们都要更新一轮结果。
  4. 重复第 2 和第 3 步,直到 right 到达字符串 S 的尽头。

        这个思路其实也不难,第 2 步相当于在寻找一个「可行解」,然后第 3 步在优化这个「可行解」,最终找到最优解。左右指针轮流前进,窗口大小增增减减,窗口不断向右滑动。

1.2 前缀和与差分数组

        前缀和是指某序列的前 n 项和,可以把它理解为数学上的数列的前 n 项和,差分数组是与前缀和数组所对应的一种逆操作,类似于求导和积分,也就是说,对差分数组求前缀和,可以得到原数组,同样的,对前缀和数组求差分,也可以得到原数组。合理的使用前缀和与差分,可以将某些复杂的问题简单化。

1. 前缀和

        假设有一个序列 A,前缀和为 S。根据概念很容易知到公式

S [ i ] = ∑ j = 1 i A [ j ] S[i]=\displaystyle \sum_{j=1}^iA[j] S[i]=j=1iA[j]

        如何求区间 [ l , r ] [l,r] [l,r] 的和呢?

s u m [ l , r ] = s [ r ] − s [ l − 1 ] sum[l,r]=s[r]-s[l-1] sum[l,r]=s[r]s[l1]

2. 差分数组

        设原数组为 A[i],差分数组为 diff[i],则:

d i f f [ i ] = { A [ i ] i = 1 A [ i ] − A [ i − 1 ] i ≥ 2 diff[i]=\begin{cases} A[i]&i=1\\ A[i]-A[i-1]&i\geq2 \end{cases} diff[i]={A[i]A[i]A[i1]i=1i2

        差分数组的性质是:

  • 如果对区间 [ l , r ] [l,r] [l,r] 进行修改,只需修改 d i f f [ l ] , d i f f [ r + 1 ] diff[l], diff[r+1] diff[l],diff[r+1](diff[l]加上修改值,diff[r+1] 减去修改值)
  • A [ i ] = ∑ j = 1 i B [ j ] A[i]=\displaystyle \sum_{j=1}^{i}B[j] A[i]=j=1iB[j](通过 B [ i ] = A [ i ] − A [ i − 1 ] B[i]=A[i]-A[i-1] B[i]=A[i]A[i1] 证明)
  • S [ x ] = ∑ i = 1 x A [ i ] = ∑ i = 1 x ∑ j = 1 i d i f f [ j ] = ∑ i = 1 x ( x − i + 1 ) ∗ d i f f [ i ] S[x]=\displaystyle \sum_{i=1}^{x}A[i]=\displaystyle \sum_{i=1}^{x} \displaystyle \sum_{j=1}^{i}diff[j]=\displaystyle \sum _{i=1}^{x}(x-i+1)*diff[i] S[x]=i=1xA[i]=i=1xj=1idiff[j]=i=1x(xi+1)diff[i]$

当我们希望对原数组的某一个区间 [i, j] 施加一个增量 inc 时,差分数组d对应的变化是:d[i] 增加 inc,d[j+1] 减少 inc,并且这种操作是可以叠加的。

        下面举个例子:

        差分数组是一个辅助数组,从侧面来表示给定某一数组的变化,一般用来对数组进行区间修改的操作。

        还是上面那个表里的例子,我们需要进行以下操作:

  1. 将区间[1,4]的数值全部加上3
  2. 将区间[3,5]的数值全部减去5

        很简单对吧,你可以进行枚举。但是如果给你的数据量是 1 × e 5 1\times e^5 1×e5,操作量 1 × e 5 1\times e^5 1×e5,限时1000ms你暴力枚举能莽的过去吗?慢到你怀疑人生直接。这时我们就需要使用到差分数组了。

        其实当你将原始数组中元素同时加上或者减掉某个数,那么他们的差分数组其实是不会变化的。

        利用这个思想,咱们将区间缩小,缩小的例子中的区间 [1,4] 吧这是你会发现只有 d[1] 和 d[5] 发生了变化,而 d[2], d[3], d[4]却保持着原样,

        进行下一个操作,

        这时我们就会发现这样一个规律,当对一个区间进行增减某个值的时候,他的差分数组对应的区间左端点的值会同步变化,而他的右端点的后一个值则会相反地变化,其实这个很好理解。

        本部分参考自:差分详解+例题

        也就是说,当我们需要对原数组的不同区间施加不同的增量,我们只要按规则修改差分数组即可。所以,差分数组的主要适⽤场景是频繁对原始数组的某个区间的元素进⾏增减,但只能是区间元素同时增加或减少相同的数的情况才能用。

        有 n n n 个数, m m m 个操作,每一次操作,将 x ∼ y x\sim y xy 区间的所有数增加 z z z;最后有 q q q 个询问,每一次询问求出 x ∼ y x\sim y xy 的区间和。设原数组为 A [ i ] A[i] A[i]。其步骤为:

  • 先求出差分数组 B [ i ] = A [ i ] − A [ i − 1 ] B[i]=A[i]−A[i−1] B[i]=A[i]A[i1]
  • 在根据 m m m 个造作修改 B [ i ] B[i] B[i]
  • 求修改后的 A [ i ] = A [ i − 1 ] + B [ i ] A[i]=A[i−1]+B[i] A[i]=A[i1]+B[i]
  • 求前缀和 S [ i ] = S [ i − 1 ] + A [ i ] S[i]=S[i−1]+A[i] S[i]=S[i1]+A[i]
  • 最后输出区间和 s u m [ x , y ] = S [ y ] − S [ x − 1 ] sum[x,y]=S[y]−S[x−1] sum[x,y]=S[y]S[x1]

2 常见题型

2.1 删除数组元素

  • 掌握数组删除元素的直接覆盖操作
  • 双指针法

题库列表:

  • 27. 移除元素 (快慢指针)

  • 26. 删除有序数组中的重复项 (快慢指针)

  • 80. 删除有序数组中的重复项 II

  • 75. 颜色分类(双指针,三色旗,小米笔试)

27. 移除元素

        题目描述:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O ( 1 ) O(1) O(1) 额外空间并 原地 修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        '''拷贝覆盖'''
        ans = 0
        for num in nums:
            if num!= val:
                nums[ans] = num
                ans += 1
        return ans

26. 删除有序数组中的重复项

        题目描述:给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        slow, fast = 0, 1
        while fast < len(nums):
            if nums[fast] != nums[slow]:
                slow += 1
                nums[slow] = nums[fast]
            fast += 1
        
        return slow + 1

80. 删除有序数组中的重复项 II

        题目描述:给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O ( 1 ) O(1) O(1) 额外空间的条件下完成。

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        slow, fast = 1, 2
        while fast < len(nums):
            if nums[fast] != nums[slow-1]:
                slow += 1
                nums[slow] = nums[fast]
            fast += 1
        return  slow + 1

通用解法:

        为了让解法更具有一般性,我们将原问题的 「最多保留 1 位」修改为「最多保留 k 位

        对于此类问题,我们应该进行如下考虑:

        由于是保留 k 个相同数字,对于前 k 个数字,我们可以直接保留。

        对于后面的任意数字,能够保留的前提是:与当前写入的位置前面的第 k 个元素进行比较,不相同则保留。

        此时,初始化时指针 p 指向数组的起始位置(nums[k-1]),指针 q 指向指针 p 的后一个位置(nums[k])。随着指针 q 不断向后移动,将指针 q 指向的元素与指 p 指向的元素进行比较:

  • 如果nums[q] ≠ nums[p-k+1],那么nums[p + 1] = nums[q];
  • 如果nums[q] = nums[p],那么指针q继续向后查找;

75. 颜色分类

        题目描述:给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

1. 单指针

class Solution:
    def sortColors(self, nums: List[int]) -> None:
        i = 0
        length = len(nums)
        for j in range(length):
            if nums[j] == 0:
                nums[i], nums[j] = nums[j], nums[i]
                i += 1
        for k in range(i, length):
            if nums[k] == 1:
                nums[k], nums[i] = nums[i], nums[k]
                i += 1

2. 双指针

class Solution:
    def sortColors(self, nums: List[int]) -> None:
        # 定义三个变量,p0 表示数组最左边0的区域,p1是数组最右边2的区域
        i, p0, p1 = 0, 0 , len(nums)-1
        while i <= p1:
            # 如果当前指向的是 0,就把这个元素交换到数组左边
			# 也就是跟 p0 指针交换,之后cur,p0 就往前一动一位
            if nums[i] == 0:
                nums[i], nums[p0] = nums[p0], nums[i]
                p0 += 1
                i += 1
            # 如果当前指向的是2,就把这个元素交换到数组右边
			# 也就是跟p2指针交换,注意此时cur指针就不用移动了
			# 因为右边的一片区域都是2,只要把元素交换过去就可以了,cur不用移动
            elif nums[i] == 2:
                nums[i], nums[p1] = nums[p1], nums[i]
                p1 -= 1
            # 如果是1的话,就不用交换	
            else:
                i += 1

2.2 双指针技巧

题库列表:

  • 88. 合并两个有序数组:如何将数组所有元素整体后移,防止数组覆盖?

  • 167. 两数之和 II - 输入有序数组(有序数列的首尾双指针)

  • 125. 验证回文串

  • 345. 反转字符串中的元音字母

  • 11. 盛最多水的容器:经典题目

  • 209. 长度最小的子数组:滑动窗口

  • 56. 合并区间:数组类操作

88. 合并两个有序数组

        题目描述:给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列

逆向双指针

class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        """
        nums1[m:] = nums2               # 直接合并后排序
        nums1.sort()           
        """
        p0, p1, p2 = m-1, n-1, m+n-1
        while p0 >= 0 or p1 >= 0:
            if p0 == -1:
                nums1[p2] = nums2[p1]
                p1 -= 1
            elif p1 == -1:
                nums1[p2] = nums1[p0]
                p0 -= 1
            elif nums1[p0] > nums2[p1]:
                nums1[p2] = nums1[p0]
                p0 -= 1
            else:
                nums1[p2] = nums2[p1]
                p1 -= 1
            p2 -= 1

167. 两数之和 II - 输入有序数组

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

class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        head, tail = 0, len(numbers)-1
        while head < tail:
            two_sum = numbers[head] + numbers[tail]
            if two_sum == target:
                return [head+1, tail+1]
            elif two_sum > target:
                tail -= 1
            else:
                head += 1

125. 验证回文串

        题目描述:如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串。字母和数字都属于字母数字字符。

import re
class Solution:
    def isPalindrome(self, s: str) -> bool:
        '''
        # 思路一:正则表达式
        if not s:
            return True
        s = s.lower()
        pattern = re.compile(r'[^a-z0-9]')   # 正则表达式,把数字和字母都剔除掉
        new_str = pattern.sub('', s)
        return new_str == new_str[::-1]
        '''
        # 字符串预处理
        new_str = ''.join(ch.lower() for ch in s if ch.isalnum())
        return new_str == new_str[::-1]

        这里使用了正则表达式移除所有非字母数字字符,然后判断新的字符串是否是回文,也可以使用双指针,直接一次遍历,遇到字母数字字符就进行判断。

345. 反转字符串中的元音字母

        题目描述:给你一个字符串 s ,仅反转字符串中的所有元音字母,并返回结果字符串。元音字母包括 ‘a’、‘e’、‘i’、‘o’、‘u’,且可能以大小写两种形式出现不止一次。

class Solution:
    def reverseVowels(self, s: str) -> str:
        str_set = set("aeiouAEIOU")
        head, tail = 0, len(s) - 1
        str_list = list(s)
        while head < tail:
            if str_list[head] in str_set and str_list[tail] in str_set:
                str_list[head], str_list[tail] = str_list[tail], str_list[head]
                head += 1
                tail -= 1
            elif str_list[head] in str_set and str_list[tail] not in str_set:
                tail -= 1
            elif str_list[head] not in str_set and str_list[tail] in str_set:
                head += 1
            else:
                head += 1
                tail -= 1
        return ''.join(str_list)

11. 盛最多水的容器

        题目描述:给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。

class Solution:
    def maxArea(self, height: List[int]) -> int:
        # 双指针,移动小的那一边
        head, tail = 0, len(height)-1
        res = 0
        while head < tail:
            if height[head] < height[tail]:
                res = max(res, height[head]*(tail-head))
                head += 1
            else:
                res = max(res, height[tail]*(tail-head))
                tail -= 1
        return res

209. 长度最小的子数组

        题目描述:给定一个含有 n 个正整数的数组和一个正整数 target。找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [ n u m s l , n u m s l + 1 , . . . , n u m s r − 1 , n u m s r ] [nums_l, nums_{l+1}, ..., nums_{r-1}, nums_r] [numsl,numsl+1,...,numsr1,numsr],并返回其长度。如果不存在符合条件的子数组,返回 0。

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        '''
        我们把数组中的右指针右移,直到总和大于等于 target 为止,记录个数;
        然后左指针右移,直到队列中元素的和小于 target 为止,记录个数;
        重复,直到右指针到达队尾。
        '''
        if min(nums) > target or sum(nums) < target:
            return 0
        min_len = inf
        head, tail = 0, 0
        total = 0
        while tail < len(nums):     # 右指针滑动
            total += nums[tail]
            while total >= target:  # 固定右指针,滑动左指针,求最小子数组
                min_len = min(min_len, tail - head + 1)
                total -= nums[head]
                head += 1
            tail += 1

        return min_len

56. 合并区间

        题目描述:以数组 intervals 表示若干个区间的集合,其中单个区间为 i n t e r v a l s [ i ] = [ s t a r t i , e n d i ] intervals[i] = [starti, endi] intervals[i]=[starti,endi]。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。

class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        result= []
        intervals.sort(key=lambda x:x[0])
        for interval in intervals:
            # 如果列表为空,或者当前区间与上一区间不重合,直接添加
            if not result or result[-1][1] < interval[0]:
                result.append(interval)
            else:
                # 否则的话,我们就可以与上一区间进行合并
                result[-1][1] = max(result[-1][1], interval[1])
        return result

2.3 前缀和与差分数组

题库列表:

  • 303. 区域和检索 - 数组不可变:一维前缀和

  • 304. 二维区域和检索 - 矩阵不可变:二维前缀和

  • 370. 区间加法:差分数组

  • 1109. 航班预订统计:差分数组

  • 1094. 拼车:差分数组

303. 区域和检索 - 数组不可变

        题目描述:

        一维前缀和

class NumArray:

    def __init__(self, nums: List[int]):
        self.nums_array = [0]            # 便于计算累加和
        for i in range(len(nums)):
            self.nums_array.append(self.nums_array[i] + nums[i])  # 计算nums累加和

    def sumRange(self, left: int, right: int) -> int:
        return self.nums_array[right+1] - self.nums_array[left]

304. 二维区域和检索 - 矩阵不可变

        题目描述:

        二维前缀和

class NumMatrix:

    def __init__(self, matrix: List[List[int]]):
        m, n = len(matrix), len(matrix[0])                  # 矩阵的行和列
        self.pre_sum = [[0]*(n+1) for _ in range(m+1)]      # 构造一维前缀和矩阵
        for i in range(m):
            for j in range(n):
                self.pre_sum[i+1][j+1] = self.pre_sum[i+1][j] + self.pre_sum[i][j+1] - self.pre_sum[i][j] + matrix[i][j]

    def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
        return (self.pre_sum[row2+1][col2+1] - self.pre_sum[row1][col2+1] - self.pre_sum[row2+1][col1] + self.pre_sum[row1][col1])

370. 区间加法

        题目描述:假设你有一个长度为n的数组,初始情况下所有的数字均为0,你将会被给出k个更新的操作。其中,每个操作会被表示为一个三元组: [startIndex, endIndex, inc],你需要将子数组 A[startIndex, endIndex](包括startlndex和endIndex)增加 inc。
请你返回 k 次操作后的数组。

class Solution:
    def getModifiedArray(self, length: int, updates: List[List[int]]) -> List[int]:
        diff = [0] * (length+1)  # 末尾多个0,防止越界
        
        for update in updates:
            start, end, inc = update[0], update[1], update[2]
            diff[start] += inc
            diff[end + 1] -= inc
        
        for i in range(1, length):
            diff[i] += diff[i - 1]            # 对差分数组求前缀和便可得到原数组
            
        return diff[:-1]

1109. 航班预订统计

        题目描述:这里有 n 个航班,它们分别从 1 到 n 进行编号。有一份航班预订表 bookings ,表中第 i i i 条预订记录 b o o k i n g s [ i ] = [ f i r s t i , l a s t i , s e a t s i ] bookings[i] = [first_i, last_i, seats_i] bookings[i]=[firsti,lasti,seatsi] 意味着在从 f i r s t i first_i firsti l a s t i last_i lasti(包含 f i r s t i first_i firsti l a s t i last_i lasti)的 每个航班 上预订了 s e a t s i seats_i seatsi 个座位。请你返回一个长度为 n 的数组 answer,里面的元素是每个航班预定的座位总数。

class Solution:
    def corpFlightBookings(self, bookings: List[List[int]], n: int) -> List[int]:
        diff = [0] * (n+1)
        for booking in bookings:
            start, end, inc = booking[0], booking[1], booking[2]
            diff[start] += inc
            if end < n:             # 没在末尾添加0,要判断一下边界
                diff[end+1] -= inc
        for i in range(1, n+1):
            diff[i] += diff[i-1]
        return diff[1:]

1094. 拼车

        题目描述:车上最初有 capacity 个空座位。车只能向一个方向行驶(也就是说,不允许掉头或改变方向),给定整数 capacity 和一个数组 trips , t r i p [ i ] = [ n u m P a s s e n g e r s i , f r o m i , t o i ] trip[i] = [numPassengers_i, from_i, to_i] trip[i]=[numPassengersi,fromi,toi] 表示第 i i i 次旅行有 n u m P a s s e n g e r s i numPassengers_i numPassengersi 乘客,接他们和放他们的位置分别是 f r o m i from_i fromi t o i to_i toi。这些位置是从汽车的初始位置向东的公里数。当且仅当你可以在所有给定的行程中接送所有乘客时,返回 true,否则请返回 false。

class Solution:
    def carPooling(self, trips: List[List[int]], capacity: int) -> bool:
        diff = [0] * (1001)      					# 题目中最多有1001个车站
        max_station = 0          					# 找到车站数
        for trip in trips:
            inc, start, end = trip[0], trip[1], trip[2]
            diff[start] += inc
            diff[end] -= inc      					# 第end站乘客已经下车,这里就不用end+1
            max_station = max(max_station, end)
        for i in range(1, max_station+1): 			# 进行区间求和
            diff[i] += diff[i-1]
        if max(diff[:max_station]) > capacity:
            return False
        return True

参考

  • 数组+常见题型与解题策略:https://blog.csdn.net/qq_42647903/article/details/120594856
  • 差分详解+例题:https://blog.csdn.net/qq_44786250/article/details/100056975
  • 算法与数据结构(一):滑动窗口法总结:https://blog.csdn.net/Dby_freedom/article/details/89066140

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

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

相关文章

想要将多个视频拼接在一起?如何把三个视频合成一个视频

从事短视频创作行业以来&#xff0c;总是存在着各种挑战。最开始&#xff0c;因为主要负责视频素材的搜集&#xff0c;所以每天虽忙但充实&#xff0c;最近逐步开始学习视频的剪辑工作&#xff0c;可把我难到了&#xff01;想要将多个视频拼接在一起&#xff1f;如何把三个视频…

「 Java 8 新特性 」Stream 中的 map、peek、foreach 方法的区别

「 Java 8 新特性 」Stream 中的 map、peek、foreach 方法的区别 文章参考&#xff1a; 面试官问&#xff1a;Stream 中的 map、peek、foreach 方法的区别&#xff1f;傻傻分不清楚。。 stream中的map,peek,foreach的区别 一、概述 在学习java 8的stream api时&#xff0c;我们…

Java【数据结构入门OJ题33道】——力扣刷题记录1

文章目录第一天存在重复元素最大子数组和第二天两数之和合并两个有序数组第三天两个数组的交集买卖股票最佳时机第四天重塑矩阵杨辉三角第五天有效的数独矩阵置零第六天字符串中第一个唯一字符救赎金第七天判断链表是否有环合并两个有序链表移除链表元素第八天反转链表删除重复…

c++面试技巧-高级应用篇

1.面试官&#xff1a;什么是文件流&#xff1f; 应聘者&#xff1a;写入文件或者从文件读出的数据流称之为文件流。 2.面试官&#xff1a;文件流的类时如何划分的&#xff1f; 应聘者&#xff1a;当C对文件进行处理时&#xff0c;需要包括头文件iostream和fstream。fstream头…

游戏逆向之游戏技能分析

角色的当前技能列表往往都是从系统的技能库中进行筛选而组成的&#xff0c;而这个筛选的过程大多非常的复杂&#xff0c;经过的代码和临时结构体的传递也非常的多&#xff0c;所以在分析技能对象来源的时候常常要将OD和CE配合来使用。下面我们来分析下《天堂2》的技能列表。 首…

拼多多存在多种重大风险

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 中国电子商务市场发展迅速 拼多多&#xff08;PDD&#xff09;目前已经成为了在中国发展最快的电子商务平台之一。随着拼多多、阿里巴巴(BABA)和京东(JD)等市场主要参与者的竞争&#xff0c;中国电子商务正在迅速发展。 由…

安装SceneBuilder时出现Verify that you have access to that directory报错,解决方法之一

1、问题描述 安装SceneBuilder时出现Error writing to file...Verify that you have access to that directory报错&#xff0c;如图 2、解决步骤 1&#xff09;SceneBuilder-19.0.0.msi可从参考教程链接1获取&#xff0c;下载到文件夹D:\LcSceneBuilder\SceneBuilder-19.0.0…

Spring Cloud融合gateway自带GatewayFilter使用 | Spring Cloud 15

一、Spring Cloud Gateway内置GatewayFilter 路由过滤器允许以某种方式修改传入的 HTTP 请求或传出的 HTTP 响应。路由过滤器的范围是特定路由。Spring Cloud Gateway 包括许多内置的 GatewayFilter 工厂。 官网地址&#xff1a;https://docs.spring.io/spring-cloud-gateway…

MATLAB与图像处理的那点小事儿~

目录 一、学习内容 二、matlab基本知识 三、线性点运算 四、非线性点运算&#xff0c;伽马矫正 五、直方图 1、直方图均衡化 &#xff08;1&#xff09;使用histep函数实现图像均衡化 &#xff08;2&#xff09;使用自行编写的均衡化函数实现图像均衡化 2、直方图规定…

Ansys Zemax | 如何使用 OpticStudio 非序列优化向导

本文描述了如何使用 OpticStudio 非序列优化向导创建常见的评价函数类型&#xff0c;以及创建用于匹配导入图像文件的目标能量分布评价函数。&#xff08;联系我们获取文章附件&#xff09; 简介 在非序列模式下优化光学系统通常比在序列模式下的优化更复杂、更耗时。下期我们…

TencentOS tiny 移植到STM32F103教程

一、移植前的准备工作1. STM32的裸机工程模板这个可以自己创建&#xff08;创建过程参考之前的STM32裸机工程也有工程网盘在上面&#xff09;2.下载TencentOS tiny 源码TencentOS tiny的源码可从TencentOS tiny GitHub仓库地址https://github.com/Tencent/TencentOS-tiny下载&a…

无人机动力测试台:150kg级-Flight Stand 150

“飞行汽车”被人们广泛视为下一代交通工具。从2022年初至今&#xff0c;eVTOL&#xff08;电动垂直起降飞行器&#xff09;产业乃至整个UAM&#xff08;城市间空中交通&#xff09;市场呈现爆发式增长——各国航空制造企业纷纷入局&#xff0c;不断有新产品问世、试飞或试运行…

吐血整理,web自动化测试,POM模式搭建自动化测试框架(超级详细)

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 POM设计模式 主要是…

IO多路复用(select、poll、epoll网络编程)

目录一、高级IO相关1.1 同步通信和异步通信1.2 阻塞与非阻塞1.3 fcntl 函数二、五种IO模型2.1 阻塞式IO模型2.2 非阻塞式IO模型2.3 多路复用IO模型2.4 信号驱动式IO模型2.5 异步IO模型三、认识IO多路复用四、select4.1 认识select函数4.2 select函数原型4.3 select网络编程4.4 …

脂肪毒性的新兴调节剂——肠道微生物组

谷禾健康 肠道微生物组与脂质代谢&#xff1a;超越关联 脂质在细胞信号转导中起着至关重要的作用&#xff0c;有助于细胞膜的结构完整性&#xff0c;并调节能量代谢。 肠道微生物组通过从头生物合成和对宿主和膳食底物的修饰产生了大量的小分子。 最近的研究表明&#xff0c;由…

[计算机操作系统(第四版 汤小丹 汤子瀛)]第一章 操作系统引论(学习复习笔记)

操作系统&#xff08;Operating System&#xff0c;OS&#xff09;是配置在计算机硬件上的第一层软件&#xff0c;是对硬件系统的首次扩充。操作系统的主要作用&#xff1a; 管理硬件设备&#xff0c;提高他们的利用率和系统吞吐量 利用率&#xff1a;使硬件充分忙碌系统吞吐量…

MQTT协议-心跳请求和心跳响应

MQTT协议-心跳请求和心跳响应 心跳请求是客户端发送给服务器的&#xff0c;心跳响应是服务器回应给客户端的 作用是保持客户端与服务器之间的连接 CONNECT报文的可变报头是由协议名&#xff08;Procotol Name&#xff09;协议级别&#xff08;Procotol Level&#xff09;连接…

冰刃杀毒工具使用实验(29)

实验目的 &#xff08;1&#xff09;学习冰刃的基本功能&#xff1b; &#xff08;2&#xff09;掌握冰刃的基本使用方法&#xff1b;预备知识 windows操作系统的基本知识&#xff0c;例如&#xff1a;进程、网络、服务和文件等的了解。 冰刃是一款广受好评的ARK工…

MYSQL 中 ROW_NUMBER 的实现

由于mysql没有 row_number函数实现&#xff0c;然后我们后台表里没给可以排序的字段&#xff0c;考虑用mysql实现一个row_number, 由于想使用视图进行实时展示&#xff0c;但是row_number在视图中不生效&#xff0c;所有我们最后还是找了后台程序伙伴沟通加了排序字段。 row_nu…

MySQL的图形化界面开发工具DataGrip的下载安装

在日常的开发中&#xff0c;会借助于MySQL的图形化界面&#xff0c;来简化开发&#xff0c;提高开发效率。目前mysql主流的图形化界面工具&#xff0c;有Navicat、SQLyog、DataGrip等&#xff0c;最后一种DataGrip&#xff0c;这种图形化界面工具&#xff0c;功能更加强大&…