【C】初阶数据结构14 -- 归并排序

本篇文章主要是讲解经典的排序算法 -- 归并排序  


目录

1  递归版本的归并排序

1) 算法思想

2) 代码 

3) 时间复杂度与空间复杂度分析 

(1) 时间复杂度

(2) 空间复杂度

2  迭代版本的归并排序

1) 算法思想

2) 代码

3  文件的归并排序 

1) 外排序

2) 文件的归并排序

(1) 算法思想

(2) 代码


1  递归版本的归并排序

1) 算法思想

  归并排序与快速排序相同,都是运用了分治算法思想:将规模为 n 的问题分为k个规模较小的与原问题性质一致且相互独立的子问题,然后解决各个子问题,将子问题的解合并为大问题的解。

   归并排序是利用了之前合并两个有序链表的思想,每次将大问题二分,直到分到只有一个元素的子数组的情况,将只含有一个元素的子数组看作是一个有序的子数组,然后再合并两个有序数组,这样子问题就解决了,解决每一个子问题之后,大问题也就随之解决了。下面是解决两个个子问题的算法过程(这里假设排升序):

a. 首先定义 4 个整型变量 begin1, end1, begin2, end2, 这里假设要排序的整个数组为 arr,其中一个子问题,也就是要合并的第一个有序数组为 arr1;另一个子问题,也就是要合并的第二个有序数组为 arr2。begin1 与 begin2 分别指向 arr1 和 arr2 的起始下标end1 与 end2 分别指向 arr1 和 arr2 的结束下标,合并两个有序数组还需要一个中间数组 tmp 以及一个整型变量 index,tmp 来暂时存储合并两个有序数组后的结果,index 用来表示 tmp 数组当前存储结果的下标。

b. 然后依次比较 arr1[begin1] 与 arr2[begin2] 的大小关系,如果 arr1[begin1] < arr2[begin2] ,那就让 tmp[index] = arr1[begin1],然后 ++index,++begin1否则,就让 tmp[index] = arr2[begin2],然后 ++index,++begin2。循环上述过程,直到 begin1 > end1 或者 begin2 > end2。

c. 经历过第二步之后,arr1 与 arr2 肯定会有一个数组还有元素,我们需要把剩下的元素也放到 tmp 里面,所以如果 begin1 <= end1,就让 tmp[index] = arr1[begin1],然后 ++index,++begin1;如果 begin2 <= end2,就让 tmp[index] = arr2[begin2],然后 ++index,++begin2

d. 经历过上述两步之后,arr1 与 arr2 的合并结果已经放到 tmp 数组中了,最后不要忘记让合并后的结果,也就是 tmp 中的元素要放回排序的数组 arr 中

 下面是合并合并两个有序数组的过程:

下面是归并排序划分子问题的过程:

注意上述过程只是描述了递归划分子问题的过程,没有进行合并,原则上是当划分到每一条绿色的竖线左右两边子数组只有一个元素的时候,就应该进行合并了。


2) 代码 

归并排序主函数:

void MergeSort(int* arr,int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail!\n");exit(1);}_MergeSort(arr, 0, n - 1, tmp);free(tmp);tmp = NULL;
}

归并排序子函数: 

void _MergeSort(int* arr, int left, int right, int* tmp)
{if (left >= right){return;}int mid = left + (right - left) / 2;_MergeSort(arr, left, mid, tmp);_MergeSort(arr, mid + 1, right, tmp);//合并两个有序数组int begin1 = left, end1 = mid;int begin2 = mid + 1, end2 = right;int index = left;while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] < arr[begin2])tmp[index++] = arr[begin1++];elsetmp[index++] = arr[begin2++];}while (begin1 <= end1)tmp[index++] = arr[begin1++];while (begin2 <= end2)tmp[index++] = arr[begin2++];//再将tmp数组的内容放回arr数组for (int i = left;i <= right;i++)arr[i] = tmp[i];
}

解释: 归并排序主函数的作用主要是动态开辟一个辅助数组 tmp 与释放动态开辟的空间,防止内存泄漏,tmp 主要用来帮助完成有序数组的合并;在子函数里面,left 代表要进行排序的子数组的最左边元素的下标,right 代表要进行排序的子数组的最右边元素的下标;然后开始有一个递归的边界条件,当 left >= right,也就是子数组中只有一个元素或者没有元素的时候,就会停止递归,此时将只有一个元素的子数组视为是一个有序数组;边界条件下面的三行代码主要就是用来进行递归,划分子问题的,首先计算出大问题的中间位置下标 mid,然后将大问题在中间数组划分为两个子问题,也就是对 [left, mid][mid + 1, right] 区间的数组进行递归;再后面的代码就是为了解决某两个具体的子问题也就是合并两个有序数组的过程(上面已经讲解过,这里不再赘述)。

 测试用例

int main()
{int arr[] = { 10, 2, 5, 7, 1, 4, 8, 9, 6, 20};int n = sizeof(arr) / sizeof(arr[0]);MergeSort(arr, n);Print(arr, n);return 0;
}

3) 时间复杂度与空间复杂度分析 

(1) 时间复杂度

  与快速排序相同,带有递归的函数的时间复杂度计算包括两部分,一部分是解决子问题的时间复杂度,另一部分是递归的深度,T(n) = 递归深度 * 解决子问题的时间复杂度。

  我们先来看解决子问题的时间复杂度,看上面解决具体解决子问题的代码,由于只有一层循环,且循环的次数最多是 end1 - begin1 或者 end2 - begin2 次,最坏情况下是对最开始要排序的整个数组从中间划分的两个子数组进行排序的时候,此时循环的次数就是 n / 2 次,所以解决子问题的时间复杂度 T(n) = O(n)。

  再来看递归的深度,由于每次都对数组进行二分,在中间位置划分数组,对左边数组进行递归,再对右边数组进行递归,所以递归树应该为:

我们假设最开始的数组元素个数为 n,那么递归到第二层的时候每个子数组元素个数就为 n/2,第三层就为 n/4,也就是 n/2^2,第四层就是 n/2^3,所以递归到第 x 层的时候元素个数就为 n/2^x,而递归的停止条件为元素个数为 1,所以 n/2^x = 1  ==>  x = \log_{2}n,所以总的层数应该是 \log_{2}n + 1 层,所以递归的深度为 \log_{2}n + 1。

  所以归并排序的时间复杂度 T(n) = O(nlogn + n)  ==>  T(n) = O(nlogn)

(2) 空间复杂度

  由于在函数中使用了一个辅助数组 tmp 来帮助合并,其余都是有限的变量,所以空间复杂度 S(n) = O(n)


2  迭代版本的归并排序

1) 算法思想

  在迭代版本的归并排序中,我们依然可以借助递归版本归并排序的算法思想:先将数组划分到 1 个元素的子数组的情况,将 1 个元素的子数组看作是有序的数组,然后依次合并两个有序数组,最终使数组有序;在迭代版本归并排序中,我们划分子问题是用一个 gap 变量来实现的,具体过程如下:

a. 函数会传入两个参数:arr(数组元素首地址)与 n (数组元素个数),先创建一个整型变量 gap 与一个辅助数组 tmp(用来合并两个有序数组);开始先让 gap = 1,代表每个要合并的数组里只有一个元素之后每次循环都让 gap *= 2,代表要合并的数组由 1 个元素变为 2 个元素,之后再变为 4 个元素,依次类推,直到 gap >= n,就停止排序

b. 然后在每次循环里面,由于在大数组里我们要寻找要合并的两个子数组,所以我们还要用一个变量 i 来遍历数组,寻找要合并的两个子数组;在循环过程中,i 每次会指向要合并的两个子数组中第一个子数组的首元素由于每次首次要合并的第一个子数组总是以 0 下标元素开头的子数组,所以 i 总是以 0 开头然后合并完两个有序子数组之后,i 会指向下一次要合并的两个有序子数组的第一个子数组首元素,所以 i 在遍历过程中会跳过上一次排序的两个子数组,也就是 2 * gap 个元素,即 i += 2 * gap;停止条件也很简单,就是 i >= n 越界时,就会停止两个子数组的合并。

c. 在合并两个子数组的过程中需要找到两个子数组,这里用 begin1, end1 代表第一个要合并的子数组的起始和结束位置,begin2 和 end2 代表第二个要合并的子数组的起始和结束位置,根据 i 与 gap 就可以推算出 begin1、end1、begin2、与 end2:begin1 = i,end1 = i + gap - 1,begin2 = i + gap,end2 = begin1 + gap - 1 = i + 2 * gap - 1

d. 再创建一个 index 来记录要放到 tmp 数组中的位置,开始先让 index = begin1,之后进行两个有序数组的合并(上面递归版本讲解过,这里不再赘述),最后不要忘记让 tmp 中的元素放到 arr 数组中。

 当 gap = 1 时排序过程如图所示: 

在上述的合并过程中,我们可以发现 begin2 与 end2 是可能发生越界的,所以当发生越界情况时,我们需要特殊处理一下:当 begin2 越界时,此时代表 begin1 的子数组后面是没有 begin2 子数组与其合并的,也就是此时只有一个有序子数组,就代表此次不需要合并了,跳出循环即可;当end2 越界时,会在 gap = 2 中出现,下面我们再详细讨论。 

 当 gap = 2 时排序过程如图所示:

通过上述合并过程,可以看到当 end2 出现越界行为时,我们让其回到了最后一个位置,也就是 end2 = n - 1,此时就可以避免越界行为的发生

  接下来 gap 会继续 gap *= 2,变为4,之后会再变为 8,最后变为 16 的时候由于大于 n,会退出循环,排序就完成了,如果你有兴趣可以自己画一下图模拟一下过程,在这里就不再画图。


2) 代码

void MergeSortNorR(int* arr, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail!\n");exit(1);}//每组元素的个数int gap = 1;while (gap < n){//遍历每两组,进行两组之间的合并// i 每次指向要合并两组的第一组的第一个元素for (int i = 0; i < n; i += 2 * gap){int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;int index = begin1;//为了防止要合并第二个数组越界,需要特殊处理一下//begin2 如果越界,代表第一组没有要合并的第二组了,不用合并了,直接跳出循环if (begin2 >= n){break;}//如果要合并的第二组元素不满gap个,那就有多少合并多少if (end2 >= n){end2 = n - 1;}//合并两个有序数组while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] < arr[begin2]){tmp[index++] = arr[begin1++];}else{tmp[index++] = arr[begin2++];}}while (begin1 <= end1){tmp[index++] = arr[begin1++];}while (begin2 <= end2){tmp[index++] = arr[begin2++];}//把tmp中的数据放回原数组memcpy(arr + i, tmp + i, sizeof(int) * (end2 - i + 1));}gap *= 2;}//最后不要忘记释放tmp数组free(tmp);tmp = NULL;
}

测试用例:

#include<stdio.h>
#include<stdlib.h>void MergeSortNorR_Test()
{//奇数个元素的情况//int arr[] = { 2, 1, 10, 4, 7, 3, 8, 5, 11 };//偶数个元素的情况int arr[] = { 2, 1, 10, 4, 7, 3, 8, 5 };int n = sizeof(arr) / sizeof(arr[0]);MergeSortNorR(arr, n);for (int i = 0; i < n; i++){printf("%d ", arr[i]);}printf("\n");
}int main()
{MergeSortNorR_Test();return 0;
}

3  文件的归并排序 

1) 外排序

        不同于内排序,外排序是进行文件与磁盘内数据的排序,也就是不是直接对存储在内存中的数据进行排序,而是先将外存中的数据先调入内存,排完序之后再将数据放回文件或者磁盘中,就是外排序。

        那么什么时候会用到外排序呢?一般都是数据量很多时,比如一个文件里面有 10G 的数据,无法全部调入内存,还想要对文件内的数据进行排序,此时就需要进行外排序。

2) 文件的归并排序

(1) 算法思想

        如果想要排序的数据量较大时,我们一般都会先把数据保存在文件中,然后通过 C 语言中文件的相关操作,将文件中的数据排序好后,再放入文件之中,而在七大排序算法中,不需要内存的随机访问而又效率比较快的排序算法就是归并排序了(堆排序、快速排序、希尔排序都需要对内存中的数据进行随机访问),所以归并排序算法既可以用于实现内排序,也可以实现外排序,以下是对文件进行归并排序的算法过程:

a. 先在要进行排序的文件 data.txt 中取出 n 个数据,将这 n 个数据排好序后,放到 file1.txt 里面;再取出 n 个数据,排好序之后,放到 file2.txt 文件里面。

b. 然后对 file1.txt 和 file2.txt 中的数据进行归并(就是前面归并排序中合并两个有序子数组的过程),将归并后的数据放到 mfile.txt 里面。

c. 此时,file1.txt 与 file2.txt 已经没用了,删除 file1,txt 与 file2.txt 文件,将 mfile.txt 文件重命名为 file1.txt 文件。

d. 然后再取出 n 个数据放到 file2.txt 里面,再将 file1.txt(之前的 mfile.txt 文件)与 file2.txt 文件归并,将归并后的数据放到新的 mfile.txt 文件里面;如此循环,直到 data.txt 文件中没有数据为止。最终,排好序的数据会存储在 file1.txt 文件里面。

上述算法过程如下图所示:
 

        在上述算法思想的实现过程中,有三个函数需要我们了解一下,第一个就是删除文件的函数,第二个就是对文件名重命名的函数,还有一个就是对数据进行排序的函数,接下来我们来一次了解一下。

a.  删除文件的函数:

删除文件的函数的函数名字叫做 remove 函数,与 printf 和 scanf 函数相同,其都被包含在 stdio.h 头文件下,传入的参数仅有一个,就是要删除文件的文件名字符指针即可。所以删除 file1.txt 文件与 file2.txt 文件,我们可以直接 remove("file1.txt") 与 remove("file2.txt")。

 b. 重命名的函数:

对文件进行重命名的函数叫做 rename 函数,传入的参数也很简单,第一个参数为要修改的文件的文件名的字符指针,也就是 oldname,第二个参数为修改后的文件名的字符指针,也就是 newname。所以将 mfile 重命名为 file1.txt 可以写为 rename("mfile.txt", "file1.txt")。

c. 对数据进行排序的函数:这里可以使用我们之前学习过的七大排序算法,比如:快速排序、堆排序、希尔排序都可以,当然这里也可以直接使用 qsort 函数:

可以看到 qsort 函数是需要四个参数的,第一个是需要进行排序的首元素的指针,第二个参数是进行排序的元素的个数,第三个参数是需要进行排序的元素的大小(单位为字节),第四个参数是一个函数指针,其指向的函数的返回值为 int, 两个参数类型为 const void*。对于第四个参数 compare 我们具体讲解一下:

从下面的表格我们可以看出其对返回值是有要求的,如果第一个参数想排在第二个参数之前,那就返回小于 0 的数字;如果相等返回 0;否则返回大于 0 的数字,由于我们这里排的都是整形,且升序排序,所以我们可以这样设计 compare 函数:

int compare(const void* a, const void* b)
{return (*(int*)a - *(int*)b);
}

 注意,一定要对 a 和 b 进行强转,因为 void* 类型的指针无法进行解引用的


(2) 代码

#include<stdio.h>
#include<stdlib.h>
#include<time.h>void CreateData()
{const char* file = "data.txt";FILE* fin = fopen(file, "w");if (fin == NULL){perror("fopen fail!\n");exit(1);}srand(time(0));const int n = 1000000;for (int i = 0; i < n; i++){fprintf(fin, "%d\n", rand() + i);}fclose(fin);
}int Compare(const void* a, const void* b)
{return (*(int*)a - *(int*)b);
}//返回读取到的数据个数
int SortNValeToFile(FILE* fout, int n, const char* file)
{int x = 0;int* a = (int*)malloc(sizeof(int) * n);if (a == NULL){perror("malloc error");return 0;}// 想读取n个数据,如果遇到文件结束,应该读到j个int j = 0;for (int i = 0; i < n; i++){if (fscanf(fout, "%d", &x) == EOF)break;a[j++] = x;}if (j == 0){free(a);return 0;}// 排序qsort(a, j, sizeof(int), Compare);FILE* fin = fopen(file, "w");if (fin == NULL){free(a);perror("fopen error");return 0;}// 写回file1文件for (int i = 0; i < j; i++){fprintf(fin, "%d\n", a[i]);}free(a);fclose(fin);return j;
}void MergeFile(const char* file1, const char* file2, const char* mfile)
{FILE* fout1 = fopen(file1, "r");if (fout1 == NULL){perror("fopen1 fail!\n");return;}FILE* fout2 = fopen(file2, "r");if (fout2 == NULL){perror("fopen2 fail!\n");return;}FILE* fin = fopen(mfile, "w");if (fin == NULL){perror("fin fail!\n");return;}//对文件进行归并排序int x1 = 0;int x2 = 0;int ret1 = fscanf(fout1, "%d", &x1);int ret2 = fscanf(fout2, "%d", &x2);while (ret1 != EOF && ret2 != EOF){//如果 file1 的数据小于 file2 的数据,就将 file1 的数放到 mfile 里面if (x1 < x2){fprintf(fin, "%d\n", x1);ret1 = fscanf(fout1, "%d", &x1);}else{fprintf(fin, "%d\n", x2);ret2 = fscanf(fout2, "%d", &x2);}}while (ret1 != EOF){fprintf(fin, "%d\n", x1);ret1 = fscanf(fout1, "%d", &x1);}while (ret2 != EOF){fprintf(fin, "%d\n", x2);ret2 = fscanf(fout2, "%d", &x2);}fclose(fout1);fclose(fout2);fclose(fin);
}int main()
{CreateData();//创建三个文件const char* file1 = "file1.txt";const char* file2 = "file2.txt";const char* mfile = "mfile.txt";//从data.txt中取出n个数据放到file1与file2中FILE* fout = fopen("data.txt", "r");if (fout == NULL){perror("fopen fail!\n");exit(1);}int N = 100000;SortNValeToFile(fout, N, file1);SortNValeToFile(fout, N, file2);while (1){//将两个文件归并到 mfile 文件MergeFile(file1, file2, mfile);//删除file1 与 file2 文件remove(file1);remove(file2);//将mfile 重命名为 file1rename(mfile, file1);//再从data.txt中取出n个数据放到file2 里面//如果取不到数据说明归并结束if (SortNValeToFile(fout, N, file2) == 0)break;}return 0;
}

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

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

相关文章

【相机标定】OpenCV 相机标定中的重投影误差与角点三维坐标计算详解

摘要&#xff1a; 本文将从以下几个方面展开&#xff0c;结合典型代码深入解析 OpenCV 中的相机标定过程&#xff0c;重点阐述重投影误差的计算方法与实际意义&#xff0c;并通过一个 calcBoardCornerPositions() 函数详细讲解棋盘格角点三维坐标的构建逻辑。 在计算机视觉领域…

RabbitMQ-运维

文章目录 前言运维-集群介绍多机多节点单机多节点 多机多节点下载配置hosts⽂件配置Erlang Cookie启动节点构建集群查看集群状态 单机多节点安装启动两个节点再启动两个节点验证RabbitMQ启动成功搭建集群把rabbit2, rabbit3添加到集群 宕机演示仲裁队列介绍raft算法协议 raft基…

JVM之内存管理(一)

部分内容来源&#xff1a;JavaGuide二哥Java 图解JVM内存结构 内存管理快速复习 栈帧&#xff1a;局部变量表&#xff0c;动态链接&#xff08;符号引用转为真实引用&#xff09;&#xff0c;操作数栈&#xff08;存储中间结算结果&#xff09;&#xff0c;方法返回地址 运行时…

无线射频模块如何通过CE RED认证?关键规范与准备策略详解

随着无线通信设备在欧洲市场的广泛应用&#xff0c;CE RED认证已成为模块类产品进入欧盟的强制通行证。作为专注于LoRa模块、对讲模块与FSK射频模块研发的技术企业&#xff0c;我们深知从设计、测试到量产&#xff0c;每一个环节都需紧扣合规底线。本文将围绕CE RED认证核心要求…

Golang中集合相关的库

一切编程语言的底层结构都是数组&#xff0c;其它复杂数据结构如Map, Stack&#xff0c;Heap和Queue都是基于数组建立起来的。 Go语言主流工具库推荐&#xff08;含常用数据结构实现&#xff09; 以下是目前Go生态中最主流且活跃的工具库&#xff0c;包含队列、栈、优先级队列…

ABAP 导入Excel形成内表

文章目录 创建导入模板程序实现代码代码解析运行结果 创建导入模板 程序实现 代码 *&---------------------------------------------------------------------* *& Report Z_EXCEL_UPLOAD_LHY *&--------------------------------------------------------------…

特殊配合力(SCA)作为全基因组关联分析(GWAS)的表型,其生物学意义和应用价值

生物学意义 解析非加性遗传效应 特殊配合力(SCA)主要反映特定亲本组合的杂交优势,由非加性遗传效应(如显性、超显性、上位性)驱动。显性效应涉及等位基因间的显性互作,上位性效应则涉及不同位点间的基因互作。通过SCA-GWAS,可以定位调控这些非加性效应的关键基因组区域…

应急响应基础模拟靶机-security1

PS:杰克创建在流量包(result.pcap)在根目录下&#xff0c;请根据已有信息进行分析 1、攻击者使用的端口扫描工具是? 2、通过流量及日志审计&#xff0c;攻击者上传shell的时访问web使用IP地址是多少? 3、审计流量日志&#xff0c;攻击者反弹shell的地址及端口? 4、攻击者…

uniapp-商城-47-后台 分类数据的生成(通过数据)

在第46章节中&#xff0c;我们为后台数据创建了分类的数据表结构schema&#xff0c;使得可以通过后台添加数据并保存&#xff0c;同时使用云函数进行数据库数据的读取。文章详细介绍了如何通过前端代码实现分类管理功能&#xff0c;包括获取数据、添加、更新和删除分类。主要代…

ClickHouse的基本操作说明

说明 文章内容包括数据库管理、表操作及查询等核心功能 创建数据库 -- 默认引擎&#xff08;Atomic&#xff09; CREATE DATABASE IF NOT EXISTS test_db; -- MySQL引擎&#xff08;映射外部MySQL数据库&#xff09; CREATE DATABASE mysql_db ENGINE MySQL(host:port, m…

Nacos源码—7.Nacos升级gRPC分析四

大纲 5.服务变动时如何通知订阅的客户端 6.微服务实例信息如何同步集群节点 6.微服务实例信息如何同步集群节点 (1)服务端处理服务注册时会发布一个ClientChangedEvent事件 (2)ClientChangedEvent事件的处理源码 (3)集群节点处理数据同步请求的源码 (1)服务端处理服务注册…

《Overlapping Experiment Infrastructure: More, Better, Faster》论文阅读笔记

文章目录 1 背景2 三个核心概念3 Launch层&#xff1a;特性发布的专用机制4 流量分发策略和条件筛选4.1 四种流量分发类型4.2 条件筛选机制 5 工具链与监控体系6 实验设计原则7 培训参考与推荐 1 背景 谷歌&#xff08;Google&#xff09;以数据驱动著称&#xff0c;几乎所有可…

国芯思辰| 医疗AED可使用2通道24位模拟前端SC2946(ADS1292)

生物电信号监测技术在医疗健康行业中发展迅速&#xff0c;成为评估人体生理健康状况的关键手段。心电&#xff08;ECG&#xff09;、脑电&#xff08;EEG&#xff09;和肌电&#xff08;EMG&#xff09;等信号&#xff0c;通过精密模拟前端芯片捕捉和处理&#xff0c;对医疗诊断…

数据结构【二叉搜索树(BST)】

二叉搜索树 1. 二叉搜索树的概念2. 二叉搜索树的性能分析3.二叉搜索树的插入4. 二叉搜索树的查找5. 二叉搜索树的删除6.二叉搜索树的实现代码7. 二叉搜索树key和key/value使用场景7.1 key搜索场景&#xff1a;7.2 key/value搜索场景&#xff1a; 1. 二叉搜索树的概念 二叉搜索…

RDMA高性能网络通信实践

RDMA高性能网络通信实践 一、背景介绍二、方法设计A.实现方案B.关键技术点 三、代码及注释四、注意事项 一、背景介绍 远程直接内存访问&#xff08;RDMA&#xff09;技术通过绕过操作系统内核和CPU直接访问远程内存&#xff0c;实现了超低延迟、高吞吐量的网络通信。该技术广…

ndarray数组掩码操作,True和False获取数据

#数组掩码的表示方法 def testht05():a np.arange(1,10)mask [True,False,True,True,False,True,False,True,True]print(a[mask]) 另外的用法&#xff1a; #掩码操作获取子集 def testht06():a np.arange(1,100)print(a[a%3 0 & (a%7 0)] )b np.array([A,"B&qu…

索引工具explain

EXPLAIN 是 MySQL 中一个非常有用的工具,用于分析查询的执行计划。通过 EXPLAIN,你可以了解 MySQL 是如何执行查询的,包括它如何使用索引、表的扫描方式等。这有助于优化查询性能。以下是 EXPLAIN 输出的各个字段的详细解释: 基本用法 EXPLAIN SELECT * FROM table_name …

Git回顾

参考视频:【GeekHour】一小时Git教程 一句话定义&#xff1a;Git是一个免费开源的分布式版本控制系统。 版本控制系统可以分为两种&#xff0c;1.集中式&#xff08;SVN&#xff0c;CVS&#xff09;&#xff1b;2.分布式&#xff08;git&#xff09; git的工作区域和文件状态…

python打卡day20

特征降维------特征组合&#xff08;以SVD为例&#xff09; 知识点回顾&#xff1a; 奇异值的应用&#xff1a; 特征降维&#xff1a;对高维数据减小计算量、可视化数据重构&#xff1a;比如重构信号、重构图像&#xff08;可以实现有损压缩&#xff0c;k 越小压缩率越高&#…

GuPPy-v1.2.0安装与使用-生信工具52

GuPPy&#xff1a;Python中用于光纤光度数据分析的免费开源工具 01 背景 Basecalling 是将原始测序信号转换为碱基序列的过程&#xff0c;通俗地说&#xff0c;就是“把碱基识别出来”。这一过程在不同代测序技术中各不相同&#xff1a; 一代测序是通过解析峰图实现&#xff1…