当前位置: 首页 > news >正文

【算法一周目】分而治之,归并如风:算法中的美学与哲理

文章目录

  • 1. 颜色分类
  • 2. 排序数组(快排)
  • 3. 数组中的第K个最大元素
  • 4. 最小的 k 个数
  • 5. 排序数组(归并)
  • 6. 数组中的逆序对
  • 7. 计算右侧小于当前元素的个数
  • 8. 翻转对

1. 颜色分类

题目链接: 75. 颜色分类

题目描述:

给定一个包含红色、白色和蓝色的数组 nums,共 n 个元素,要求对它们进行原地排序,使得相同颜色的元素相邻,并按红色、白色、蓝色的顺序排列。这里使用整数 012 分别表示红色、白色和蓝色,且不能使用库的排序函数

示例 1:

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

示例 2:

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

提示:

  • n == nums.length
  • 1 <= n <= 300
  • nums[i] 为 0、1 或 2

解题思路

对于颜色分类问题,我们可以采取三路划分的思想来解决,利用三个指针 l e f t 、 c u r 、 r i g h t left、cur、right leftcurright 将数组划分成 3 3 3 块,分别是 0 、 1 、 2 0、1、2 012 ,这样可以实现一次遍历就将颜色快速分类。

具体如下:

  1. 各个区间的含义:

    • [ 0 , l e f t ] [0, left] [0,left] :区间内的元素全是0
    • [ l e f t + 1 , c u r − 1 ] [left+1, cur-1] [left+1,cur1] :区间内的元素全是1
    • [ c u r , r i g h t − 1 ] [cur, right-1] [cur,right1] :待处理的区间
    • [ r i g h t , n − 1 ] [right, n-1] [right,n1] :区间内的元素全是2
  2. 遍历数组:

    • c u r cur cur 从左往右遍历数组。
    • 若 nums[cur] == 0 ,则 s w a p ( n u m s [ l e f t + 1 , c u r ] ) swap(nums[left+1, cur]) swap(nums[left+1,cur]) ,然后 l e f t + + , c u r + + left++,cur++ left++cur++
    • 若 nums[cur] == 1,直接 l e f t + + left++ left++ 即可,无需做额外操作。
    • 若 nums[cur] == 2,则 s w a p ( n u m s [ r i g h t − 1 , c u r ] ) swap(nums[right-1, cur]) swap(nums[right1,cur]) ,然后 r i g h t + + right++ right++ ,此时 c u r cur cur 需要保持不变,因为 r i g h t − 1 right - 1 right1 位置的元素是待处理区间的元素。
  3. c u r = = r i g h t cur == right cur==right 时,结束循环,数组被成功划分成 0 、 1 、 2 0、1、2 012 区间。

代码实现

class Solution {
public:void sortColors(vector<int>& nums) {int n = nums.size();int left = -1, right = n, cur = 0;while(cur < right){if(nums[cur] == 0) swap(nums[++left], nums[cur++]);	//将0交换到0区间的末尾else if(nums[cur] == 1) cur++;	//元素为1,直接让cur++else if(nums[cur] == 2) swap(nums[--right], nums[cur]);	//将2交换到2区间的起始的前一个位置}}
};
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

2. 排序数组(快排)

题目链接: 912. 排序数组

题目描述:

给定一个整数数组 nums,请将该数组按升序排列。

你必须在 不使用任何内置函数 的情况下解决问题,时间复杂度为 O(nlog(n)),并且空间复杂度尽可能小。

示例 1:

  • 输入:nums = [5,2,3,1]
  • 输出:[1,2,3,5]

示例 2:

  • 输入:nums = [5,1,1,2,0,0]
  • 输出:[0,0,1,1,2,5]

提示:

  • 1 <= nums.length <= 5 * 104
  • -5 * 104 <= nums[i] <= 5 * 104

解题思路

题目要求实现 O(nlog(n)) 的算法来排序数组,我们这里选择实现快排算法。

这里快排的实现主要的要点是 随机选择基准元素+三路划分

  1. 随机选择基准元素保证了快排在递归深度比较深的情况下时间复杂度依然逼近 O(nlog(n))
  2. 三路划分在处理重复元素较多时效率较高,时间复杂度不会退化。

具体的三路划分思路与75. 颜色分类类似,使用三个指针 l e f t 、 i 、 r i g h t left、i、right leftiright 来划分区间:

  • [ l , l e f t ] [l, left] [l,left] :小于 k e y key key 的区间
  • [ l e f t + 1 , i − 1 ] [left+1, i-1] [left+1,i1] 等于 k e y key key 的区间
  • [ i , r i g h t − 1 ] [i, right-1] [i,right1] 待处理的区间
  • [ r i g h t , r ] [right, r] [right,r] 大于 k e y key key 的区间

代码实现

class Solution {
public://随机选择基准元素int getRandom(vector<int>& nums, int l, int r){int randomNum = rand();return nums[randomNum % (r - l + 1) +l];}//对区间[l, r]的数组进行快排void qsort(vector<int>& nums, int l, int r){//当区间无效或者区间元素个数为1时,退出递归if(l >= r) return;//生成随机选择的keyint key = getRandom(nums, l, r);//初始化三个指针int left = l - 1, right = r + 1, cur = l;while(cur < right){if(nums[cur] < key) swap(nums[++left], nums[cur++]);else if(nums[cur] == key) cur++;else swap(nums[--right], nums[cur]);}//分别对左区间和右区间进行快排qsort(nums, l, left);qsort(nums, right, r);}vector<int> sortArray(vector<int>& nums) {//生成随机数的种子srand(time(NULL));qsort(nums, 0, nums.size() - 1);return nums;}
};
  • 时间复杂度: 平均的时间复杂度为 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))
  • 空间复杂度: O ( l o g ( n ) ) O(log(n)) O(log(n)) ,递归的空间开销,某些极端情况下会退化成 O ( n ) O(n) O(n)

3. 数组中的第K个最大元素

题目链接:215. 数组中的第K个最大元素

题目描述:

给定一个整数数组 nums 和一个整数 k,返回数组中第 k个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例1:

  • 输入:[3,2,1,5,6,4], k = 2
  • 输出:5

示例2:

  • 输入:[3,2,3,1,2,4,5,5,6], k = 4
  • 输出:4

提示:

  • 1 <= k <= nums.length <= 105
  • -104 <= nums[i] <= 104

解题思路

题目要求的第 K K K 大元素就是位置为 N − K N-K NK 元素,我们可以利用快排的思想来构造快速选择算法。

  1. 快排的一次操作(随机选择基准元素+三路划分)让数组分为了三块区间:小于 k e y key key 、等于 k e y key key 、大于 k e y key key ,根据各区间的长度确定第 K K K 大的元素处在哪个区间,从而再去目标区间寻找第 K K K 大元素。
  2. 小于 k e y key key 、等于 k e y key key 、大于 k e y key key 的区间长度分别为 a 、 b 、 c a、b、c abc
    • c ≥ K c \ge K cK ,说明第 K K K 大元素在大于 k e y key key 的区间,再去该区间递归寻找。
    • b + c ≥ K b + c \ge K b+cK ,说明第 K K K 大元素在等于 k e y key key 的区间,直接返回该区间的任意一个元素即可。
    • b + c > K b + c > K b+c>K ,说明第 K K K 大元素在小于 k e y key key 的区间,再去该区间递归寻找第 K − b − c K-b-c Kbc 大的元素。

代码实现

class Solution {
public:int GetRandomNum(vector<int>& nums, int l, int r){return nums[rand() % (r - l + 1) + l];}int quickSelect(vector<int>& nums, int l, int r, int k){if(l == r) return nums[l];//随机选择+三路划分int key = GetRandomNum(nums, l, r);int left = l - 1, right = r + 1, i = l;while(i < right){if(nums[i] < key) swap(nums[++left], nums[i++]);else if(nums[i] == key) i++;else swap(nums[--right], nums[i]);}//[l, left] [left + 1, right - 1] [right, r]//将各个区间长度与k比较从而确定目标元素在哪个区间int b = right - left - 1, c = r - right + 1;if(c >= k) return quickSelect(nums, right, r, k);else if(b + c >= k) return nums[left + 1];else return quickSelect(nums, l, left, k - b -c);}int findKthLargest(vector<int>& nums, int k) {srand(time(NULL));return quickSelect(nums, 0, nums.size() - 1, k);}
};
  • 时间复杂度: 平均为 O ( n ) O(n) O(n)
  • 空间复杂度: O ( l o g ( n ) ) O(log(n)) O(log(n)) ,递归的额外空间开销

4. 最小的 k 个数

题目链接: 剑指 Offer 40. 最小的k个数

题目描述:

设计一个算法,找出数组中最小的 k 个数。以任意顺序返回这k个数均可。

示例:

  • 输入: arr = [1,3,5,7,2,4,6,8], k = 4
  • 输出: [1,2,3,4]

提示:

  • 0 <= len(arr) <= 100000
  • 0 <= k <= min(100000, len(arr))

解题思路

这道题与上一道题类似,使用快速选择算法。

进行一次快排操作后(随机选择基准元素+三路划分),根据各个区间的长度确定前 K K K 小的元素在哪个区间,再去该区间递归寻找。

小于 k e y key key 、等于 k e y key key 、大于 k e y key key 的区间长度分别为 a 、 b 、 c a、b、c abc

  • a > k a > k a>k ,说明前 K K K 小元素在小于 k e y key key 的区间,再去该区间递归寻找。
  • a + b ≥ K a + b \ge K a+bK ,说明前 K K K 小元素刚好在小于 k e y key key 的区间或者横跨小于 k e y key key 和等于 k e y key key 的区间,我们直接返回即可。
  • a + b < K a + b < K a+b<K ,说明前 K K K 小元素在大于 k e y key key 的区间,再去该区间递归寻找前 K − a − b K - a -b Kab 小的元素。

代码实现

class Solution {
public:int getRandom(vector<int>& arr, int l, int r){return arr[rand() % (r - l + 1) + l];}//将数组arr的[l, r]范围的前k个小的元素排列到数组的[l, l + k - 1]位置,不考虑顺序void quickSelect(vector<int>& arr, int l, int r, int k){if(l >= r) return;int key = getRandom(arr, l, r);int left = l - 1, right = r + 1, i = l;while(i < right){if(arr[i] < key) swap(arr[++left], arr[i++]);else if(arr[i] == key) i++;else swap(arr[--right], arr[i]);}//[l, left] [left + 1, right - 1] [right, r]int a = left - l + 1, b = right - left - 1;if(a > k) quickSelect(arr, l, left, k);else if(a + b >= k) return;else quickSelect(arr, right, r, k - a - b);}vector<int> smallestK(vector<int>& arr, int k) {srand(time(NULL));//将数组arr前k个小的元素排列到数组的[0, k - 1]位置,不考虑顺序quickSelect(arr, 0, arr.size() - 1, k);return {arr.begin(), arr.begin() + k};}
};
  • 时间复杂度: 平均 O ( n ) O(n) O(n) ,随机选择基准值大大降低了效率退化的可能性
  • 空间复杂度: O ( l o g ( n ) ) O(log(n)) O(log(n)) ,递归的额外空间开销。

5. 排序数组(归并)

题目链接: 912. 排序数组

题目描述:

给定一个整数数组 nums,请将该数组按升序排列。

你必须在 不使用任何内置函数 的情况下解决问题,时间复杂度为 O(nlog(n)),并且空间复杂度尽可能小。

示例 1:

  • 输入:nums = [5,2,3,1]
  • 输出:[1,2,3,5]

示例 2:

  • 输入:nums = [5,1,1,2,0,0]
  • 输出:[0,0,1,1,2,5]

提示:

  • 1 <= nums.length <= 5 * 104
  • -5 * 104 <= nums[i] <= 5 * 104

解题思路

我们直接使用归并排序来排序数组即可。

归并排序的过程:

  1. 计算中点,划分区间。
  2. 递归调用归并排序的函数 m e r g e S o r t mergeSort mergeSort 对划分的左右区间进行排序。
  3. 将排序好的两个区间合并在提前开好的 t m p tmp tmp 数组,由于是合并成升序,因此小的元素先转移到 t m p tmp tmp 数组。
  4. t m p tmp tmp 数组赋值回原来的数组 n u m s nums nums 的对应位置。

因为 m e r g e S o r t mergeSort mergeSort 要递归调用,我们需要确定递归的出口,当传入的区间元素个数为 1 1 1 个时或者区间无效时,递归结束。

代码实现

class Solution {
public://合并两个有序数组需要一个临时数组vector<int> tmp;void mergeSort(vector<int>& nums, int left, int right){if(left >= right) return;//1.求中点划分区间int mid = (right - left) / 2 + left;//[left, mid] [mid + 1, right]//2.递归调用mergeSort对两个区间进行排序mergeSort(nums, left, mid);mergeSort(nums, mid + 1, right);//3.利用tmp合并两个数组,合并成升序int cur1 = left, cur2 = mid + 1, i = 0;while(cur1 <= mid && cur2 <= right)tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur1++] : nums[cur2++];while(cur1 <= mid) tmp[i++] = nums[cur1++];while(cur2 <= right) tmp[i++] = nums[cur2++];//4.将tmp数组赋值回nums的对应位置for(int j = left; j <= right; j++)nums[j] = tmp[j - left];}vector<int> sortArray(vector<int>& nums) {int n = nums.size();tmp.resize(n);mergeSort(nums, 0, n - 1);return nums;}
};
  • 时间复杂度: O ( n × l o g ( n ) ) O(n \times log(n)) O(n×log(n))
  • 空间复杂度: 实际上为 O ( n + l o g ( n ) ) O(n + log(n)) O(n+log(n)) ,但是一般忽略掉 l o g ( n ) log(n) log(n) ,所以为 O ( n ) O(n ) O(n)

6. 数组中的逆序对

题目链接: 剑指 Offer 51. 数组中的逆序对

题目描述:

在一个数组中的两个数字,如果前面的一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

示例 1:

  • 输入:record = [9, 7, 5, 4, 6]
  • 输出:8

限制:

  • 0 <= record.length <= 50000

解题思路

计算数组中的逆序对,可以将数组划分两个区间,分别计算出两个区间对应的逆序对的数量,再计算两个区间缺失的逆序对,最后将得到的数量全部加起来就能得到整个数组中的逆序对的数量,而缺失的逆序对数量主要由于前一个区间的某个元素可能大于后一个区间的某个元素,也能构成逆序对。

因此,这道题我们基于归并排序来解决。

  1. M e r g e Merge Merge 函数的作用是返回数组 r e c o r d record record [ l e f t , r i g h t ] [left, right] [left,right] 范围内的逆序对的个数,并且将数组排序好。

  2. 计算中点对数组 r e c o r d record record 进行划分区间,并对得到的区间递归调用 M e r g e Merge Merge 函数,获取了两个区间的逆序对的数量且对两个区间进行排序(假设为升序)。

  3. 合并两个区间并且统计缺失的逆序对:

    • r e c o r d [ c u r 1 ] ≤ r e c o r d [ c u r 2 ] record[cur1] \le record[cur2] record[cur1]record[cur2] ,不更新逆序对的数量,将 r e c o r d [ c u r 1 ] record[cur1] record[cur1] 赋值给 t m p tmp tmp 数组,然后 c u r 1 + + cur1++ cur1++
    • r e c o r d [ c u r 1 ] > r e c o r d [ c u r 2 ] record[cur1] > record[cur2] record[cur1]>record[cur2] ,由于两个区间已经是升序,区间 [ c u r 1 , m i d ] [cur1, mid] [cur1,mid] 内的元素都大于 r e c o r d [ c u r 2 ] record[cur2] record[cur2] ,更新逆序对的数量,并且将 r e c o r d [ c u r 2 ] record[cur2] record[cur2] 赋值给 t m p tmp tmp 数组,然后 c u r 2 + + cur2++ cur2++
  4. t m p tmp tmp 赋值回原数组对应的位置,最后返回逆序对的数量。

代码实现

class Solution {
public://用于合并两个数组所用到的临时数组int tmp[50005];//返回数组record的[left, right]范围内的逆序对的个数,并且将数组排序好int Merge(vector<int>& record, int left, int right){if(left >= right) return 0;//记录逆序对的数量int ret = 0;//1.计算中点划分区间int mid = (right - left) / 2 + left;//[left, mid] [mid + 1, right]//2.对两个区间排序并计算其逆序对的数量ret += Merge(record, left, mid);ret += Merge(record, mid + 1, right);//3.合并两个有序区间的元素并统计缺失逆序对的数量int cur1 = left, cur2 = mid + 1, i = 0;while(cur1 <= mid && cur2 <= right){if(record[cur1] <= record[cur2]){tmp[i++] = record[cur1++];}else{//更新逆序对的数量ret += mid - cur1 + 1;tmp[i++] = record[cur2++];}}while(cur1 <= mid) tmp[i++] = record[cur1++];while(cur2 <= right) tmp[i++] = record[cur2++];//4.将tmp数组赋值回原来的数组for(int j = left; j <= right; j++)record[j] = tmp[j - left];return ret; }int reversePairs(vector<int>& record) {return Merge(record, 0, record.size() - 1);}
};
  • 时间复杂度: O ( n × l o g ( n ) ) O(n \times log(n)) O(n×log(n))
  • 空间复杂度: O ( n ) O(n ) O(n)

7. 计算右侧小于当前元素的个数

题目链接: 315. 计算右侧小于当前元素的个数

题目描述:

给你一个整数数组 nums ,按要求返回一个新数组 counts
数组 counts 有如下性质:counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。

示例 1:

  • 输入:nums = [5,2,6,1]
  • 输出:[2,1,1,0]
  • 解释:
    • 5 的右侧有 2 个更小的元素 (21)
    • 2 的右侧仅有 1 个更小的元素 (1)
    • 6 的右侧有 1 个更小的元素 (1)
    • 1 的右侧有 0 个更小的元素

示例 2:

  • 输入:nums = [-1]
  • 输出:[0]

示例 3:

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

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104

解题思路

这道题与上一道题类似,一样基于归并排序去解决。

  1. M e r g e C o u n t MergeCount MergeCount 函数是对数组进行排序,计算每个元素右侧小于当前元素的个数并且记录在返回数组 r e t ret ret 。注意,这里排的是降序,便于快速统计右侧小于当前元素的个数

  2. 计算中点划分区间,并且对两个区间递归调用 M e r g e C o u n t MergeCount MergeCount 函数

  3. 合并两个有序区间,并且统计第一个区间的元素对应缺失的右侧小于当前元素的个数并且修改对应的 返回数组 r e t ret ret

    • n u m s [ c u r 1 ] > n u m s [ c u r 2 ] nums[cur1] > nums[cur2] nums[cur1]>nums[cur2] ,由于数组是降序,说明 [ c u r 2 , r i g h t ] [cur2, right] [cur2,right] 内的元素全部比 n u m s [ c u r 1 ] nums[cur1] nums[cur1] 小,统计该范围内的元素个数
    • n u m s [ c u r 1 ] ≤ n u m s [ c u r 2 ] nums[cur1] \le nums[cur2] nums[cur1]nums[cur2] ,无需统计,直接让 c u r 1 + + cur1++ cur1++ 即可

问题来了,如何正确去修改 r e t ret ret 中的值?

因为递归调用 MergeCount 函数,两个区间的元素已经是有序了,相较于原来未排序的数组,每个元素已经已经不是对应原来的下标了,无法得知当前元素在未排序前的下标,从而导致无法修改 r e t ret ret 数组中右侧小于当前元素的个数

解决方法是创建一个与 n u m s nums nums 数组同等大小的 i n d e x index index 数组,用于记录未排序之前每个元素对应的下标,让 i n d e x index index 数组与 n u m s nums nums 数组同时参与归并排序,实现 n u m s nums nums 数组元素与其原来的下标的绑定效果。

具体如何实现同时参与归并排序请参考代码实现。

代码实现

class Solution {
public://记录结果的数组vector<int> ret;//记录数组未排序之后每个元素的下标vector<int> index;//用于合并两个有序数组的辅助数组int tmpNums[100005];//用于合并两个"排序"后index数组的辅助数组int tmpIndex[100005];void MergeCount(vector<int>& nums, int left, int right){if(left >= right) return;//1.计算中点划分区间int mid = (right - left) / 2 + left;//[left, mid] [mid + 1, right]//2.左右区间调用MergeMergeCount(nums, left, mid);MergeCount(nums, mid + 1, right);//3.合并+修改左区间元素对应ret数组的元素值int cur1 = left, cur2 = mid + 1, i = 0;while(cur1 <= mid && cur2 <= right){//这合并后是降序,较大的元素先合并if(nums[cur1] <= nums[cur2]){tmpNums[i] = nums[cur2];tmpIndex[i] = index[cur2];i++; cur2++;}else{//通过index数组找到当前元素的未排序之前的下标ret[index[cur1]] += right - cur2 + 1;tmpNums[i] = nums[cur1];tmpIndex[i] = index[cur1];i++; cur1++;}}while(cur1 <= mid){tmpNums[i] = nums[cur1];tmpIndex[i++] = index[cur1++];}while(cur2 <= right){tmpNums[i] = nums[cur2];tmpIndex[i++] = index[cur2++];}//4.将tmpNums和tmpIndex数组恢复到nums数组和index数组for(int i = left; i <= right; ++i){nums[i] = tmpNums[i - left];index[i] = tmpIndex[i - left];}}vector<int> countSmaller(vector<int>& nums) {int n = nums.size();ret.resize(n);index.resize(n);for(int i = 0; i < n; ++i) index[i] = i;MergeCount(nums, 0, n - 1);return ret;}
};
  • 时间复杂度: O ( n × l o g ( n ) ) O(n \times log(n)) O(n×log(n))
  • 空间复杂度: O ( n ) O(n ) O(n)

8. 翻转对

题目链接: 493. 翻转对

题目描述:

给定一个数组 nums,如果 i < jnums[i] > 2 * nums[j],我们就将 (i, j) 称作一个重要翻转对。
你需要返回给定数组中的重要翻转对的数量。

示例 1:

  • 输入: [1,3,2,3,1]
  • 输出: 2

示例 2:

  • 输入: [2,4,3,5,1]
  • 输出: 3

注意:

  • 给定数组的长度不会超过50000
  • 输入数组中的所有数字都在32位整数的表示范围内。

解题思路

与上两道题的思路基本一致,还是基于归并排序去解决。

与上两道题不同的是,本道题不能与归并排序完美适配,不能在合并数组时候统计翻转对的数量,因为翻转对的要求是前一个数大于后一个数的两倍。

所以我们在对划分出来的两个区间递归调用完 Merge 函数后,就直接统计翻转对的数量,统计完后再合并数组。

这里我们采用将数组合并成降序,当然,合并成升序同样能解决。

代码实现

class Solution {
public://用于合并int tmp[50005];int Merge(vector<int>& nums, int left, int right){if(left >= right) return 0;int ret = 0;//1.求中点划分区间int mid = (right - left) / 2 + left;//[left mid] [mid + 1, right]//2.对划分出来的两个数组递归调用Mergeret += Merge(nums, left, mid);ret += Merge(nums, mid + 1, right);//3.统计翻转对的数量int cur1 = left, cur2 = mid + 1, i = 0;while(cur1 <= mid){while(cur2 <= right && nums[cur1] / 2.0 <= nums[cur2]) cur2++;if(cur2 > right) break;ret += right - cur2 + 1;cur1++;}//4.合并两个降序的数组cur1 = left, cur2 = mid + 1;while(cur1 <= mid && cur2 <= right)tmp[i++] = nums[cur1] < nums[cur2] ? nums[cur2++] : nums[cur1++];while(cur1 <= mid) tmp[i++] = nums[cur1++];while(cur2 <= right) tmp[i++] = nums[cur2++];//5.将tmp数组的每个元素恢复到原来数组的对应地方for(int j = left; j <= right; ++j)nums[j] = tmp[j - left];return ret;}int reversePairs(vector<int>& nums) {return Merge(nums, 0, nums.size() - 1);}
};
  • 时间复杂度: O ( n × l o g ( n ) ) O(n \times log(n)) O(n×log(n))
  • 空间复杂度: O ( n ) O(n ) O(n)

Have a good day😏

See you next time, guys!😁✨🎞
请添加图片描述

相关文章:

  • IEC61850 一致性测试中的 UCA 测试
  • AI大模型学习之基础数学:高斯分布-AI大模型概率统计的基石
  • `toRaw` 与 `markRaw`:Vue3 响应式系统的细粒度控制
  • ad24智能pdf输出的装配图没有四个边角那里的圆孔
  • 学习C++、QT---03(C++的输入输出、C++的基本数据类型介绍)
  • GO语言---数组
  • `teleport` 传送 API 的使用:在 Vue 3 中的最佳实践
  • ffmpeg(七):直播相关命令
  • Python列表常用操作方法
  • 爱高集团引领转型浪潮:AI与区块链驱动香港科技资本新机遇
  • GitHub Copilot快捷键
  • 【AGI】突破感知-决策边界:VLA-具身智能2.0
  • 力扣-72.编辑距离
  • Qt输入数据验证的方法
  • 在 `setup` 函数中使用 Vuex
  • XCVU47P-2FSVH2892E Xilinx Virtex UltraScale+ FPGA AMD
  • 华为OD机试_2025 B卷_判断一组不等式是否满足约束并输出最大差(Python,100分)(附详细解题思路)
  • 网站并发访问量达到1万以上需要注意哪些事项
  • 【DCS开源项目】—— Lua 如何调用 DLL、DLL 与 DCS World 的交互
  • 最具有实际意义价值的比赛项目
  • php 网站授权/网络营销活动策划
  • 网站建设公司 青岛/百度推广竞价开户
  • 怎么用vs做网站开发/seo优化培训
  • 冠县网站开发/免费做网页的网站
  • 水墨风格 网站/百度知道客服
  • 网站 个人 公司 区别/青岛网站建设与设计制作