数据结构:快速排序,归并排序,计数排序的实现分析

✨✨小新课堂开课了,欢迎欢迎~✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:数据结构与算法

小新的主页:编程版小新-CSDN博客

1.快排

1.1算法思想

选择一个基准元素,将数组分为两部分,一部分小于基准元素,一部分大于基准元素。通过不断对划分的子序列进行同样的操作,递归地对小于基准元素和大于基准元素的两个子区间进行排序,从而实现整个数组的排序。

1.2算法实现

下面我们介绍三种方法

1.2.1 Hoare法

具体步骤:

1.先选择一个基准值keyi,我们一般把最左边的元素作为基准值

2.定义两个变量L和R,R先从右往左遍历找比keyi小的值,L从左往右找比keyi大的值,交换找到的两个值,并重复上面的过程,直到L和R相遇,相遇位置的值与keyi交换,以相遇位置mid为基点划分区间。

3.对[left  mid-1]和[mid+1 right]这两个区间进行递归排序。left和right指原数组的其实位置和末尾。

动图演示:

这里是单趟的思想。

小新:这个算法保证了相遇位置一定比keyi小?思考一下这是为什么呢?

先说结论:左边作keyi,让右边向先走, 可以保证相遇位置的值一定比keyi小,相反,右边作keyi,让左边先走,可以保证相遇位置的值一定比keyi大。

相遇有两种情况:1.R遇L   2.L遇R

1.R在没遇见L的前一步,因为上一次交换的原因,此时L下标对应的值是比keyi下标对应的值要小的,然后R遇见L,保证了相遇位置一定比keyi下标对应的值要小。

2.R是从右往左找小,也就是说明了R下标对应的值比keyi下标对应的值要小,L遇见R,保证了相遇位置的值一定比keyi下标对应的值要小。

代码实现: 

void Swap(int*a,int*b)
{int tmp = *a;*a = *b;*b = tmp;
}void QuickSort(int* a, int left, int right)
{//当只有一个数据或者区间不存在的时候,返回if (left >= right)return;int keyi = left;int begin = left;int end = right;while (begin < end){//右边找小while (begin < end && a[end] >= a[keyi]){end--;}//左边找大while (begin < end && a[begin] <= a[keyi]){begin++;}Swap(&a[begin], &a[end]);}//相遇位置与key交换Swap(&a[keyi], &a[begin]);int mid = begin;//[left mid-1] mid [mid+1  right]QuickSort(a, left, mid - 1);QuickSort(a, mid + 1, right);
}

1.2.2挖坑法

具体步骤:

1.将keyi位置的值记录起来,形成一个坑位。

2.R从右往左找比keyi下标对应的值小的数,找到就将该值放在坑位处,此位置形成新的坑,L从左往右找比keyi下标位置对应的值大的树,找到就把该值放在坑位处,此位置形成新的坑,重复以上过程。

3.R与L相遇,把keyi位置的值放在相遇位置处,相遇位置一定是坑,因为在交换过程中R或L一定有一个是坑。以相遇位置hole为基点,划分区间。

4.对[left  hole-1]和[hole+1 right]这两个区间进行递归排序。left和right指原数组的其实位置和末尾。

动图演示:

代码实现:

void Swap(int*a,int*b)
{int tmp = *a;*a = *b;*b = tmp;
}int PartSort2(int* a, int left, int right)
{int begin = left;int end = right;int hole = begin;//起始坑位int keyi = a[left];while (begin < end){//右边找小while (begin < end && a[end] >= keyi){end--;}//找到就交换Swap(&a[hole], &a[end]);//记录新的坑位hole = end;while (begin < end && a[begin] <= keyi){begin++;}Swap(&a[hole], &a[begin]);hole = begin;}//将keyi位置的值,放进相遇位置(这个相遇位置也是一个坑)a[hole] = a[left];return hole;//[left hole-1] hole [hole+1  right]
}void QuickSort(int* a, int left, int right)
{if (left >= right){return;}int hole = PartSort2(a, left, right);QuickSort(a, left, hole - 1);QuickSort(a, hole + 1, right);}

1.2.3前后指针法 

具体步骤:

1.我们选最左边的值作为基准值keyi。

2.定义两个指针变量prev和cur,初始时,prev指向数组的起始位置,cur指向prev的下一个位置。cur从左往右找比keyi小的值,找的话prev++,交换prev和cur指向的值,重复此过程。直到cur越界。

3.交换prev指向的值与keyi,以此时prev的位置mid为基点,划分区间。

4.对[left mid -1]和[mid+1 right]这两个区间进行递归排序。left和right指原数组的其实位置和末尾。

动图演示:

代码实现 :

void Swap(int*a,int*b)
{int tmp = *a;*a = *b;*b = tmp;
}int PartSort3(int* a, int left, int right)
{int mid = GetMid(a, left, right);Swap(&a[left], &a[mid]);int prev = left;int cur = prev + 1;int keyi = left;while (cur <= right){if (a[cur] < a[keyi] && ++prev != cur){Swap(&a[prev], &a[cur]);}cur++;}int midd = prev;Swap(&a[keyi], &a[midd]);return midd;
}void QuickSort(int* a, int left, int right)
{if (left >= right){return;}int keyi = PartSort3(a, left, right);QuickSort(a, left, keyi - 1);QuickSort(a, keyi + 1, right);}

1.3复杂度分析 

时间复杂度:一般需要递归logN层,并且每层都需要遍历,因此时间复杂度为O(NlogN)。

空间复杂度:一般需要递归logN层,并且每层都需要遍历,因此时间复杂度为O(NlogN)。

1.4算法优化 

我们考虑一下一个比较特殊的情况,假设该数组有序的情况下,该算法就会退化成一个O(N^2)的算法。

为了避免这种情况,我们可以随机选keyi,保证选到的keyi既不是最大值也不是最小值。即将选到的值与最左边的值交换,选到的值作为我们的基准值。

随机选keyi:

//三数取中(中代表的是left mid right代表的三个数中既不是最大也不是最小的那个数)
int GetMid(int* a, int left, int right)
{int mid = (left + right) / 2;//left  mid  rightif (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] > a[right])//a[mid]>a[right]{return left;}else{return right;}}else//a[left]>a[mid]{if (a[mid] > a[right]){return mid;}else if (a[left] > a[right])//a[mid]<a[right]{return right;}else{return left;}}
}int PartSort1(int* a, int left, int right)
{//避免了有序情况下的栈溢出int mid = GetMid(a, left, right);Swap(&a[left], &a[mid]);int keyi = left;int begin = left;int end = right;while (begin < end){//右边找小while (begin < end && a[end] >= a[keyi]){end--;}//左边找大while (begin < end && a[begin] <= a[keyi]){begin++;}Swap(&a[begin], &a[end]);}int midd = begin;//相遇位置与key交换Swap(&a[keyi], &a[midd]);return midd;}void QuickSort(int* a, int left, int right)
{if (left >= right){return;}int keyi = PartSort1(a, left, right);//单趟排序QuickSort(a, left, keyi - 1);QuickSort(a, keyi + 1, right);}

如果让个数少的一组数有序,采用递归去实现的代价比较大。为了避免递归的深度太深,我们可以对其进行小区间优化 ,当该组的元素较少时,我们可以直接使用其他的排序,比如插入排序使其有序。

如何理解递归的深度太深呢?

我们在使用快排的时候,递归就类似于二叉树的前序遍历,确定"根"(基点),"左子树"(左区间),"右子树"(右区间)。

前面我们学习过二叉树的知识,我们知道越往下递归的次数就越多。最后一层大约占总调用次数的50%,倒数第二层占总调用次数的25%,倒数第三层占总调用次数的12.5%,也就是说后三层占了总调用次数的87.5%,可想而知小区间优化存在的极大意义。

小区间优化:

//三数取中(中代表的是left mid right代表的三个数中既不是最大也不是最小的那个数)
int GetMid(int* a, int left, int right)
{int mid = (left + right) / 2;//left  mid  rightif (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] > a[right])//a[mid]>a[right]{return left;}else{return right;}}else//a[left]>a[mid]{if (a[mid] > a[right]){return mid;}else if (a[left] > a[right])//a[mid]<a[right]{return right;}else{return left;}}
}int PartSort1(int* a, int left, int right)
{//避免了有序情况下的栈溢出int mid = GetMid(a, left, right);Swap(&a[left], &a[mid]);int keyi = left;int begin = left;int end = right;while (begin < end){//右边找小while (begin < end && a[end] >= a[keyi]){end--;}//左边找大while (begin < end && a[begin] <= a[keyi]){begin++;}Swap(&a[begin], &a[end]);}int midd = begin;//相遇位置与key交换Swap(&a[keyi], &a[midd]);return midd;}void QuickSort(int* a, int left, int right)
{if (left >= right){return;}//小区间优化,减少递归调用次数,避免深度太深if (right - left + 1 < 10){InsertSort(a + left, right - left + 1);return;}int keyi = PartSort1(a, left, right);//单趟排序QuickSort(a, left, keyi - 1);QuickSort(a, keyi + 1, right);}

1.5非递归实现

递归改非递归,一般有两种方式:1.用栈实现  2.用循环实现。

这里我们用栈来实现,应用栈先进后出的特点,划分左右区间。

具体步骤:

我们先创建一个栈并对其初始化。

先将整个数组的区间入栈,利用该区间找到基点,利用基点划分左右区间。

再将左右区间入栈,重复上面的过程,当栈为空的时候,排序结束。

代码实现: 

#include"stack.h"//非递归实现快排
void QuickSortNonR(int* a, int left, int right)
{ST st;STInit(&st);STPush(&st, right);STPush(&st, left);while (!STempty(&st)){int begin = STTop(&st);STPop(&st);int end = STTop(&st);STPop(&st);int keyi = PartSort3(a, begin, end);if (keyi + 1 < end){STPush(&st, end);STPush(&st, keyi + 1);}if (begin < keyi - 1){STPush(&st, keyi - 1);STPush(&st, begin);}}STDestroy(&st);}

1.6稳定性分析 

在排序过程中,划分操作会交换元素的位置,当存在相同的元素时,可能会被分到不同的区间,他们的相对位置就发生了变化,因此快排不稳定

2.归并排序

2.1算法思想

归并排序是一种分治算法,它的步骤主要包含两个部分:分解和合并。

分解:将待排序的数组从中间分成两个子数组,如果数组只有一个元素或为空,则不需要排序(这是递归的基本条件)

合并:将已经排序好的子数组合并成一个有序数组。通常比较两个子数组的前段元素来决定哪个元素应先放在合并后的数组中。

2.2具体步骤

1.我们先开辟一个与待排数组一样大小的数组tmp。

2.然后进行分解操作,找到中间位置,把待排数组分成两个子数组,在对子数组进行相同的分解操作,直到子数组不存在或子数组只有一个元素为止。分解操作完成。此时每个子数组都是有序的。

3.接下开始合并两个子数组,因为每个子数组都是有序的,我们创建两个指针变量begin1,begin2分别指向两个子数组的开头。遍历比较两个子数组,以升序为例,把他们较小的先插入到tmp数组中中。

4.每归并一次,就把tmp数组中的元素拷贝回原数组中。

2.3动图演示

2.4代码实现

//归并排序
void _MergeSort(int* arr, int begin, int end, int* tmp)
{if (begin >= end)return;int mid = (begin + end) / 2;_MergeSort(arr, begin, mid, tmp);_MergeSort(arr, mid + 1, end,tmp);//归并int i = begin;int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] > arr[begin2]){tmp[i++] = arr[begin2++];}else{tmp[i++] = arr[begin1++];}}//如果还剩下一些元素,就将剩下的元素直接放进tmp数组里while (begin1 <= end1){tmp[i++] = arr[begin1++];}while (begin2 <= end2){tmp[i++] = arr[begin2++];}//将归并排序好的数据拷贝回原数组中memcpy(arr+begin, tmp+begin, sizeof(int)*(end-begin+1));}void MergeSort(int* arr, int n)
{int* tmp = (int*)calloc(10,sizeof(int));if (tmp == NULL){perror("calloc fail");return;}_MergeSort(arr, 0, n - 1, tmp);free(tmp);tmp = NULL;
}

2.5算法优化

和快排一样,这里也会有递归深度太深的问题,解决方式也是一样的。


void _MergeSort(int* arr, int begin, int end, int* tmp)
{if (begin >= end)return;if (end-begin+1<10)//小区间优化{InsertSort(arr+begin, end-begin+1);return;}int mid = (begin + end) / 2;_MergeSort(arr, begin, mid, tmp);_MergeSort(arr, mid + 1, end,tmp);//归并int i = begin;int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] > arr[begin2]){tmp[i++] = arr[begin2++];}else{tmp[i++] = arr[begin1++];}}//如果还剩下一些元素,就将剩下的元素直接放进tmp数组里while (begin1 <= end1){tmp[i++] = arr[begin1++];}while (begin2 <= end2){tmp[i++] = arr[begin2++];}//将归并排序好的数据拷贝回原数组中memcpy(arr+begin, tmp+begin, sizeof(int)*(end-begin+1));}void MergeSort(int* arr, int n)
{int* tmp = (int*)calloc(10,sizeof(int));if (tmp == NULL){perror("calloc fail");return;}_MergeSort(arr, 0, n - 1, tmp);free(tmp);tmp = NULL;
}

2.6复杂度分析

时间复杂度:一般需要递归logN层,并且每层都需要遍历,因此时间复杂度为O(NlogN)

空间复杂度:这里因为额外开辟了一个数组,因此空间复杂度为O(N)。

2.7稳定性分析 

我们知道归并排序是把待排数组每次都划分为两个子数组,直到子区间不存在或只有一个元素,分解完成,开始合并。如果有两个相同的数,他们在归并的过程中相对位置是不会改变的。因此归并排序是稳定的

2.8非递归实现 

我们已经知道递归改非递归一般采用两种方式,栈或者是循环。

用栈先进后出的特点模拟快排的递归实现是比较容易的,但是归并排序有一个往回返的过程,当我们准备开始合并操作时,栈已经为空了,我们不知道之前子数组的区间,就还要用一个栈去存储该信息,比较麻烦,采用循环的话,是比较简单的。

void MergeSortNonR(int* arr, int n)
{int* tmp = (int*)calloc(n, sizeof(int));if (tmp == NULL){perror("calloc fail");return;}int gap = 1;//gap代表每组的数据个数while (gap < n){for (int i = 0; i < n; i += 2 * gap)//控制每组归并的起始位置{//第一组的区间int begin1 = i, end1 = i + gap - 1;//每组的数据个数为gap个,区间是左闭右闭->起始位置+个数-1//第二组的区间int begin2 = i + gap, end2 = i + 2 * gap - 1;int j = i;while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] > arr[begin2]){tmp[j++] = arr[begin2++];}else{tmp[j++] = arr[begin1++];}}//如果还剩下一些元素,就将剩下的元素直接放进tmp数组里while (begin1 <= end1){tmp[j++] = arr[begin1++];}while (begin2 <= end2){tmp[j++] = arr[begin2++];}//将归并排序好的数据拷贝回原数组中memcpy(arr + i, tmp + i, sizeof(int) * (end2-i+1));}gap *= 2;}free(tmp);tmp = NULL;
}

上面这个代码还存在一些问题,它只适用于排大小为2的次方倍的数组,如果换成其他的数组就会出现越界问题。

代码优化:

void MergeSortNonR(int* arr, int n)
{int* tmp = (int*)calloc(n, sizeof(int));if (tmp == NULL){perror("calloc fail");return;}int gap = 1;//gap代表每组的数据个数while (gap < n){for (int i = 0; i < n; i += 2 * gap)//控制每组归并的起始位置{//第一组的区间int begin1 = i, end1 = i + gap - 1;//每组的数据个数为gap个,区间是左闭右闭->起始位置+个数-1//第二组的区间int begin2 = i + gap, end2 = i + 2 * gap - 1;int j = i;if ( end1 >= n || begin2 >= n)break;//光标到这里的时候就是在处理第一种越界的情况if (end2 >= n)end2 = n-1;while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] > arr[begin2]){tmp[j++] = arr[begin2++];}else{tmp[j++] = arr[begin1++];}}//如果还剩下一些元素,就将剩下的元素直接放进tmp数组里while (begin1 <= end1){tmp[j++] = arr[begin1++];}while (begin2 <= end2){tmp[j++] = arr[begin2++];}//将归并排序好的数据拷贝回原数组中memcpy(arr + i, tmp + i, sizeof(int) * (end2 - i + 1));}gap *= 2;}}

 那我们来看一下为什么要这么改。

我们可以看到这里用三种越界方式:

1.end2越界,其余不越界【0 7】【8 15】

2.begin2 end2越界 【10 11】

3.end1 begin2 end2越界【8 11】【12 15】

我们再来看归并,归并其实没有必要保持每组子数组的元素个数相同才能归并,不相同也能归并,这并不会影响最终的排序效果。

理解了这个就好处理我们的问题了,观察一下可以发现,第二种情况和第三种情况已经不需要归并了,我们直接退出归并过程,开始下一轮归并。

只有第一种情况不好处理,只有end2一个越界,其他的都没有。

这里只需要对end2做一下更改即可,end2=n-1,就变成了【0 7】【8 9】,只是改变了第二个子数组中的元素个数,让两个子数组的元素个数不一样,这样也能归并,并且结果也是对的。

3.计数排序

3.1算法思想

计数排序其实就是统计数组中元素出现的个数,并根据统计的数据,对原数组数据直接覆盖即可。

3.2具体步骤

1.找出待排数组的max和min,即最大值和最小值(为了避免不必要的空间浪费)

2.开辟一个空间大小为max-min+1的数组count,遍历待排数组来计数

3.根据计数信息,对待排数组进行重新排序,

4.要注意的是在对待排数组进行更改的时候,要记得加上min才是原本的值。

3.3动图演示

3.4代码实现

//计数排序
//计数排序分为两步:1.计数 2.排序(排序的时候直接在原数组上覆盖)
void CountSort(int* arr, int n)
{int min = arr[0];int max = 0;for (int i = 1; i < n; i++){if (arr[i] > max)max = arr[i];if (arr[i] < min)min = arr[i];}int range = max - min + 1;int* count = (int*)calloc(range, sizeof(int));if (count == NULL){perror("calloc fail");return;}//计数for (int i = 0; i < n; i++){count[arr[i] - min]++;}//排序int j = 0;for (int i = 0; i < range; i++){while (count[i]--){arr[j++] = i + min;}}free(count);count = NULL;
}

3.5复杂度分析

时间复杂度:我们遍历了待排数组和count数组,因此时间复杂度为O(N+range)。

空间复杂度:额外开辟一个数组count,因此空间复杂度为O(N)。

3.6稳定性分析 

显而易见,计数排序不会改变相同值的相对位置,因此基数排序是稳定的。

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

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

相关文章

服务器SSH 免密码登录

1. 背景 为了服务器的安全着想&#xff0c;设置的服务器密钥非常长。但是这导致每次连接服务器都需要输入一长串的密码&#xff0c;把人折腾的很痛苦&#xff0c;所以我就在想&#xff0c;能不能在终端SSH的时候无需输入密码。 windows 可以使用 xshell 软件&#xff0c;会自…

安装react之nvm版本低引起的问题

1.背景 准备搭建一个react&#xff0c;然后看官网文档 创建项目&#xff0c;使用命令行 npx create-next-applatest 创建项目的流程都是正常的。当我准备运行项目的时候&#xff0c;报错了 原先的报错没有了&#xff0c;从网上找了一个类似的 重要的内容是&#xff1a;当前…

锂电池寿命预测 | Matlab基于ARIMA的锂电池寿命预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 锂电池寿命预测 | Matlab基于ARIMA的锂电池寿命预测 NASA数据集&#xff0c;B0005号电池&#xff0c;选择前110个数据训练&#xff0c;后58个数据测试预测。程序包含去趋势线、差分、平稳化及AIC准则判定p和q。命令窗…

【调试笔记-20240619-Windows-Typescripts中类型不匹配的解决方法】

调试笔记-系列文章目录 调试笔记-20240619-Windows-Typescripts中类型不匹配的解决方法 文章目录 调试笔记-系列文章目录调试笔记-20240619-Windows-Typescripts中类型不匹配的解决方法 前言一、调试环境操作系统&#xff1a;Windows 10 专业版调试环境调试目标 二、调试步骤搜…

webstorm yarn环境配置

1. 安装nodejs https://nodejs.cn/download/ 2. 安装npm npm i yarn -g3.下载并安装webstorm https://www.jetbrains.com/webstorm/ 4. 打开settings确认node和yarn的配置正确5. 打开项目更新包 yarn install

数据分析第十一讲:pandas应用入门(六)

pandas应用入门&#xff08;六&#xff09; 我们再来看看Index类型&#xff0c;它为Series和DataFrame对象提供了索引服务&#xff0c;有了索引我们就可以排序数据&#xff08;sort_index方法&#xff09;、对齐数据&#xff08;在运算和合并数据时非常重要&#xff09;并实现…

技术与创意并驾齐驱:打造扭蛋机小程序的独特魅力

引言 扭蛋机小程序以其独特的玩法和吸引力&#xff0c;在移动互联网市场中崭露头角。本文将深入探讨如何通过技术与创意的并驾齐驱&#xff0c;打造扭蛋机小程序的独特魅力。 一、技术驱动&#xff1a;打造稳定高效的小程序平台 在扭蛋机小程序的开发过程中&#xff0c;技术是…

Java swing JTable 示例

代码&#xff0c; import java.awt.Container; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTable;public class Mylmlk {public static void main(String[] agrs){JFrame framenew JFrame("学生成绩表");frame.setSize(500,2…

Hadoop升级失败,File system image contains an old layout version -64

原始版本 Hadoop 3.1.3 升级版本 Hadoop 3.3.3 报错内容如下 datasophon 部署Hadoop版本 查看Hadoop格式化版本 which hadoop-daemon.sh/bigdata/app/hadoop-3.1.3/sbin/hadoop-daemon.sh删除原来的旧版本 rm -rf /bigdata/app/hadoop-3.1.3查看环境变量 env|grep HADOOPHAD…

【归档】maven的使用

学习自波波酱老师SSM企业级框架最全教学视频 maven篇 maven的设置 <?xml version"1.0" encoding"UTF-8"?> <settings xmlns"http://maven.apache.org/SETTINGS/1.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance&qu…

定制汽车霍尔传感器

磁电效应霍尔传感器、饱和霍尔传感器、非线性霍尔传感器 霍尔传感器原理 霍尔传感器的工作原理基于霍尔效应&#xff0c;即当一块通有电流的金属或半导体薄片垂直地放在磁场中时&#xff0c;薄片的两端会产生电位差。这种现象称为霍尔效应&#xff0c;两端具有的电位差值称为…

嵌入式实验---实验二 中断功能实验

一、实验目的 1、掌握STM32F103中断程序设计流程&#xff1b; 2、熟悉STM32固件库的基本使用。 二、实验原理 1、在上一章的实验基础上&#xff0c;添加一个按键和一个LED&#xff1b; 2、使用中断的方式实现以下两个功能&#xff1a; &#xff08;1&#xff09;KEY1按键…

【git】gitee仓库本地克隆失败可能的一种解决办法

出错点&#xff1a; 在 gitee 克隆远程仓库到 本地时&#xff0c;可能会出现以下报错情况&#xff0c;无法成功克隆 正常流程&#xff1a;&#xff08;熟悉正常克隆流程的可以直接跳到下面的【解决办法】&#xff09; 我们一般复制仓库地址是在下面红线框框的位置&#xff0c…

华为云与AWS负载均衡服务深度对比:性能、成本与可用性

随着云计算的迅速发展&#xff0c;企业对于云服务提供商的选择变得越来越关键。在选择云服务提供商时&#xff0c;负载均衡服务是企业关注的重点之一。我们九河云将深入比较两大知名云服务提供商华为云和AWS的负载均衡服务&#xff0c;从性能、成本和可用性等方面进行对比。 AW…

知识库的创建(4) - KBServiceFactory:获取不同类型知识库服务的工厂类

文章目录 前言一、 方法详解1. get_service2. get_service_by_name3. get_default 二、 代码注释总结 前言 上一篇我们在update_docs里看到了 KBServiceFactory.get_service_by_name(knowledge_base_name)&#xff0c;这一篇我们一起来看看KBServiceFactory类 KBServiceFactor…

Vue--》从零开始打造交互体验一流的电商平台(三)

今天开始使用 vue3 + ts 搭建一个电商项目平台,因为文章会将项目的每处代码的书写都会讲解到,所以本项目会分成好几篇文章进行讲解,我会在最后一篇文章中会将项目代码开源到我的github上,大家可以自行去进行下载运行,希望本文章对有帮助的朋友们能多多关注本专栏,学习更多…

leetcode刷题(46-50)

算法是码农的基本功&#xff0c;也是各个大厂必考察的重点&#xff0c;让我们一起坚持写题吧。 遇事不决&#xff0c;可问春风&#xff0c;春风不语&#xff0c;即是本心。 我们在我们能力范围内&#xff0c;做好我们该做的事&#xff0c;然后相信一切都事最好的安排就可以啦…

【机器学习】 第1章 概述

一、概念 1.机器学习是一种通过先验信息来提升模型能力的方式。 即从数据中产生“模型”( model )的算法&#xff0c;然后对新的数据集进行预测。 2.数据集&#xff08;Dataset&#xff09;&#xff1a;所有数据的集合称为数据集。 训练集&#xff1a;用来训练出一个适合模…

什么是无限铸币攻击?它是如何运作的?

一、无限铸币攻击解释 无限铸币攻击是指攻击者操纵合约代码不断铸造超出授权供应限制的新代币。 这种黑客行为在去中心化金融 (DeFi) 协议中最为常见。这种攻击通过创建无限数量的代币来损害加密货币或代币的完整性和价值。 例如&#xff0c;一名黑客利用了 Paid 网络的智能…

ansible 模块进阶及变量

yum 模块进阶 - name: install pkgs hosts: webservers tasks: - name: install web pkgs # 此任务通过yum安装三个包 yum: name: httpd,php,php-mysqlnd state: present # 根据功能等&#xff0c;可以将一系列软件放到一个组中&#xff0c;安装软件包组&#xff0c;将会把很…