【408精华知识】速看!各种排序的大总结!

news2024/11/23 6:20:41

在这里插入图片描述

文章目录

  • 一、插入排序
    • (一)直接插入排序
    • (二)折半插入排序
    • (三)希尔排序
  • 二、交换排序
    • (一)冒泡排序
    • (二)快速排序
  • 三、选择排序
    • (一)简单选择排序
    • (二)堆排序
  • 四、归并排序
  • 五、基数排序
  • 六、计数排序
  • 七、外部排序

一、插入排序

(一)直接插入排序

算法思想:每次将一个待排序元素按其关键字大小插入到已排好序的序列中,直至所有元素都被排完。

1.排序过程:1)比较:将待排序元素与其左侧的已排序序列从后向前比较,直至找到它的位置;2)移动:将其待放位置的原元素与右侧元素全部右移一位;3)对下一个待排序元素继续执行1)、2)操作。

2.举个例子
在这里插入图片描述

3.算法特点:待排序元素左边的序列是已排好序的序列,右边的序列是未排序序列。

4.时间复杂度
1)最好情况:最好情况是有序序列,每个元素只需要和其左侧第一个元素比较一次,无需移动,故这种情况下的时间复杂度是O(n)。

2)最坏情况:最坏情况是倒序序列,每个元素要每个元素要和其左侧所有元素比较,故这种情况下的时间复杂度是O(n2)。

3)平均情况:取上述最好与最坏情况的平均值作为平均情况下的时间复杂度,故这种情况下的时间复杂度是O(n2)。

5.空间复杂度:仅使用了常数个辅助单元,因而空间复杂度为O(1)。

6.是否稳定: 因为每次插入元素时总是从后往前先比较再移动,所以不会出现相同元素相对位置发生变化的情况,即直接插入排序是一个稳定的排序算法。

7.适用性:适用于顺序存储和链式存储的线性表,采用链式存储时无须移动元素。

8.C语言代码

void InsertSort(ElemType A[],int n){
	int i,j;
	for(i=2;i<=n;i++){ //依次将A[2]-A[n]插入前面已排序的序列 
		if(A[i]<A[i-1]){ //若A[i]关键码小于其前驱,将A[i]插入有序表 
			A[0]=A[i]; //复制为哨兵,A[0]不存放元素 
			for(j=i-1;A[0]<A[j];--j){ //从后向前查找待插入元素 
				A[j+1]=A[j]; //向后挪位 
			}
			A[j+1]=A[0]; //复制到插入位置 
		}
	}
}

(二)折半插入排序

算法思想:对直接插入排序进行改进,折半查找出元素的待插入位置,然后统一地移动待插入位置之后的所有元素。

1.排序过程:1)比较:将待排序元素与其左侧的已排序序列折半比较,直至找到它的位置;2)移动:将其待放位置的原元素与右侧元素全部右移一位;3)对下一个待排序元素继续执行1)、2)操作。

2.举个例子
在这里插入图片描述

3.算法特点:对直接插入排序的改进,仅减少了比较元素的次数,可以提交时间效率,对于数据量不很大的排序表,折半插入排序往往能表现出很好的性能。

4.时间复杂度
1)最好情况:最好情况是有序序列,折半插入排序减少了元素的平均比较次数,且折半插入的比较次数与初始状态无关,其移动次数与直接插入排序是一样的,故在最好情况下,每个元素需要比较,时间复杂度为O( n l o g 2 n nlog_2{n} nlog2n),其中n是元素个数,O( l o g 2 n log_2{n} log2n)是每个元素需要比较的次数。

2)最坏情况:最坏情况是倒序序列,共n个元素,此时每个元素需要比较O( l o g 2 n log_2{n} log2n)次,但每个元素需要移动O(n)次,所以总的时间复杂度和直接插入排序一样,还是O(n2)。

3)平均情况:取上述最好与最坏情况的平均值作为平均情况下的时间复杂度,故这种情况下的时间复杂度是O(n2)。

5.空间复杂度:仅使用了常数个辅助单元,因而空间复杂度为O(1)。

6.是否稳定:同样值的元素,左侧的一定会出现在右侧的前面,故是稳定的。

7.适用性:涉及到给定数组下标找相应的数组元素的问题,所以只适用于顺序存储的线性表。

8.C语言代码

void InsertSort(ElemType A[],int n){
	int i,j,low,high,mid;
	for(i=2;i<=n;i++){ //依次将A[2]-A[n]插入前面已排序的序列 
		A[0]=A[i]; //暂存至A[0]
		low=1;high=i-1; //设置折半查找的范围
		while(low<=high){
			mid=(low+high)/2; //取中间点
			if(A[mid]>A[0]){
				high=mid-1; //查找左半子表 
			} 
			else	loe=mid+1; //查找右半子表 
		} 
		for(j=i-1;j>=high+1;--j){
			A[j+1]=A[j]; //统一后移元素,空出插入位置 
		}
		A[high+1]=A[0]; //插入操作 
	}
}

(三)希尔排序

算法思想:从前面的分析可知,直接插入排序算法的时间复杂度为O(n2),但若待排序列为“正序”时,其时间效率可提高至O(n),由此可见它更适用于基本有序的排序表和数据量不大的排序表。希尔排序正是基于这两点分析对直接插入排序进行改进而得来的,先将待排序表分割成若干形如L[i,i+d,i+2d,⋯,i+kd]的“特殊”子表,即把相隔某个“增量”的记录组成一个子表,对各个子表分别进行直接插入排序,当整个表中的元素已呈“基本有序”时,再对全体记录进行一次直接插入排序。

1.排序过程:取增量d,使所有距离为d的元素为一组,进行直接插入排序,直至d取到1。

2.举个例子
在这里插入图片描述

3.算法特点:根据希尔排序的特点,可以发现,在希尔排序的过程中,是按增量段有序的,可以通过这个特点判断一个排序过程是否是希尔排序或者是以多大增量进行插入排序的希尔排序。

4.时间复杂度:因为希尔排序的时间复杂度依赖于增量序列的函数,这涉及数学上尚未解决的难题,所以其时间复杂度分析比较困难。当n在某个特定范围时,希尔排序的时间复杂度约为O(n1.3)。在最坏情况下希尔排序的时间复杂度为O(n2)。

5.空间复杂度:仅使用了常数个辅助单元,因而空间复杂度为O(1)。

6.是否稳定: 由于是按增量进行插入排序,可能导致相同值元素相对位置发生变化,因此是不稳定的。

7.适用性:涉及到给定数组下标找相应的数组元素的问题,所以只适用于顺序存储的线性表。

8.C语言代码

void ShellSort(ElemType A[],int n){
//A[0]只是暂存单元,不是哨兵,当j<=0时,插入位置已到 
	for(dk=n/2;dk>=1;dk=dk/2){ //步长变化 
		for(i=dk+1;i<n;++i){
			if(A[i]<A[i-dk]){ //需将A[i]插入有序增量子表 
				A[0]=A[i]; //暂存在A[0]; 
				for(j=i-dk;j>0&&A[0]<A[j];j-=dk){
					A[j+dk]=A[j]; //记录后移,寻找插入的位置 
				}
				A[j+dk]=A[0]; //插入 
			}
		}
	}
}

二、交换排序

(一)冒泡排序

算法思想:冒泡排序恰如其名,是将最大(最小)元素一个个浮上水面,最终得到有序序列。

1.排序过程:从后往前(或从前往后)两两比较相邻元素的值,若为逆序(即
A[i-1]>A[i]),则交换它们,直到序列比较完,这称为一次冒泡。重复冒泡,直至成为有序序列。

2.举个例子
在这里插入图片描述

3.算法特点:冒泡排序中所产生的有序子序列一定是全局有序的(不同于直接插入排序),也就是说,有序子序列中的所有元素的关键字一定小于(或大于)无序子序列中所有元素的关键字,这样每趟排序都会将一个元素放置到其最终的位置上,因此会有前面的部分元素或者后面的部分元素是有序的。

4.时间复杂度
1)最好情况:当初始序列有序时,显然第一趟冒泡后flag依然为false(本趟没有元素交换),从而直接跳出循环,比较次数为n-1,移动次数为0,从而最好情况下的时间复杂度为O(n)。

2)最坏情况:最坏情况是逆序序列,此时每个元素需要比较和交换O(n)次,所有元素排完序后比较和交换O(n2)次,也即时间复杂度为O(n2)。

3)平均情况:取上述最好与最坏情况的平均值作为平均情况下的时间复杂度,故这种情况下的时间复杂度是O(n2)。

5.空间复杂度:仅使用了常数个辅助单元,因而空间复杂度为O(1)。

6.是否稳定: 如果是从后往前排,假如是同值元素,后面的元素无法浮到前面的元素的前面,也就是相对位置不会改变,即算法稳定。

7.适用性:冒泡排序适用于顺序存储和链式存储的线性表。

8.C语言代码

void BubbleSort(ElemType A[],int n){
	for(i=0;i<n-1;i++){
		flag=false;//表示本趟冒泡是否发生交换的标志
		for(j=n-1;j>i;j--){ //一趟冒泡过程 
			if(A[j-1]>A[j]){ //若为逆序 
				swap(A[j-1],A[j]); //交换 
				flag=true;
			}
		}
		if(flag==false)	return; //本趟遍历后没有发生交换,说明表已有序 
	}
}

(二)快速排序

算法思想:基于分治法,其中分是划分为两部分,治是在这两部分上分别采用快速排序。在待排序表L[1.n]中任取一个元素 pivot 作为枢轴(或称基准,通常取首元素),通过一趟排序将待排序表划分为独立的两部分L[1⋯k-1]和L[k+1⋯n],使得L[1⋯k-1]中的所有元素小于pivot,L[k+1.n]中的所有元素大于或等于pivot,则pivot放在了其最终位置L(k)上,这个过程称为一次划分。然后分别递归地对两个子表重复上述过程,直至每部分内只有一个元素或为空为止,即所有元素放在了其最终位置上。

1.排序过程
1)设两个指针i和j,初值分别为low和 high,取第一个元素(值为low)为枢轴赋值到变量 pivot;
2)指针j从high往前搜索找到第一个小于枢轴的元素,将其交换到i所指位置;指针i从low往后搜索找到第一个大于枢轴的元素,将其交换到j所指位置;
3)重复2),直至i==j;
4)对pivot两边的元素再从1)开始进行快排。

2.举个例子
在这里插入图片描述

3.算法特点:快速排序是所有内部排序算法中平均性能最优的排序算法,其每趟排序都能确定枢轴元素的最终位置。

4.时间复杂度:快速排序的运行时间与划分是否对称有关,有很多方法可以提高算法的效率:一种方法是尽量选取一个可以将数据中分的枢轴元素,如从序列的头尾及中间选取三个元素,再取这三个元素的中间值作为最终的枢轴元素;或者随机地从当前表中选取枢轴元素,这样做可使得最坏情况在实际排序中几乎不会发生。

1)最好情况:在最理想的状态下,即Partition()能做到最平衡的划分,得到的两个子问题的大小都不可能大于 n/2,在这种情况下,快速排序的运行速度将大大提升,此时,时间复杂度为O( n l o g 2 n nlog_2{n} nlog2n)。

2)最坏情况:快速排序的最坏情况发生在两个区域分别包含n-1个元素和0个元素时,这种最大限度的不对称性若发生在每层递归上,即对应于初始排序表基本有序或基本逆序时,就得到最坏情况下的时间复杂度为O(n2)。

3)平均情况:平均情况下为O(n2)。

5.空间复杂度:由于快速排序是递归的,因此需要借助一个递归工作栈来保存每层递归调用的必要信息,其容量与递归调用的最大层数一致。
1)最好情况:最好情况下为O( l o g 2 n log_2{n} log2n);

2)最坏情况:最坏情况下,要进行n-1次递归调用,因此栈的深度为O(n);

3)平均情况:平均情况下为O( l o g 2 n log_2{n} log2n)。

6.是否稳定:在划分算法中,若右端区间有两个关键字相同,且均小于基准值的记录,则在交换到左端区间后,它们的相对位置会发生变化,即快速排序是一种不稳定的排序算法。

7.适用性:涉及到给定数组下标找相应的数组元素的问题,所以只适用于顺序存储的线性表。

8.C语言代码

void QuickSort(ElemType A[],int low,int high){
	if(low<high){ //递归跳出的条件 
		//Partition()是划分操作,将表A划分为满足上述条件的两个子表
		int p=Partition(A,low,high); //划分
		QuickSort(A,low,p-1); //依次对两个子表进行递归排序
		QuickSort(A,p+1,high); 
	}
}

int Partition(ElemType A[],int low,int high){ //一趟划分 
	ElemType pivot=A[low]; //将表中第一个元素设置为枢轴,对表进行划分
	while(low<high){ //循环跳出条件 
		while(low<high&&A[high]>=pivot)	--high;
		A[low]=A[high]; //将比枢轴元素小的移动至左端 
		while(low<high&&A[low]<=pivot)	++low;
		A[high]=A[hlow]; //将比枢轴元素大的移动至右端 
	} 
	A[low]=pivot; //枢轴元素存放至最终位置 
}

三、选择排序

(一)简单选择排序

算法思想::每一趟(如第i趟)在后面n-i+1(i=1,2,⋯,n-1)个待排序元素中选取关键字最小的元素,作为有序子序列的第i个元素,直到第n-1趟做完,待排序元素只剩下1个,就不用再选。

1.排序过程:假设排序表为L[1⋯n],第i趟排序即从L[i⋯n]中选择关键字最小的元素与L(i)交换,每一趟排序可以确定一个元素的最终位置,这样经过n-1趟排序就可使得整个排序表有序。

2.举个例子
在这里插入图片描述

3.算法特点:排序过程中前面部分或者后面部分的元素是有序的。

4.时间复杂度:从上述伪码中不难看出,在简单选择排序过程中,元素移动的操作次数很少,不会超过3(n-1)次,最好的情况是移动0次,此时对应的表已经有序;但元素间比较的次数与序列的初始状态无关,始终是n(n-1)/2次,因此时间复杂度始终是O(n2)。

5.空间复杂度:仅使用常数个辅助单元,所以空间效率为O(1)。

6.是否稳定: 在第i趟找到最小元素后,和第i个元素交换,可能会导致第i个元素与含有相同关键字的元素的相对位置发生改变,简单选择排序是一种不稳定的排序算法。

7.适用性:简单选择排序适用于顺序存储和链式存储的线性表,以及关键字较少的情况。

8.C语言代码

void SelectSort(ElemType A[],int n){
	for(i=0;i<n-1;i++){ //共进行n-1min=i; //记录最小元素位置 
		for(j=i+1;j<n;j++){ //在A[]中选择最小的元素 
			if(A[j]<A[min])	min=j; //更新最小元素位置 
		}
		if(min!=i)
			swap(A[i],A[min]);
	}
}

(二)堆排序

算法思想:堆排序的思路很简单:先初始化堆,然后调整堆。

1.排序过程
1)初始化堆:将存放在L[1⋯n]中的元素从上至下按层排列成一个堆,然后从下至上依次比较左右结点并将较大元素与其对应的父结点交换((以大顶堆为例)),因为堆本身的特点,所以堆顶元素就是最大值。
2)输出堆顶元素后,通常将堆底元素送入堆顶,此时根结点已不满足大顶堆的性质,堆被破坏,将堆顶元素向下调整使其继续保持大顶堆的性质,再输出堆顶元素。如此重复,直到堆中仅剩一个元素为止。
3)如果后续再插入,先将新结点放在堆的末端,再对这个新结点向上执行调整操作。

2.举个例子
1)初始化堆:
在这里插入图片描述
2)调整堆:
在这里插入图片描述
3)插入元素:
在这里插入图片描述

3.算法特点:每趟都能确定一个元素在其最终位置。

4.时间复杂度:建堆时间为O(n),之后有n-1次向下调整操作,每次调整的时间复杂度为O(h),所以在最好、最坏和平均情况下,堆排序的时间复杂度为O( n l o g 2 n nlog_2{n} nlog2n)。

5.空间复杂度:仅使用了常数个辅助单元,所以空间复杂度为O(1)。

6.是否稳定: 进行筛选时,有可能把后面相同关键字的元素调整到前面,所以堆排序算法是一种不稳定的排序算法。

7.适用性:堆排序仅适用于顺序存储的线性表。

8.C语言代码

void HeadAdjust(ElemType A[],int k,int len){
	//函数将k为根的子树进行调整
	A[0]=A[k]; //A[0]暂存子树的根节点
	for(i=2*k;i<=len;i*=2){
		if(i<len&&A[i]<A[i+1]){
			i++; //取key较大的子结点的下标 
		}
		if(A[0]>=A[i])	break; //筛选结束
		else{
			A[k]=A[i]; //将A[i]调整到双亲结点上
			k=i; 
		} 
	} 
	A[k]=A[0]; //将筛选结点的值放入最终位置 
}

void BuildMaxHeap(ElemType A[],int len){
	for(int i=len/2;i>0;i--){ //从i=[n/2]--1,反复调整堆 
		HeadAdjust(A,i,len);
	}
}

void HeapSort(ElemType A[],int len){
	BuildMaxHeap(A,len); //初始建堆
	for(i=len;i>1;i--){ //n-1趟的交换和建堆的过程 
		Swap(A[i],A[1]); //输出堆顶元素(和堆底元素交换) 
		HeadAdjust(A,1,i-1); //调整,把剩余的i-1个元素整理成堆 
	} 
}

四、归并排序

算法思想:归并排序与上述基于交换、选择等排序的思想不一样,归并的含义是将两个或两个以上的有序表合并成一个新的有序表。

1.排序过程:假定待排序表含有n个记录,则可将其视为n个有序的子表,每个子表的长度为1,然后两两归并,得到「n/2]个长度为2或1的有序表;继续两两归并⋯⋯如此重复,直到合并成一个长度为n的有序表为止,这种排序算法称为二路归并排序。

2.举个例子
在这里插入图片描述

3.算法特点:逐段有序。

4.时间复杂度:每趟归并的时间复杂度为O(n),共需进行「log?n]趟归并,因此算法的时间复杂度为O( n l o g 2 n nlog_2{n} nlog2n)。

5.空间复杂度:Merge()操作中,辅助空间刚好为n个单元,因此算法的空间复杂度为O(n)。

6.是否稳定: 由于 Merge()操作不会改变相同关键字记录的相对次序,因此二路归并排序算法是一种稳定的排序算法。

7.适用性:归并排序适用于顺序存储和链式存储的线性表。

8.C语言代码

int *B=(int *)malloc(n*sizeof(int)); //辅助数组B

//A[low...mid]和A[mid+...high]各自有序,将两个部分归并
void Merge(int A[],int low,int mid,int high){
	int i,j,k;
	for(k=low;k<high;k++)
		B[k]=A[k]; //将A中所有元素复制到B中
	for(i=low,j=mid+1,k=i;i<mid&&j<=high;k++){
		if(B[i]<=B[j])
			A[k]=B[i++]; //将较小值复制到A中
		else
			A[k]=B[j++]; 
	} 
	while(i<=mid) A[k++]=B[i++];
	while(j<=high) A[k++]=B[j++];
} 

void MergeSort(int A[],int low,int high){
	if(low<high){
		int mid=(low+high)/2; //从中间划分
		MergeSort(A,low,mid); //对左半部分进行归并排序
		MergeSort(A,mid+1,high); //对右半部分进行归并排序
		Merge(A,low,mid,high); //归并 
	}
}

五、基数排序

算法思想:基数排序是一种很特别的排序算法,它不基于比较和移动进行排序,而基于关键字各位的大小进行排序。基数排序是一种借助多关键字排序的思想对单逻辑关键字进行排序的方法。

1.排序过程
在这里插入图片描述

2.举个例子
在这里插入图片描述
在这里插入图片描述

3.算法特点:逐位有序。

4.时间复杂度:基数排序需要进行d趟“分配”和”收集”操作。一趟分配需要遍历所有关键字,时间复杂度为O(n);一趟收集需要合并r个队列,时间复杂度为O®。因此基数排序的时间复杂度为O(d(n+r)),它与序列的初始状态无关。

5.空间复杂度:一趟排序需要的辅助存储空间为r(r个队列:r个队头指针和r个队尾指针),但以后的排序中会重复使用这些队列,所以基数排序的空间复杂度为O®。

6.是否稳定: 每一趟分配和收集都是从前往后进行的,不会交换相同关键字的相对位置,因此基数排序是一种稳定的排序算法。

7.适用性:基数排序适用于顺序存储和链式存储的线性表。

8.C语言代码

void RadixSort(int* arr, int n){
	//max为数组中最大值
	int max = arr[0];
	int base = 1;

	//找出数组中的最大值
	for (int i = 0; i < n; i++){
		if (arr[i] > max){
			max = arr[i];
		}
	}
	//循环结束max就是数组最大值

	//临时存放数组元素的空间
	int* tmp = (int*)malloc(sizeof(int)*n);

	//循环次数为最大数的位数
	while (max / base > 0){
		//定义十个桶,桶里面装的不是数据本身,而是每一轮排序对应(十、白、千...)位的个数
		//统计每个桶里面装几个数
		int bucket[10] = { 0 };
		for (int i = 0; i < n; i++){
			//arr[i] / base % 10可以取到个位、十位、百位对应的数字
			bucket[arr[i] / base % 10]++;
		}
		//循环结束就已经统计好了本轮每个桶里面应该装几个数

		//将桶里面的元素依次累加起来,就可以知道元素存放在临时数组中的位置
		for (int i = 1; i < 10; i++){
			bucket[i] += bucket[i - 1];
		}
		//循环结束现在桶中就存放的是每个元素应该存放到临时数组的位置

		//开始放数到临时数组tmp
		for (int i = n - 1; i >= 0; i--){
			tmp[bucket[arr[i] / base % 10] - 1] = arr[i];
			bucket[arr[i] / base % 10]--;
		}
		//不能从前往后放,因为这样会导致十位排好了个位又乱了,百位排好了十位又乱了

		//把临时数组里面的数拷贝回去
		for (int i = 0; i < n; i++){
			arr[i] = tmp[i];
		}
		base *= 10;
	}
	free(tmp);
}

六、计数排序

算法思想:计数排序也是一种不基于比较的排序算法。计数排序的思想是:对每个待排序元素x,统计小于x的元素个数,利用该信息就可确定x的最终位置。

1.排序过程:假设输入是一个数组 A[n],序列长度为 n,我们还需要两个数组:B[n]存放输出的排序序列,c[k]存储计数值。用输入数组A中的元素作为数组c的下标(索
引),而该元素出现的次数存储在该元素作为下标的数组c中。

2.举个例子
在这里插入图片描述

3.算法特点:计数排序适用于序列中的元素是整数。

4.时间复杂度:上述代码的第1个和第3个 for循环所花的时间为O(k),第2个和第4个 for 循环所花的时间为O(n),总时间复杂度为O(n+k)。因此,当k=O(n)时,计数排序的时间复杂度为O(n);但当k>O(nlogn)时,其效率反而不如一些基于比较的排序(如快速排序、堆排序等)。

5.空间复杂度:计数排序是一种用空间换时间的做法。输出数组的长度为n;辅助的计数数组的长度为k,空间复杂度为O(n+k)。若不把输出数组视为辅助空间,则空间复杂度为O(k)。

6.是否稳定: 相同元素在输出数组中的相对位置不会改变,因此计数排序是一种稳定的排序算法。

7.适用性:计数排序更适用于顺序存储的线性表。计数排序适用于序列中的元素是整数且元素范围(0~k-1)不能太大,否则会造成辅助空间的浪费。

8.C语言代码

void CountSort(ElemType A[],ElemType B[],int n,int k){
    int i,c[k];
    for(i=0;i<k;i++){
        c[i]=0; //初始化计数数组
    } 
    for(i=0;i<n;i++){ //遍历输入数组,统计每个元素出现的次数
        C[A[i]]++; //c[A[i]]保存的是等于A[i]的元素个数
    }
    for(i=1;i<k;i++){
        c[i]=C[i]+C[i-1]; //C[x]保存的是小于或等于x的元素个数
    }
    for(i=n-1;i>=0;i--){ //从后往前遍历输入数组
        B[C[A[i]-1]]=A[i]; //将元素A[i]放在输出数组 B[]的正确位置上
        C[A[i]]=C[A[i]]-1;
    }
}

七、外部排序

算法思想:上述排序算法都是内部排序,而在许多应用中,经常需要对大文件进行排序,因为文件中的记录很多,无法将整个文件复制进内存中进行排序。因此,需要将待排序的记录存储在外存上,排序时再把数据一部分一部分地调入内存进行序,在排序过程中需要多次进行内存和外存之间的交换。这种排序算法就称为外部排序。

1.排序过程:外部排序通常采用归并排序算法。它包括两个阶段:①根据内存缓冲区大小,将外存上的文件分成若干长度为C的子文件,依次读入内存并利用内部排序算法对它们进行排序,并将排序后得到的有序子文件重新写回外存,称这些有序子文件为归并段或顺串;②对这些归并段进行逐趟归并,使归并段(有序子文件)逐渐由小到大,直至得到整个有序文件为止。

为减少平衡归并中外存读/写次数以提高排序速度所采取的方法:增大归并路数和减少归并段个数
①利用败者树增大归并路数;

②利用置换-选择排序增大归并段长度来减少归并段个数;

③由长度不等的归并段进行多路平衡归并,需要构造最佳归并树

关于提高外部排序速度的三种方式,我将在下一篇文章中说明:【408精华知识】提高外部排序速度的三种方式

3.时间效率:文件通常是按块存储在磁盘上的,操作系统也是按块对磁盘上的信息进行读/写的。因为磁盘读/写的机械动作所需的时间远远超过在内存中进行运算的时间(相比而言可以忽略不计),因此在外部排序过程中的时间代价主要考虑访问磁盘的次数,即 I/O 次数。

小总结
1)我们大致可以发现,与二叉树、折半有关的排序,其时间复杂度都有log,这可以帮助我们记忆各种排序的时间复杂度。
2)在排序过程中,每趟都能确定一个元素在其最终位置的有冒泡排序、简单选择排序、堆排序、快速排序,其中前三者能形成全局有序的子序列,后者能确定枢轴元素的最终位置。
3)内部排序(在内存中进行的排序)包括插入、交换、选择、归并、基数、计数。

写在后面

这个专栏主要是我在学习408真题的过程中总结的一些笔记,因为我学的也很一般,如果有错误和不足之处,还望大家在评论区指出。希望能给大家的学习带来一点帮助,共同进步!!!

参考文献
[1]王道408教材(2025版)

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

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

相关文章

基于springboot+mybatis+vue的项目实战之页面参数传递

如图所示&#xff0c;删除操作可以用按钮实现&#xff0c;也可以用超链接来实现。 1、第一种情况&#xff0c;用按钮实现。 html页面相关&#xff1a; <button type"button" click"deleteId(peot.id)">删除</button> <script>new Vue(…

centos7中如何全局搜索一下nginx的配置文件?

在CentOS 7中搜索Nginx的配置文件&#xff0c;你可以使用一些常用的命令行工具&#xff0c;比如find、grep等。这些工具可以帮助你在文件系统中查找文件&#xff0c;也可以用来查找Docker容器内部的文件&#xff0c;只要你知道如何访问容器的文件系统。 1. 搜索系统中的Nginx配…

Vue Excel 文件流导出乱码快速解决方案

今日在开发一个导出功能&#xff0c;原本一个非常简单的功能&#xff0c;却没想里面藏了陷阱&#xff01; 背景 前端导出的文件流乱码&#xff0c;此时确定非后端问题&#xff08;可以在postman导出是否正常来判断&#xff09;。 前端导出&#xff1a; 后端正常数据&#xf…

产品推荐 | 基于AMD Virtex 7 FPGA VC709 的高速连接功能开发板

01 产品概述 Virtex™ 7 FPGA VC709 连接功能套件是一款速率为 40Gb/s 的高速平台&#xff0c;您可以通过评估和开发连接功能&#xff0c;迅速为包含所有必要软硬件和 IP 核的高带宽和高性能应用提供强大的支持。它包括一个含有 PCI Express Gen 3、Northwest Logic 公司推出的…

FFmpeg常用API与示例(三)—— 音视频解码与编码

编解码层 1.解码 (1) 注册所有容器格式和 CODEC:av_register_all() (2) 打开文件:av_open_input_file() (3) 从文件中提取流信息:av_find_stream_info() (4) 穷举所有的流&#xff0c;查找其中种类为 CODEC_TYPE_VIDEO (5) 查找对应的解码器:avcodec_find_decoder() (6) …

【Arduino】数字I/O的使用

目录 1、引脚工作模式 2、写入引脚digitaWrite&#xff08;&#xff09; 3、读取引脚digitalRead(pin); 4、示例 跑马灯 1、引脚工作模式 Arduino通过pinMode()设置引脚的io工作模式&#xff0c;一共有4种模式 工作模式 Mode 说明 输出模式 OUTPUT 引脚为低阻抗状态&…

MQTT学习(二)

订阅主题和订阅确认 SUBSCRIBE——订阅主题 之前的CONNECT报文&#xff0c;分为 固定报头&#xff1a;必须存在&#xff0c;用于描述报文信息。里面有指出什么类型的报文&#xff0c;报文的等级。可变报头&#xff1a;不一定存在。主要看什么样子类型的报文。有效载荷部分&a…

OSU micro-benchmarks安装测试指导

OSU micro-benchmarks安装测试指导 OSU micro-benchmarks工具介绍 OSU Micro benchmark工具是由Ohio State University提供的MPI&#xff08;Message Passing Interface&#xff0c;消息传递接口&#xff09;通信效率评测工具。该工具旨在通过执行不同模式的MPI操作&#xff…

力扣75. 颜色分类

Problem: 75. 颜色分类 文章目录 题目描述思路及解法复杂度Code 题目描述 思路及解法 由于题目只提供0&#xff0c;1&#xff0c;2分别代表颜色红、白、蓝&#xff0c;并按此排序&#xff0c;那么我们可以遍历两次数组&#xff0c;第一次将0&#xff0c;全部放到数组前面一部分…

Rumor Remove Order Strategy on Social Networks

ABSTRACT 谣言被定义为广泛传播且没有可靠来源支持的言论。现代社会&#xff0c;谣言在社交网络上广泛传播。谣言的传播给社会带来了巨大的挑战。 “假新闻”故事可能会激怒您的情绪并改变您的情绪。有些谣言甚至会造成社会恐慌和经济损失。因此&#xff0c;谣言的影响可能是深…

计算机网络5——运输层4TCP拥塞控制

文章目录 一、拥塞控制的一般原理二、举例三、理解四、TCP 的拥塞控制方法1、慢开始和拥塞避免 五、主动队列管理AOM1、背景2、介绍3、实现 一、拥塞控制的一般原理 在计算机网络中的链路容量(即带宽)、交换节点中的缓存和处理机等都是网络的资源。在某段时间&#xff0c;若对…

基于MetaGPT的LLM Agent学习实战(一)

前言 我最近一直在做基于AI Agent 的个人项目&#xff0c; 因为工作加班较多&#xff0c;设计思考时间不足&#xff0c;这里借着Datawhale的开源学习课程《MetaGPT智能体理论与实战》课程&#xff0c;来完善自己的思路&#xff0c;抛砖引玉&#xff0c;和各位开发者一起学习&am…

uniapp编译H5解决ios的border-radius失效问题,以及ios满屏显示不全的问题

1.解决方案 .card-itemA {width: 650rpx;height: 326rpx;box-shadow: 0rpx 0rpx 30rpx 14rpx rgba(236, 235, 236, 0.25);background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0) 100%);border-radius: 60rpx;overflow: hidden;// 兼容ios的圆角问题transfor…

5.13作业

使用消息队列实现的2个终端之间的互相聊天 并使用信号控制消息队列的读取方式&#xff1a; 当键盘按ctrlc的时候&#xff0c;切换消息读取方式&#xff0c;一般情况为读取指定编号的消息&#xff0c; 按ctrlc之后&#xff0c;指定的编号不读取&#xff0c;读取其他所有编号的…

计算机毕业设计hadoop+spark+hive知识图谱bilibili视频数据分析可视化大屏 视频推荐系统 预测系统 实时计算 离线计算 数据仓库

研究意义 随着互联网的快速发展&#xff0c;人们面临着海量的视频内容&#xff0c;如何从这些繁杂的视频中找到自己感兴趣的内容成为一个重要的问题[1]。推荐系统作为一种解决信息过载问题的重要工具&#xff0c;能够根据用户的历史行为和偏好&#xff0c;预测用户可能感兴趣的…

【错误的集合】力扣python

最初想法 def findErrorNums(nums):n len(nums)duplicate -1missing -1for num in nums:if nums[abs(num) - 1] < 0:duplicate abs(num)else:nums[abs(num) - 1] * -1for i in range(n):if nums[i] > 0:missing i 1breakreturn [duplicate, missing] 遇到力扣大佬…

废品回收小程序,推动回收行业数字化发展

在垃圾分类、资源回收利用的时代背景下&#xff0c;废品回收行业迅速成长&#xff0c;市场规模逐渐扩大&#xff01; 随着“互联网”应用的普及&#xff0c;废品回收行业也进入到了数字化回收领域&#xff0c;各大回收行业开始专注于发展智能回收。此外&#xff0c;线上废品回…

JavaEE技术之SpringCloud(Nacos注册中心、Nacos配置中心、Sentinel实现熔断与限流)

文章目录 SpringCloud Alibaba1、简介1.1 背景1.2 Nacos主要功能1.3 Nacos和SpringBoot、SpringCloud版本选择 2、Nacos注册中心2.1 案例准备2.2 Nacos注册中心下载启动2.2.1 下载2.2.2 解压启动2.2.3 nacos-server访问测试 2.3 nacos注册中心客户端整合2.3.1 订单服务整合naco…

Linux---windows 机器和远端的 Linux 机器如何通过 XShell 传输文件

一、关于rzsz 这个工具用于 windows 机器和远端的 Linux 机器通过 Xshell 传输文件. 二、下载rzsz软件 用root输入命令&#xff1a; sudo yum install -y lrzsz下载完成&#xff1a; 三、如何传输 有图形化界面 1、从Windows机器传输给远端Linux机器 ① 直接拖拽 直接将…

虚拟机有线已连接但无法上网—·可能性之一

背景 VMware虚拟机&#xff0c;搭建了三台Linux服务器&#xff0c;组成Hadoop集群&#xff0c;由于在Hadoop102上有一些经常与Mysql数据库交互的任务&#xff0c;需要经常打开运行&#xff0c;而Hadoop103和104则经常处于关闭状态&#xff0c;一段时间后再次启动集群时候&…