C C++最新八大排序--高质量总结 干净又卫生_干净的数字排列(1),25岁成功入职阿里P7的小哥哥告诉你

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

O

(

N

l

o

g

2

N

)

O(N*log_2 ^N)

O(N∗log2N​)
这时候我们再通过100000个数的伪随机数来试试看排序的速度(单位是毫秒)

效果很明显

image-20211205220552877

image-20211206111256120

3. 交换排序

3.1 冒泡排序

3.1.1 基本想法

起源于水里的气泡随着上升,越来越大

在这里插入图片描述

3.1.2 实现冒泡排序
  • 问题拆解
    1. 单趟
    2. 多趟

单趟

	for (int i = 1; i < sz; ++i)
	{
		if (a[i-1] > a[i])
		{
			Swap(&a[i], &a[i - 1]);
		}
	}

多趟

void BubbleSort(int\* a, int sz)
{
	for (int j = 0; j < sz; ++j)
	{
		for (int i = 1; i < sz-j; ++i)
		{
			if (a[i - 1] > a[i])
			{
				Swap(&a[i], &a[i - 1]);
			}
		}
	}
}

优化

创建一个优化的标志,某一次如果没有交换,直接跳出循环,因为已经有序

void BubbleSort(int\* a, int sz)
{
	for (int j = 0; j < sz; ++j)
	{
		int exchange = 0;
		for (int i = 1; i < j; ++i)
		{
			if (a[i - 1] > a[i])
			{
				Swap(&a[i], &a[i - 1]);
				exchange = 1;
			}
		}
		if (exchange == 0)
		{
			break;
		}
	}
}

3.1.3 分析冒泡复杂度
最好与最坏复杂度发生情况
最坏的情况O(N2)逆序的一个数组
最好的情况O(N)顺序有序的数组

看上去好像和插入排序是一样的,那么到底谁更好呢?

顺序有序的话,一样好

接近有序的话,插入好

3.2.快速排序

注:本图片来源于https://commons.wikimedia.org

快排,如其名说明很快,且了解后发现其变形多

3.2.1 基本想法(Hoare)

Hoare的实现

  • 单趟排序

选出一个key,一般是最左边的或者是最右边的,key放到正确的位置去,左边比key要小,右边的比key要大

这里看图来举个例子:

image-20211206173207110

假设我们把这个数组里面的最左边的6选作为key让right先走

其中right找小数,left找大数,目的其实是为了让左边的比key小右边的比key大

找完之后互相交换

image-20211206173753284

继续找,继续交换

image-20211206173840027

直到相遇,发现该值比key小,交换一下

image-20211206181349075

这样的话就走完一趟了

  • 这里有人会提出疑问
  1. 我们在之前为什么要求让right先走?
  2. 那如果我发现相遇的时候的值比key大怎么办?

其实我们让right先走这样设置的目的就是为了防止相遇的时候的值比key大的情况产生,就能保证始终比key小了

image-20211206181257621

当走到最后一步时,如果先走的是left而不是right的话,left在找大的时候直接略过了3,使得相遇的时候的数字变成9,那最后一步要让比key小的数字和key互换就实现不了,所以最左边数字为key的时候,要让右边的right先走 。也正是右边先走才能使得两种相遇形式,即左遇右(右边先找到小数就停,左边没找到继续走使得相遇)和右遇左(右边找不到小数直接走使得和左所在位置相遇),这两种情况都会使right找到小的(仔细画图分析)

接下来,我们来完成多趟

在确定了之前的key之后我们把中间的现在放着key的左右两边拆开,就像分成左子树和右子树一样

image-20211206184029563

对左一段和右一段分别重复上述操作,左边和右边就成了这样

image-20211206184434997

继续如上操作

发现已经是有序的了

image-20211206184658660

当最后只剩下一个的时候就是有序的

3.2.2 实现快速排序

对于单次排序,我们需要排除一种可能,也就是顺序数组可能会使得right一直找不到,使得right最后越界也没有完成任务

开始进入的时候while (left<right),并不能排除right越界,要在找大和找小的时候再判断一次

while (left<right&&a[right] > a[keyi])
while (left<right&&a[left]<a[keyi])

image-20211206200655936

还要注意在找大和找小的时候=不能忘记,如果忘记就会使得当left和right遇到同样大的时候不++和–,最后直接交换陷入死循环,所以找大别带上等于,找小也别带上等于

	while (left<right&&a[right] >= a[keyi])
	while (left < right&&a[left]<=a[keyi])

image-20211206201816417

所以细节很多,最后正确的单趟应该是这样

void QuickSort(int\* a, int sz)
{
	int left = 0, right = sz - 1;
	int keyi = left;
	while (left<right)
	{
		//找大
		while (left<right&&a[right] >= a[keyi])
		{
			--right;
		}
		//找小
		while (left < right&&a[left]<=a[keyi])
		{
			++left;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[keyi], &a[left]);//最后交换一下key的值和相遇的值
}

接下来完成多趟,完整实现

首先为了要递归,那么函数的参数肯定还用sz是不合适的,那么改成start和end,同时增加一个递归终止条件

那么这样就是交换版的快速排序

void QuickSort(int\* a, int begin,int end)
{
	if (begin >= end)
	{
		return;
	}
	int left = begin, right = end;
	int keyi = left;
	while (left<right)
	{
		//找大
		while (left<right&&a[right] >= a[keyi])
		{
			--right;
		}
		//找小
		while (left < right&&a[left]<=a[keyi])
		{
			++left;
		}
		Swap(&a[left], &a[right]);
	}
	int meeti = left;
	Swap(&a[keyi], &a[left]);//最后交换一下key的值和相遇的值
	//左[begin,meeti-1] 右[meeti+1,end]
	QuickSort(a, begin, meeti - 1);
	QuickSort(a, meeti+1, end);
}

那么这个思想就是类似于二叉树的–“分治”排序

3.2.3 实现多种方法

这里我们把单趟排序抽离出来,将单趟返回参数来递归,那么

PartSort(a, begin, end)其实是由多种写法的,接下来分析多种写法

首先这个本体是不变的

void QuickSort(int\* a, int begin,int end)
{
	if (begin >= end)
	{
		return;
	}
	int keyi = PartSort(a, begin, end);//传key值
	//左[begin,meeti-1] 右[meeti+1,end]
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi+1, end);
}

3.2.3.1 Hoare法(左右指针法)

这里我们称之前所讲的方法为Hoare法,或许命名可能是最早发现这个方法的人

int PartSort1(int\* a, int left, int right)
{
	int keyi = left;
	while (left < right)
	{
		//找大
		while (left < right && a[right] >= a[keyi])
		{
			--right;
		}
		//找小
		while (left < right && a[left] <= a[keyi])
		{
			++left;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[keyi], &a[left]);//最后交换一下key的值和相遇的值
	return left;
}

试试看其他几种方法实现单趟排序

3.2.3.2 挖坑法
  1. 还是取出left的值用key保存,比如说下图的6
  2. 这里不同的点在于当前left所指向的变成了一个坑

image-20211206221615831

  1. 同样的还是右边的right开始走,找小,找到小数之后这次不一样了,不是交换,而是把right的值放到坑里面,于是形成了新的坑

image-20211206222417444

  1. 再然后left找大,填到坑里面,再产生新坑

image-20211206222451890

  1. 最后会相遇,然后放进去key就好了

image-20211206222717080

image-20211206223113656

int PartSort2(int\* a, int left, int right)
{
	int key = a[left];
	while (left < right)
	{//找小
		while (left < right && a[right] >= key)
		{
			--right;
		}
		//放到左边的坑中,右边形成新的坑
		a[left] = a[right];
		//找大
		while (left < right && a[left] <= key)
		{
			++left;
		}
		//放到右边的坑中,左边形成新的坑
		a[right] = a[left];
	}
	a[left] = key;
	return left; 
}

那么这就是挖坑法

3.2.3.3 前后指针法
  1. 先是有两个指针,分别是cur和prev,开始的时候分别在一前一后

image-20211207180937635
2. 然后cur去找比key小的值,找到以后++prev,再交换prev和cur位置的值

  • 前两次的交换之后没什么变化,因为cur==prev,所以等会写代码的时候可以排除这个可能性

image-20211207181158098

  • 第三次之后看到了变化,其实观察这个操作的意义是在把大的往后放,小的往前放
    image-20211207181429109
  1. 直到cur走出数组尾之后停止

image-20211207181711640

  1. 最后把key位置和prev位置互相交换

image-20211207182002968

这样的单趟的目的是什么呢,就是使得prev左边的数都比key小,右边的数都比key大

int PartSort3(int\* a, int left, int right)
{
	int prev = left;
	int cur = left+1;
	int keyi = left;
	while (cur <= right)
	{
		if (a[cur] < a[keyi]&&++prev!=cur)
		{
			Swap(&a[left], &a[right]);
		}
		++cur;//不进或者交换之后都要往后走
	}
	Swap(&a[keyi], &a[prev]);
	return prev;//返回分隔的值 
}

3.2.4 时间复杂度测试

接下来用同样的方法,随机生成数组感受一下快排的速度

debug版在优化递归的时候不太好,性能不会太明显,建议用release来测性能

debug版 100000

image-20211206224805210

release版 1000000

image-20211206225131593

4.4.1 理想时间复杂度计算

最好的情况是:

理想的快排应该是每次的key恰巧是中位数,然后相当于二分法一样的走下去

image-20211206230129273

时间复杂度是

O

(

l

o

g

2

N

N

)

O(log_2^N*N)

O(log2N​∗N)

最坏的情况是

有序的数组,则此时每个排序都取最左边为key

image-20211206230814871

这样的时间复杂度是

O

(

N

2

)

O(N^2)

O(N2)
因此这样的话来看,我们之前写的快排太不稳定,这样不是一个合格排序,我们要对这个快排优化,让它对所有的情况都好用

于是我们可以采取

  1. 三数取中法(通过采取改进选key的方法来优化)
  2. 小区间排序优化
3.2.5 优化快排
3.2.5.1三数取中法(通过采取改进选key的方法来优化)

既然问题出在选key,那么我们使得选恰好中间的数能够最优时间复杂度,于是我们可以每次找出头尾和中间那个三个数字中的中间数,并返回给key,使key的选取变得优化

int GetMidIndex(int\* a, int left, int right)
{//找出头尾和中间那个三个数字中的中间数
	int mid = (left + right) >> 1;
	if (a[left] < a[mid]){
		if(a[mid]<a[right]){
			return mid;
		}else if(a[left]>a[right]){
			return left;
		}else{
			return right;
		}
	} else{
		if (a[mid] > a[right]){
			return mid;
		}else if (a[left] < a[right]){
			return left;
		}else {
			return right;
		}
	}
}
void QuickSort(int\* a, int begin,int end)
{
	if (begin >= end)
	{
		return;
	}
	int keyi = PartSort1(a, begin, end);
	//左[begin,meeti-1] 右[meeti+1,end]
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi+1, end);
}

3.2.5.1.1 三数取中法+Hoare
int PartSort1(int\* a, int left, int right)
{
	int midIndex = GetMidIndex(a, left, right);
	Swap(&a[left], &a[midIndex]);//到这步left里的值将会使头中尾的中间值
	int keyi = left;
	while (left < right)
	{
		//找大
		while (left < right && a[right] >= a[keyi])
		{
			--right;
		}
		//找小
		while (left < right && a[left] <= a[keyi])
		{
			++left;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[keyi], &a[left]);//最后交换一下key的值和相遇的值
	return left;
}

3.2.5.1.2 三数取中法+前后指针
int PartSort3(int\* a, int left, int right)
{
	int midIndex = GetMidIndex(a, left, right);
	Swap(&a[left], &a[midIndex]);//到这步left里的值将会使头中尾的中间值
	int prev = left;
	int cur = left+1;
	int keyi = left;
	while (cur <= right)
	{
		if (a[cur] < a[keyi]&&++prev!=cur)
		{
			Swap(&a[left], &a[right]);
		}
		++cur;//不进或者交换之后都要往后走
	}
	Swap(&a[keyi], &a[prev]);
	return prev;//返回分隔的值 
}

下图是对100000个数量的有序数组排序的比较,可以看出三数取中法有 效解决了左右指针的缺陷, 甚至当遇到顺序数组的时候效率反而最高

3.2.5.2 小区间排序优化

也就是递归型的快排,是从头至尾一直是递归的,不断左右分段,像二叉树,不过即使到了最后几个数没有顺序,都要一直递归,于是想法是最后分割到数据到10个的时候用插排或选择解决,毕竟再用希尔和堆排序还要建堆和产生gap,那么又因为之前测过的插排优于选排,所以最后一个小区间里面就使用插排

那么我们改变这个本体

void QuickSort(int\* a, int begin,int end)
{
	if (begin >= end)
	{
		return;
	}
	int keyi = PartSort(a, begin, end);//传key值
	//左[begin,meeti-1] 右[meeti+1,end]
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi+1, end);
}

void QuickSort(int\* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	//1.如果这个区间是数据多,继续选key单趟,分割子区间
	//2.如果子区间数据量太小,再去递归不合适,不划算
	if (end - begin > 100)
	{
		int keyi = PartSort3(a, begin, end);
		//左[begin,meeti-1] 右[meeti+1,end]
		QuickSort(a, begin, keyi - 1);
		QuickSort(a, keyi + 1, end);
	}
	else
	{
		InsertSort(a + begin, end - begin + 1);
	}
}

当然如果用编译器其实优化效果不是很明显,因为 编译器已经就递归做到了一些优化,但不像三数取中,优化来的好

注意:优化可以通过改变end-begin的值,具体多少要还是看数据的数量

3.2.5.3 非递归快排

递归,现代编译器优化的比较好,性能问题不是很大,最大的问题在于,递归的深度太深,程序本身没问题,但是栈空间不够,导致栈溢出,只能改非递归stack overflow警告😁

改成非递归有两种方式:

  1. 直接改循环,比如斐波那契数列
  2. 树遍历非递归和快排非递归,只能用栈模拟递归过程

接下来我们用栈来模拟递归过程

其实分治递归的思想和从栈中放入数据再取出来一样,不是递归,但是和递归很像

我们把单趟排序的区间给入栈,然后依次取栈里面的区间出来单趟排序,再循环需要处理的子区间入栈,直到栈为空。

void QuickSortNonR(int\* a, int begin, int end)
{
	Stack st;
	StackInit(&st);
	StackPush(&st, begin);
	StackPush(&st, end);

	while (!StackEmpty(&st))
	{
		int left, right;
		right = StackTop(&st);
		StackPop(&st);

		left = StackTop(&st);
		StackPop(&st);

		int keyi = PartSort1(a, left, right);
		if (left < keyi - 1)
		{
			StackPush(&st, left);
			StackPush(&st, keyi - 1);
		}

		if (keyi + 1 < right)
		{
			StackPush(&st, keyi + 1);
			StackPush(&st, right);
		}
	}
	StackDestroy(&st);
}

注:非递归方法中栈的实现就放在GitHub中,码上来显得文章冗长,所以不写上来了,那么后期也会就栈这个数据结构仔细展开

4. 归并排序

在这里插入图片描述

4.1 基本想法

先把这个区间分成两半,然后均分成两个数组进行排序,最后得出两个有序的子区间之后再归并在一起就成为了有序数组

image-20211208171230101

怎么归并?

设置左右指针两个互相比一比,取小的尾插到下面的数据,然后往后走,直到一个区间结束,再把另外一个区间尾插到结束

image-20211208172257306

但是要归并的前提是有序,如果我还是没有序怎么办?就继续拆拆拆

image-20211208172544007

拆到一个的时候就是有序的,然后归并就可以了

那么也就是说这样一个区间经过这样的过程就是有序的了

image-20211208172814824

4.2 实现归并排序

void \_MergeSort(int\* a, int left,int right,int \*tmp)
{
	if (left >= right)
		return;

	int mid = (left + right) >> 1;
	//[left,mid] [mid+1,right]
	\_MergeSort(a, left, mid, tmp);
	\_MergeSort(a, mid+1, right, tmp);
	
	//两段有序子区间归并tmp,并拷贝回去
	int begin1 = left,end1 = mid;
	int begin2 = mid + 1, end2 = right;
	int i = left;
	while (begin1<=end1&&begin2<=end2){
		if (a[begin1] < a[begin2]){
			tmp[i++] = a[begin1++];
		}else{
			tmp[i++] = a[begin2++];
		}
	}
    //把剩下一个区间走完,两个区间总有一个没走完
	while (begin1 <= end1){
		tmp[i++] = a[begin1++];
	}
	while (begin2 <= end2){
		tmp[i++] = a[begin2++];
	}
	//归并完之后,拷回原数组
	for (int j = left; j <= right; ++j)
	{
		a[j] = tmp[j];
	}
}

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

4.3 归并的复杂度

排序算法时间复杂度空间复杂度
归并排序O(N*logN)O(N)

release版海量数据排序,所需时间如下

注:这里的快排是三数取中+Hoare

100000数据

image-20211208182032545

image-20211208182259655

1000000数据

image-20211208182404118

4.4 非递归实现归并排序

类似的,我们知道递归会带给快排的问题,也会给归并排序带来,所以我们尝试用非递归方式实现归并排序

  • 想法

要一半一半分开这个区间,放入tmp中,然后再取回来放到a中,使开始的gap=1,然后每次gap翻一倍,每次以gap为大小的区间是有序的,直到最后总长一半

  • 注意事项

image-20211208214444692

  1. 随着两两划分是有可能到最后一个只有一个数,[i+gap,i+2*gap-1],也就是说第二个区间可能是不存在的

那修改一下,不存在就不要归了

image-20211208222554521
2. 还有一种可能性是第二组区间有但是不完整

image-20211208222851834
3. 还有一种可能性是第二组区间没有,第一组区间也不完整

把1和3归为一类去处理,直接不去归并了

要检查的是第二种可能,要修正一下

void MergeSortNonR(int\* a, int sz)
{
	int\* tmp = (int\*)malloc(sizeof(int) \* sz);
	if (tmp == NULL){
		printf("malloc fail\n");
		exit(-1);
	}
	int left;
	int right;
	int gap = 1;
	while (gap < sz){
		for (int i = 0; i < sz; i += (2 \* gap)){
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 \* gap - 1;
			//第二个区间不存在就不需要归并了,结束循环
			if (begin2>=sz){
				break;
			}
			//第二个小区间存在,但是第二个小区间不够gap个,结束位置越界,需要修正
			if (end2 >= sz){
				end2 = sz - 1;
			}
			//[i,i+gap-1] [i+gap,i+2\*gap-1]
			left = begin1;
			right = end2;
			int index = begin1;
			while (begin1 <= end1 && begin2 <= end2) {
				if (a[begin1] < a[begin2]) {
					tmp[index++] = a[begin1++];
				}
				else {
					tmp[index++] = a[begin2++];
				}
			}
			//走到这里一定有一边没有走完
			while (begin1 <= end1) {
				tmp[index++] = a[begin1++];
			}
			while (begin2 <= end2) {
				tmp[index++] = a[begin2++];
			}
			//归并完之后,拷回原数组
			for (int j = left; j <=right; ++j)
			{
				a[j] = tmp[j];
			}
		}
		gap \*= 2;
	}
	free(tmp);
	tmp = NULL;
}

4.5 补充

我们这里要提到外排序和内排序,如何对大数据文件进行排序

image-20211208225649476

归并排序天然支持外排序

  • 举个栗子:

假如我们有10亿个整数在文件中,需要排序,计算后大小约等于4个G,不能直接放在内存中运算假如我们的内存是0.5G

  1. 我们可以对文件每次读1/8,也就是512M到内存中去排序(这里的排序不能用归并,因为归并空间复杂度O(N),可以用快排),然后写到文件里,再继续重复这个过程,最后将这几个有序小文件分别读数进行归并,两两归就成为4个,再两两归,两两归,最后合成一个
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fa7WQ30Y-1639028292067)(…/…/…/…/AppData/Roaming/Typora/typora-user-images/image-20211209085241562.png)]
  1. 第二种可能性是第一个文件和第二个文件归并,然后第二个和第三个文件归并大文件,最后使得

image-20211209085428177

5. 计数排序

计数排序有成为鸽巢原理,是对哈希直接定址法的变形应用

a

之前的排序都是比较大小来进行排序的,计数排序采取一种新思路,他的思想是

  1. 统计相同元素出现个数
  2. 根据统计结果将序列回收到原来的序列中

5.1 基本想法

如何统计每个数的次数
这里我们有一个A[i]数组,A[i]的值就是对Count数组该位置++

for(int i-0;i<sz;++i)
{
	++Count[A[i]];
}

image-20211209091009653
然后根据count数组按照count数组的数据的下标和值是多少,产生相应值数量的下标区间
这个叫做绝对映射
注:
image-20211209091544170
加入我给了如上几个数字,有一半多都没有映射,那我不可能去浪费0-9存空
所以我为什么不把下标为0的保存10然后下标为5的保存15,跳过1-9呢?
这就叫相对映射

那么因为绝对映射还是存在浪费的,所以在这里的计数排序我们采用相对映射

5.2 实现计数排序

void CountSort(int\* a, int sz)
{
	int max = a[0], min = a[0];
	for (int i = 0; i < sz; ++i)
	{
		if (a[i] > max)
		{
			max = a[i];
		}
		if (a[i] < min)
			min = a[i];
	}
	int range = max - min + 1;
	int\* count = (int \*)malloc(sizeof(int) \* range);
	memset(count, 0, sizeof(int) \* range);
	for (int i = 0; i < sz; i++)
	{
		count[a[i] - min]++;
	}
	int i = 0;
	for (int j = 0; j < range; ++j)
	{
		while (count[j]--)
		{
			a[i++] = j+min;
		}
	}
}

5.3 时间复杂度分析

计数排序的复杂度

排序名时间复杂度空间复杂度
计数排序O(N+range)O(range)

所以说其实是和range有关的,最好是这组数据中,数据的范围都比较集中,很适合用计数排序,这个排序还是很优秀的,也有局限性,该排序只适合整数,浮点数字符串都不行,数据不集中也不行

6. 分析八大排序

6.1 八大排序分析

img

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

39e5e8ef8a5ae2.png)
加入我给了如上几个数字,有一半多都没有映射,那我不可能去浪费0-9存空
所以我为什么不把下标为0的保存10然后下标为5的保存15,跳过1-9呢?
这就叫相对映射

那么因为绝对映射还是存在浪费的,所以在这里的计数排序我们采用相对映射

5.2 实现计数排序

void CountSort(int\* a, int sz)
{
	int max = a[0], min = a[0];
	for (int i = 0; i < sz; ++i)
	{
		if (a[i] > max)
		{
			max = a[i];
		}
		if (a[i] < min)
			min = a[i];
	}
	int range = max - min + 1;
	int\* count = (int \*)malloc(sizeof(int) \* range);
	memset(count, 0, sizeof(int) \* range);
	for (int i = 0; i < sz; i++)
	{
		count[a[i] - min]++;
	}
	int i = 0;
	for (int j = 0; j < range; ++j)
	{
		while (count[j]--)
		{
			a[i++] = j+min;
		}
	}
}

5.3 时间复杂度分析

计数排序的复杂度

排序名时间复杂度空间复杂度
计数排序O(N+range)O(range)

所以说其实是和range有关的,最好是这组数据中,数据的范围都比较集中,很适合用计数排序,这个排序还是很优秀的,也有局限性,该排序只适合整数,浮点数字符串都不行,数据不集中也不行

6. 分析八大排序

6.1 八大排序分析

img

[外链图片转存中…(img-kgWNHfZg-1715728042332)]
[外链图片转存中…(img-o7Vnb9Pb-1715728042332)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值