【算法一周目】分而治之,归并如风:算法中的美学与哲理
文章目录
- 1. 颜色分类
- 2. 排序数组(快排)
- 3. 数组中的第K个最大元素
- 4. 最小的 k 个数
- 5. 排序数组(归并)
- 6. 数组中的逆序对
- 7. 计算右侧小于当前元素的个数
- 8. 翻转对
1. 颜色分类
题目链接: 75. 颜色分类
题目描述:
给定一个包含红色、白色和蓝色的数组 nums
,共 n
个元素,要求对它们进行原地排序,使得相同颜色的元素相邻,并按红色、白色、蓝色的顺序排列。这里使用整数 0
、1
和 2
分别表示红色、白色和蓝色,且不能使用库的排序函数。
示例 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 left、cur、right 将数组划分成 3 3 3 块,分别是 0 、 1 、 2 0、1、2 0、1、2 ,这样可以实现一次遍历就将颜色快速分类。
具体如下:
-
各个区间的含义:
- [ 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,cur−1] :区间内的元素全是1
- [ c u r , r i g h t − 1 ] [cur, right-1] [cur,right−1] :待处理的区间
- [ r i g h t , n − 1 ] [right, n-1] [right,n−1] :区间内的元素全是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[right−1,cur]) ,然后 r i g h t + + right++ right++ ,此时 c u r cur cur 需要保持不变,因为 r i g h t − 1 right - 1 right−1 位置的元素是待处理区间的元素。
-
当 c u r = = r i g h t cur == right cur==right 时,结束循环,数组被成功划分成 0 、 1 、 2 0、1、2 0、1、2 区间。
代码实现
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))
的算法来排序数组,我们这里选择实现快排算法。
这里快排的实现主要的要点是 随机选择基准元素+三路划分。
- 随机选择基准元素保证了快排在递归深度比较深的情况下时间复杂度依然逼近
O(nlog(n))
。 - 三路划分在处理重复元素较多时效率较高,时间复杂度不会退化。
具体的三路划分思路与75. 颜色分类类似,使用三个指针 l e f t 、 i 、 r i g h t left、i、right left、i、right 来划分区间:
- [ 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,i−1] 等于 k e y key key 的区间
- [ i , r i g h t − 1 ] [i, right-1] [i,right−1] 待处理的区间
- [ 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 N−K 元素,我们可以利用快排的思想来构造快速选择算法。
- 快排的一次操作(随机选择基准元素+三路划分)让数组分为了三块区间:小于 k e y key key 、等于 k e y key key 、大于 k e y key key ,根据各区间的长度确定第 K K K 大的元素处在哪个区间,从而再去目标区间寻找第 K K K 大元素。
- 小于 k e y key key 、等于 k e y key key 、大于 k e y key key 的区间长度分别为 a 、 b 、 c a、b、c a、b、c 。
- 若 c ≥ K c \ge K c≥K ,说明第 K K K 大元素在大于 k e y key key 的区间,再去该区间递归寻找。
- 若 b + c ≥ K b + c \ge K b+c≥K ,说明第 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 K−b−c 大的元素。
代码实现
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 a、b、c :
- 若 a > k a > k a>k ,说明前 K K K 小元素在小于 k e y key key 的区间,再去该区间递归寻找。
- 若 a + b ≥ K a + b \ge K a+b≥K ,说明前 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 K−a−b 小的元素。
代码实现
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
解题思路
我们直接使用归并排序来排序数组即可。
归并排序的过程:
- 计算中点,划分区间。
- 递归调用归并排序的函数 m e r g e S o r t mergeSort mergeSort 对划分的左右区间进行排序。
- 将排序好的两个区间合并在提前开好的 t m p tmp tmp 数组,由于是合并成升序,因此小的元素先转移到 t m p tmp tmp 数组。
- 将 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
解题思路
计算数组中的逆序对,可以将数组划分两个区间,分别计算出两个区间对应的逆序对的数量,再计算两个区间缺失的逆序对,最后将得到的数量全部加起来就能得到整个数组中的逆序对的数量,而缺失的逆序对数量主要由于前一个区间的某个元素可能大于后一个区间的某个元素,也能构成逆序对。
因此,这道题我们基于归并排序来解决。
-
设 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] 范围内的逆序对的个数,并且将数组排序好。
-
计算中点对数组 r e c o r d record record 进行划分区间,并对得到的区间递归调用 M e r g e Merge Merge 函数,获取了两个区间的逆序对的数量且对两个区间进行排序(假设为升序)。
-
合并两个区间并且统计缺失的逆序对:
- 若 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++ 。
-
将 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
个更小的元素 (2
和1
)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
解题思路
这道题与上一道题类似,一样基于归并排序去解决。
-
M e r g e C o u n t MergeCount MergeCount 函数是对数组进行排序,计算每个元素右侧小于当前元素的个数并且记录在返回数组 r e t ret ret 。注意,这里排的是降序,便于快速统计右侧小于当前元素的个数
-
计算中点划分区间,并且对两个区间递归调用 M e r g e C o u n t MergeCount MergeCount 函数
-
合并两个有序区间,并且统计第一个区间的元素对应缺失的右侧小于当前元素的个数并且修改对应的 返回数组 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 < j
且 nums[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!😁✨🎞