【ONE·基础算法 || 分治·快排并归】

在这里插入图片描述

总言

  主要内容:编程题举例,理解分治的思想(主要是对快排、并归的应用)。
  
  

文章目录

  • 总言
  • 1、基本介绍
  • 2、颜色分类(medium)
    • 2.1、题解
  • 3、快速排序(medium)
    • 3.1、题解:三指针版本的快排
  • 4、快速选择算法(medium)
    • 4.1、题解
  • 5、最小的 k 个数(medium)
    • 5.1、题解
  • 6、归并排序(medium)
    • 6.1、题解:复习快排(二路归并)
  • 7、数组中的逆序对(hard)
    • 7.1、题解
  • 8、计算右侧小于当前元素的个数(hard)
    • 8.1、题解
  • 9、翻转对(hard)
    • 9.1、题解
  • Fin、共勉。

  
  
  
  
  

1、基本介绍

  分治是一种解决问题的策略,它将一个大问题分解成若干个小问题,这些小问题与原问题类型相同但规模更小,然后递归地解决这些小问题,最后将小问题的解合并,从而得到原问题的解。这种策略在许多算法中都有应用,如排序、搜索、图论问题等。

  快速排序就是基于分治策略的一种排序算法。它的基本思想是选取一个基准值,通过一趟排序将待排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。快速排序的时间复杂度为O(nlogn),是目前基于比较的内部排序里被认为是最好的方法,特别适用于大数据集。

  归并排序则是另一种基于分治策略的排序算法。它的主要思路是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的,然后再把有序子序列合并为整体有序序列。归并排序的过程可以分为分解和合并两个步骤,分解是将序列每次折半划分,合并则是将划分后的序列段两两合并后排序。这种排序算法是稳定的,适用于各种规模的数据集。
  
  
  
  

2、颜色分类(medium)

  题源:链接

在这里插入图片描述

  

2.1、题解

  1)、思路分析
  此题的思路本质可划分为双指针(双指针复习链接:移动零。),但对此进行了一点变动,使用的是三指针:一个用于遍历数组的指针+两个用于划分区域的指针

在这里插入图片描述
  PS:上述指针如何取名完全可随意,不必生搬硬套。
  

  如何判断移动?
  1、初始时: i = 0left = -1right = numsSize(若数组尾元素下标为n-1right指向尾元素下一个位置,即n
  2、i从左到右遍历过程中:
  ①、若nums[cur] == 0 ;说明 i 位置指向的元素要纳入左区域范围内。因此,交换 left + 1 与 i 位置的元素,并让 left++ (因为纳入了新元素, 0 序列的右边界应当右滑扩展),cur++ (为什么可以 ++ ?因为 left + 1 位置要么是 0 ,要么是 1,交换完毕之后,这个位置的值已经符合我们的要求,因此 cur++ );
  ②、nums[cur] == 1 ;说明这个位置应该在 left 和 cur 之间,此时⽆需交换,直接让 cur++ ,判断下⼀个元素即可;
  ③、nums[cur] == 2 ;说明这个位置的元素应该在 right - 1 的位置,因此交换 right - 1 与 cur 位置的元素,并且让 right-- (指向 2 序列的左边界),cur 不变(因为交换过来的数是没有被判断过的,因此需要在下轮循环中判断)
  
  
  2)、题解

class Solution {
public:void sortColors(vector<int>& nums) {int left = -1, right = nums.size(), i = 0;while(i < right)//right表⽰的是2序列的左边界,因此当碰到right时,说明已经将所有数据扫描完毕了{if(nums[i] == 0)swap(nums[i++],nums[++left]);else if(nums[i] == 1)i++;else //nums[i] == 2swap(nums[i],nums[--right]);//cur 不变,因为交换过来的数是没有被判断过的}}
};

  
  
  
  
  
  

3、快速排序(medium)

  题源:链接

在这里插入图片描述
  
  

3.1、题解:三指针版本的快排

  1)、思路分析
  这题的题解方法有多种,这里我们主要学习快排,后续讲以同样的题讲解归并。
  
  快排,我们在排序章节讲解过几种写法:Hoare法、挖坑法、双指针法,并且当时对key值的优化使用的是三数取中。这里我们讲解三指针法,以及随机数法选取key值。
  
  三指针法: 这里三指针的原理同上述颜色分类。
  从待排序的数组中选择一个元素作为基准值(key),以该基准值进行排序,将数组划分为左、中 、右三部分(以升序为例)。
在这里插入图片描述
  三指针排序的过程在上述颜色分类讲解过,这里不做赘述。
  
  在对当前序列进行三块划分处理后,再去递归排序左边部分和右边部分即可(可以舍去大量的中间部分,减少不必要的交换操作。 在处理有大量重复元素的数组时,效率会大大提升)。
在这里插入图片描述
  
  
  随机数法取key值: 在主函数中种一颗随机数种子, 由于我们要随机产生一个满足[left,right]区间范围内的基准元素值,因此可以将生成随机数转换成为生成当前[left,right]区间的随机下标。如何做到?

(rand() % (right -left +1 )) + left

  解释:这里我们设区间大小为n,让随机数 % 上区间大小,可得 [0,n-1]范围内的随机数,加上区间的左边界,即可得到 [left, left + (n -1)] 范围内的值,即[left, right]区间范围内的值。
  
  
  
  2)、题解

class Solution {
public:void quick(vector<int>& nums, int left, int right){if(left >= right) return; //递归结束条件//随机数法选取key值int n = right - left + 1;//当前段区间内元素总数int index = (rand() % n) + left;//获取到[left, right]区间段内的随机下标int key = nums[index];//这里不能直接用key的下标进行排序(即与nums[index]比较是错误的)。//因为快排属于交换排序,index下标位置的值会在排序过程中被交换。//三指针法进行区间排序:[left,right]int i = left, l = left - 1, r = right + 1;while(i < r){if(nums[i] < key) swap(nums[++l],nums[i++]);else if(nums[i] == key) i++;else swap(nums[--r],nums[i]);//交换过来的数是没有被判断过,此处i不能++}//对左右排序:[left, l] [l+1, r-1] [r,right]quick(nums,left,l);quick(nums,r,right);}vector<int> sortArray(vector<int>& nums) {srand(time(nullptr));// 种下⼀个随机数种⼦quick(nums,0,nums.size()-1);//使用递归法排序,这里传入[left,right]区间return nums;}
};

  
  
  
  
  
  

4、快速选择算法(medium)

  题源:链接

在这里插入图片描述

  

4.1、题解

  1)、思路分析
  这题若使用的是选择排序,比如直接选择排序或堆排序(堆排时间复杂度为 N l o g N NlogN NlogN),也可以解决此题:

class Solution {
public:int findKthLargest(vector<int>& nums, int k) {//建堆priority_queue<int> maxHeap(nums.begin(),nums.end());//取前K-1个数while(--k)//此写法下要用前置自减{maxHeap.pop();}//取第K个数return maxHeap.top();}
};

  
  
  这里我们主要学习应用快排:

在这里插入图片描述

  
  

  2)、题解
  使用升序的写法:

class Solution {
public:int qsort(vector<int>& nums, int k, int left, int right) {if (left >= right) // 递归调用return nums[left];int n = right - left + 1;   // 当前区间的元素总数int id = rand() % n + left; // 获取当前区间段的随机下标int key = nums[id]; // 获取该下标上的元素值(作为key值使用)int i = left, l = left - 1, r = right + 1;while (i < r) // 排升序,找第k大。{if (nums[i] < key)swap(nums[i++], nums[++l]);else if (nums[i] == key)i++;elseswap(nums[i], nums[--r]);}// 用于判断下一轮递归区间:[left,l]  [l+1,r-1]  [r, right]int a = l - left + 1;int b = r - 1 - (l + 1) + 1;int c = right - r + 1;if (c >= k)return qsort(nums, k, r, right);else if (b + c >= k)return key;elsereturn qsort(nums, k - b - c, left, l);}int findKthLargest(vector<int>& nums, int k) {srand(time(nullptr)); // 种下随机种子return qsort(nums, k, 0, nums.size() - 1); // 传入的区间边界应该是闭区间[left,right]}
};

  使用降序的写法:只是对当前序列的排序部分及判断下一轮递归的条件做了变动。

        while (i < r) // 排降序,找第k大。{if (nums[i] < key)swap(nums[i], nums[--r]);else if (nums[i] == key)i++;elseswap(nums[i++], nums[++l]);}// 用于判断下一轮递归区间:[left,l]  [l+1,r-1]  [r, right]int a = l - left + 1;int b = r - 1 - (l + 1) + 1;int c = right - r + 1;if (a >= k)return qsort(nums, k, left, l);else if (a + b >= k)return key;elsereturn qsort(nums, k - b - a, r, right);

  
  
  
  
  

5、最小的 k 个数(medium)

  题源:链接

在这里插入图片描述
  
  

5.1、题解

  1)、思路分析
  此题解法多种,可用堆的TOP-K( N l o g K NlogK NlogK)、可用排序(如快排: N l o g N NlogN NlogN)、还可以用快速选择算法(即上述题4讲解的原理。)
  这里主要演示快速选择算法。
在这里插入图片描述
  
  
  2)、题解
  以升序为例: 最后我们返回时,前K个元素区间虽然没有完全有序,但已经满足了找出题目最小的K个数的要求。

class Solution {
public:void qsort(vector<int>& arr, int left, int right, int k){if(left >= right) return;int n = right - left + 1;int index = rand() % n + left;int key = arr[index];//三指针排序: [left,right],排升序int L = left - 1 , R = right + 1, i = left;while(i < R){if(arr[i] < key) swap(arr[++L], arr[i++]);else if(arr[i] == key) ++i;else swap(arr[--R], arr[i]);}//对左右区间:[left, L] [L+1, R-1] [R,right]int a = L - left + 1;int b = R-1 - (L+1) + 1;int c = right - R + 1;if(a >= k)  qsort(arr, left, L, k);else if(a + b >= k) return;else qsort(arr, R, right, k-a-b);}vector<int> smallestK(vector<int>& arr, int k) {srand(time(nullptr));qsort(arr,0,arr.size()-1,k);return {arr.begin(),arr.begin()+k};}
};

  
  
  
  
  
  
  
  

6、归并排序(medium)

  题源:链接

在这里插入图片描述

  

6.1、题解:复习快排(二路归并)

  1)、思路分析
  在上述,我们用快排解过此题,这里我们使用归并排序来解决。PS:归并排序相关复习见博文:常见排序。
  这里主要是指二路归并。 总体思想不变,细节实现看个人写法:
  1、分割: 将待排序的数组从中间分割成两个子数组,直到子数组的大小为1(即每个子数组只包含一个元素,自然是有序的)。
  2、递归排序: 递归地对子数组进行归并排序(「左半部分排序」 + 「右半部分排序」)。
  3、合并: 将两个已排序的子数组合并成一个有序的大数组。(通常借助一个辅助数组,可以是每次归并时创建临时变量,也可以用全局变量或者在堆区开辟)
  
  
  2)、题解

class Solution {
public:vector<int> temp;void MergeSort(vector<int>& nums, int left, int right){if(left >= right) return;//这里使用二路归并,选择中间点将区间划分为两段:[left,mid][mid+1,right]int mid = ( right + left ) / 2;//题中给出数据不大(也可以使用防越界版本的求中间值)//先对左右区间排序MergeSort(nums,left,mid);MergeSort(nums,mid+1,right);//再合并两个有序数组(来到此处,意味着当前左右两段区间各自有序)int cur1 = left; int cur2 = mid+1;int i = 0;while(cur1 <= mid && cur2 <= right)temp[i++] =  nums[cur1] < nums[cur2] ? nums[cur1++] : nums[cur2++];//处理剩余区间while(cur1 <= mid) temp[i++] = nums[cur1++];while(cur2 <= right) temp[i++] = nums[cur2++];//将排序好的元素重新返回原数组中(可借助memcpy等函数)for(int j = left; j <= right; ++j){nums[j] = temp[j - left];}}vector<int> sortArray(vector<int>& nums) {temp.resize(nums.size());//归并需要借助一个辅助数组,这里可以每次归并时定义,也可以直接搞一个全局变量(或者堆上申请)MergeSort(nums,0,nums.size()-1);return nums;}
};

  

  题外话:
  理论上也可以实现k路归并排序(k-way merge sort),其中k是大于1的整数。在k路归并排序中,待排序的数组被分割成k个更小的子数组,而不是仅仅两个。然后,这k个子数组被分别排序,并最终合并成一个完整的有序数组。
  k路归并排序在某些特定场景下可能具有优势,比如当待排序的数据分布在多个不连续的内存区域时,或者当使用并行计算资源时。然而,实现k路归并排序通常比实现二路归并排序更为复杂,并且不一定总是带来性能上的提升。因此,在实际应用中,二路归并排序更为常见和流行。
  
  
  
  
  

7、数组中的逆序对(hard)

  题源:链接

在这里插入图片描述

  
  

7.1、题解

  1)、思路分析
  直接解法: 双层循环暴力枚举,看在难度hard的份上,大概率超时。
  
  问题一:为什么可以使用归并排序?
  1、题目要求找出数组中所有逆序对,既如此,将数组一分为二,先找出左区间中的逆序对,再找出右区间的逆序对,最后选出一左一右在两区间的逆序对。效果与从左到右/从右到左线性遍历查找等同。
在这里插入图片描述

  
  2、对此进行优化。在找出左区间中的逆序对后我们对其排个序,同理,在找出右区间的逆序对后也对其排序。此时,尽管左右两区间内部元素顺序发生改变,但对于在两区间中分别选取元素组成一左一右的逆序对并无影响。
  3、回顾上述历程,这不就和归并的思想一样吗?当然,在选取出一左一右的逆序对后,我们要对当前整体区间也进行排序

归并排序的过程:
• 先排序左数组;
• 再排序右数组;
• 左数组和右数组合⼆为⼀。逆序对中两个元素:
• 全部从左数组中选择逆序对
• 全部从右数组中选择逆序对
• ⼀个选左数组另⼀个选右数组,组成逆序对

  
  
  
  问题二:如何使用归并排序?及,为什么这里使用归并会提高效率(单调性)
  除了这里举例的两种(升序+固定右值找左侧;降序+固定左值找右侧),如何选取与组合看个人风格。
在这里插入图片描述
  
  
  
  
  2)、题解
  升序写法如下:

class Solution {
public:int tmp[50005]={0};int mergesort(vector<int>& record, int left, int right){if(left >= right) return 0;//取中数,分区间:[left, mid] [mid+1, right]int mid = (left+right) / 2;int sum = 0;//用于统计//左区间找逆序对+排序、右区间找逆序对+排序sum += mergesort(record,left,mid)+ mergesort(record,mid+1,right);//一左一右找逆序对+排序int cur1 = left, cur2 = mid + 1;int i = 0;while(cur1 <= mid && cur2 <= right)//排升序,找左数比右数大{if(record[cur1] <= record[cur2])tmp[i++] = record[cur1++];else//record[cur1] > record[cur2]{sum += mid - cur1 + 1;tmp[i++] = record[cur2++];}}//排序:处理剩余元素while(cur1 <= mid) tmp[i++] = record[cur1++];while(cur2 <= right) tmp[i++] = record[cur2++];//将有序数列返回原数列中for(int j = left; j <= right; ++j)record[j]=tmp[j-left];//返回return sum;}int reversePairs(vector<int>& record) {return mergesort(record,0,record.size()-1);}
};

  降序写法总体不变,只需稍加改动即可:

        while(cur1 <= mid && cur2 <= right)//排降序,找左数比右数大{if(record[cur1] <= record[cur2])tmp[i++] = record[cur2++];else//record[cur1] > record[cur2]{sum += right - cur2 + 1;tmp[i++] = record[cur1++];}}

  
  
  
  
  
  

8、计算右侧小于当前元素的个数(hard)

  题源:链接

在这里插入图片描述
  
  

8.1、题解

  1)、思路分析
  有了上一题的铺垫,这一道题的解法与之类似,但是这一道题要求的不是求总的个数,而是要返回一个数组,记录每一个元素的右边有多少个元素比自己小
在这里插入图片描述

  这里存在一个问题,在归并排序的过程中,元素的下标是会跟着变化的。 当前我们查找出的顺序与需要返回的顺序不一定完全匹配。
在这里插入图片描述
  因此,为了确保返回时数组输出的元素顺序,我们需要一个额外数组 index,将数组元素和对应的下标绑定在一起归并,也就是在归并nums中元素的时候,顺势将其下标index也转移到对应的位置上。(意味着辅助数组也需要两个,分别用于排序归并后的nums、index。)
  
  
  
  2)、题解
  这是vector<int> indexvector<int> count使用全局变量的版本。

class Solution {
public:int tmp_n[100005];//辅助数组1:归并时用于排序numsint tmp_i[100005];//辅助数组2:归并时用于排序indexvector<int> index;//记录元素对应下标vector<int> count;//用于返回输出结果void mergesort(vector<int>& nums, int left, int right){if(left>=right) return;//求中值划分区间:[left,mid] [mid+1,right]int mid = (left + right) /2;//先对左右区间进行找数+归并排序:mergesort(nums, left, mid);mergesort(nums,mid+1, right);//再对当前整段区间进行找数+排序:int cur1 = left, cur2 = mid+1;int i = 0;while(cur1<=mid && cur2 <= right){if(nums[cur1] <= nums[cur2]){tmp_n[i] = nums[cur2];tmp_i[i++] = index[cur2++];//注意这里一次处理两个数组}else//nums[cur1] > nums[cur2]{count[index[cur1]] += right - cur2 + 1;tmp_n[i] = nums[cur1];tmp_i[i++] = index[cur1++];}}//处理剩余区间while(cur1 <= mid){tmp_n[i]=nums[cur1];tmp_i[i++]=index[cur1++];}while(cur2 <= right){tmp_n[i]=nums[cur2];tmp_i[i++]=index[cur2++];}//将获取到的有序数据写回原数组中for(int j = left; j <= right; ++j){nums[j]=tmp_n[j-left];index[j]=tmp_i[j-left];}}vector<int> countSmaller(vector<int>& nums) {int n = nums.size();index.resize(n);//重新扩容count.resize(n);for(int i = 0; i < n; ++i)//初始化下标{index[i] = i;}mergesort(nums, 0, n-1);return count;}
};

  
  如果不使用全局变量,也可如下,但需要传参时多增几个参数。总之写法不一。

class Solution {
public:int tmp_n[100005];int tmp_i[100005];void mergesort(vector<int>& nums, int left, int right, vector<int>& index, vector<int>& count){if(left >= right) return;//[left,mid] [mid+1,right]int mid = (left + right) /2;//左右区间:mergesort(nums, left, mid, index,count);mergesort(nums,mid+1, right, index,count);//当前整个区间:int cur1 = left, cur2 = mid+1;int i = 0;while(cur1<=mid && cur2 <= right){if(nums[cur1] <= nums[cur2]){tmp_n[i] = nums[cur2];tmp_i[i++] = index[cur2++];}else//nums[cur1] > nums[cur2]{count[index[cur1]] += right - cur2 + 1;tmp_n[i] = nums[cur1];tmp_i[i++] = index[cur1++];}}//处理剩余区间while(cur1 <= mid){tmp_n[i]=nums[cur1];tmp_i[i++]=index[cur1++];}while(cur2 <= right){tmp_n[i]=nums[cur2];tmp_i[i++]=index[cur2++];}//写回原数组中for(int j = left; j <= right; ++j){nums[j]=tmp_n[j-left];index[j]=tmp_i[j-left];}}vector<int> countSmaller(vector<int>& nums) {int n = nums.size();vector<int> index(n);vector<int> count(n);for(int i = 0; i < n; ++i){index[i] = i;}mergesort(nums, 0, n-1, index, count);return count;}
};

  
  关于count[index[cur1]] += right - cur2 + 1; 为什么要用+=,而不能使用+
  要知道归并的过程,元素位置是变化的。 使用 += 是因为我们需要累计 nums[cur1] 右侧小于它的元素数量。在归并排序的合并过程中,cur1 会遍历左子数组的所有元素,每次遇到一个比右子数组当前元素大的左子数组元素时,我们都需要将右子数组中剩余的元素数量(即 right - cur2 + 1)累加到对应的 count 中。因此,这是一个累加操作,每次都需要将新的数量加到之前累计的数量上。

  如果使用 + 而不是 +=,那么每次只会将 right - cur2 + 1 赋值给 count[index[cur1]],而不会保留之前累计的数量。这会导致 count 数组中每个位置只存储了最后一次计算得到的数量,而不是所有累计的数量,从而得到错误的结果。

在这里插入图片描述

  
  
  
  
  
  

9、翻转对(hard)

  题源:链接

在这里插入图片描述

  
  

9.1、题解

  1)、思路分析
  翻转对和逆序对的定义大同小异,逆序对是前面的数要大于后面的数。而翻转对是前面的一个数要大于后面某个数的两倍。因此,我们依旧可以用归并排序的思想来解决这个问题。
  
  而与上个问题不同的是,上一道题我们可以一边合并一遍计算小于当前元素右侧元素,但是这道题要求的是左边元素大于右边元素的两倍,若直接合并的话,是无法快速计算出翻转对的数量的。
  因此,在归并排序之前,我们可以借助左右区间已经排序好的单调性,先完成对翻转对的统计。由于两个指针都是不回退的的扫描到数组的结尾,因此两个有序序列求出翻转对的时间复杂度是 O ( N ) O(N) O(N)

在这里插入图片描述

  
  2)、题解
  以升序为例:

class Solution {
public:int tmp[50005] = {0};int mergesort(vector<int>& nums, int left, int right) {if (left >= right)return 0;//[left,mid] [mid+1, right]int mid = (left + right) / 2;int sum = 0;sum += mergesort(nums, left, mid) + mergesort(nums, mid + 1, right);// 统计:(单调性为升序)int cur1 = left, cur2 = mid + 1;while(cur1 <= mid && cur2 <= right){if(nums[cur1]/2.0 > nums[cur2]){sum += mid - cur1 + 1;cur2++;}else cur1++;}// while (cur2 <= right) // 统计部分其它写法:升序的情况// {//     while (cur1 <= mid && nums[cur2] >= nums[cur1] / 2.0)//         cur1++;//     if (cur1 > mid)//         break;//     sum += mid - cur1 + 1;//     cur2++;// }// 排序:升序版,[left, mid][mid+1, right]cur1 = left, cur2 = mid + 1;int i = left;while (cur1 <= mid && cur2 <= right) {if (nums[cur1] > nums[cur2])tmp[i++] = nums[cur2++];elsetmp[i++] = nums[cur1++];}while (cur1 <= mid)tmp[i++] = nums[cur1++];while (cur2 <= right)tmp[i++] = nums[cur2++];//写回for (int j = left; j <= right; ++j) {nums[j] = tmp[j];}return sum;}int reversePairs(vector<int>& nums) {return mergesort(nums, 0, nums.size()-1);}
};

  
  
  
  
  以降序为例:

class Solution {
public:int tmp[50005] = {0};int mergesort(vector<int>& nums, int left, int right) {if (left >= right)return 0;//[left,mid] [mid+1, right]int mid = (left + right) / 2;int sum = 0;sum += mergesort(nums, left, mid) + mergesort(nums, mid + 1, right);// 统计:(单调性为降序)int cur1 = left, cur2 = mid + 1;while (cur1 <= mid && cur2 <= right) {if (nums[cur1] / 2.0 > nums[cur2]) {sum += right - cur2 + 1;cur1++;} elsecur2++;}// while (cur1 <= mid) // 统计部分其它写法:降序的情况// {//     while (cur2 <= right && nums[cur2] >= nums[cur1] / 2.0)//         cur2++;//     if (cur2 > right)//         break;//     ret += right - cur2 + 1;//     cur1++;// }// 排序:降序版,[left, mid][mid+1, right]cur1 = left, cur2 = mid + 1;int i = left;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++];// 写回for (int j = left; j <= right; ++j) {nums[j] = tmp[j];}return sum;}int reversePairs(vector<int>& nums) {return mergesort(nums, 0, nums.size() - 1);}
};

  
  
  
  
  
  
  
  

Fin、共勉。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024年最新阿里云服务器价格表2核2G、2核4G、4核8G、8核16G等配置报价

2024年阿里云服务器优惠价格表&#xff0c;一张表整理阿里云服务器最新报价&#xff0c;阿里云服务器网aliyunfuwuqi.com整理云服务器ECS和轻量应用服务器详细CPU内存、公网带宽和系统盘详细配置报价单&#xff0c;大家也可以直接移步到阿里云CLUB中心查看 aliyun.club 当前最新…

IntelliJ IDEA中遇到的“cannot access java.lang.String“错误及其解决方案(day8)

intelliJ 今天遇到使用intelliJ遇到了一个新错误&#xff0c;有问题就解决问题是一个程序员最基本的修养&#xff0c;如下&#xff1a; 在上面的代码中&#xff0c;我使用了this.这个关键字&#xff0c;发现出现了以上问题&#xff0c;找了一些资料&#xff0c;不是很明白&am…

【二叉树】Leetcode 98. 验证二叉搜索树【中等】

验证二叉搜索树 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 示例1&a…

Unity照片墙简易圆形交互效果总结

还要很多可以优化的点地方&#xff0c;有兴趣的可以做 比如对象的销毁和生成可以做成对象池&#xff0c;走到最左边后再移动到最右边循环利用 分析过程文件&#xff0c;采用Blender&#xff0c;资源已上传&#xff0c;可以播放动画看效果&#xff0c;下面截个图&#xff1a; …

Redis开源协议变更!Garnet:微软开源代替方案?

Garnet&#xff1a;微软开源的高性能替代方案&#xff0c;秉承兼容 RESP 协议的同时&#xff0c;以卓越性能和无缝迁移能力重新定义分布式缓存存储&#xff01; - 精选真开源&#xff0c;释放新价值。 概览 最近&#xff0c;Redis修改了开源协议&#xff0c;从BSD变成了 SSPLv…

主流公链 - Filecoin

探索Filecoin&#xff1a;去中心化存储网络 1. Filecoin简介 Filecoin是一个去中心化的存储网络&#xff0c;旨在通过区块链技术实现全球性的分布式文件存储和检索市场。Filecoin允许用户将文件存储在网络中的节点上&#xff0c;并通过加密、分片和复制等技术保证数据的安全性…

八大技术趋势案例(虚拟现实增强现实)

科技巨变,未来已来,八大技术趋势引领数字化时代。信息技术的迅猛发展,深刻改变了我们的生活、工作和生产方式。人工智能、物联网、云计算、大数据、虚拟现实、增强现实、区块链、量子计算等新兴技术在各行各业得到广泛应用,为各个领域带来了新的活力和变革。 为了更好地了解…

【数学符合】

数学符合 ■ ∑ ■ ∑

Javascript — 异步方法与异常捕获问题

问题&#xff1a;try catch为什么不能捕获未被 await 修饰的异步方法异常 async function throwError() {throw new Error("自定义Error&#xff01;") }let testOne async () > {console.debug("start testOne")try {throwError()} catch (e) {conso…

HSF:阿里RPC框架

简介 HSF&#xff08;High-speed Service Framework&#xff09;是一种广泛应用于阿里巴巴内部的分布式RPC服务框架。 作为阿里巴巴的基础中间件&#xff0c;HSF连接了不同的业务系统&#xff0c;解耦了系统间的实现依赖。HSF从分布式应用的角度统一了服务的发布和调用方式&a…

智慧工地安全生产与风险预警大平台的构建,需要哪些技术?

随着科技的不断发展&#xff0c;智慧工地已成为现代建筑行业的重要发展趋势。智慧工地方案是一种基于先进信息技术的工程管理模式&#xff0c;旨在提高施工效率、降低施工成本、保障施工安全、提升施工质量。一般来说&#xff0c;智慧工地方案的构建&#xff0c;需要通过集成物…

Rust编程(五)终章:查漏补缺

闭包 & 迭代器 闭包&#xff08;Closure&#xff09;通常是指词法闭包&#xff0c;是一个持有外部环境变量的函数。外部环境是指闭包定义时所在的词法作用域。外部环境变量&#xff0c;在函数式编程范式中也被称为自由变量&#xff0c;是指并不是在闭包内定义的变量。将自…