C/C++中四种排序算法的时间空间复杂度

C/C++中四种排序算法的时间空间复杂度

一.浅谈时间复杂度和空间复杂度

1.概念:

时间复杂度:就是说执行算法需要消耗的时间长短,越快越好。
空间复杂度:就是说执行当前算法需要消耗的存储空间大小,也是越少越好。

2.时间复杂度:

1.表示方法:

一般用“大O符号表示法”来表示时间复杂度:T(n) = O(f(n)),n是影响复杂度变化的因子,f(n)是复杂度具体的算法。

1-1.循环次数最多原则:
	当n变得越来越大时,公式中的低阶,常量,系数三部分影响不了其增长趋势,可以直接忽略他们,
	只记录一个最大的量级就可以了。计算时间复杂度时,只需关注循环次数最多的那段代码即可。
	int sumFunc(int n) {
	int sum = 0; //执行1次,忽略不计
	for (int i = 0; i < n; i++) {
	sum += i; // 循环内执行次数最多,执行次数为n次,因此时间复杂度记为O(n)
	}
	return sum; //执行1次,忽略不计
	}
1-2.加法原则:
for (int i = 0; i < n; i++) {
sum += i; //执行n次 O(n)
}
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++) {
sum += i; //执行n*n次 O(n*n)
}
}
上述两断代码的时间复杂度分别为 O(n)和O(n*n),T(n)=O(n)+O(n*n),但我们只取最大的量级即O(n*n)
量级最大的那段代码时间复杂度=总的时间复杂度
1-3.乘法原则
嵌套代码的复杂度等于嵌套内外代码复杂度的乘积
int sum = 0;
for (int i = 0; i < n; i++) 
{            
for (int j = 0; j < n; i++); //执行n次
{
sum += 1; //执行n次
}
}
因此这段代码时间复杂度为O(n) * O(n) = O(n*n) = O(n*n)
2.常见的时间复杂度量级
一丶常数阶O(1)  eg:
int a=1;
int b=2;
int c=3;
各执行一次,时间复杂度为 O(1);
for (int i = 0; i < 100; i++) {
printf("hello"); //执行一百次,也是常量级,记为O(1)
}

大O符号表示法并不是用于来真实代表算法的执行时间的,它是用来表示代码执行时间的增长变化趋势的。
上面的算法并没有随着某个变量的增长而增长,所以无论多长,时间复杂度都为O(1).


二丶线性阶O(n)  eg :
for(int i=0;i<n;i++)
{
    
}
上面的循环要执行N次,所以时间复杂度为O(n)。


对数阶O(logN)
int a= 1;
while(a < n) 
{
    a = a * 2;
}
执行x次之后 x=log2(n),把2换成其他数也是一样(换底公式:loga(b)*logc(a)=logc(b))
所以时间复杂度为O(logN)


三丶线性对数阶O(nlogN)

for(int i=0;i<n;i++)
{
while(a<n)
{
i=i*2;
}
}
for循环嵌套while循环 即时间复杂度为 O(n)*O(logN)  O(nlogN)


四丶平方阶O(n²)
for (int i = 0; i < n; i++) 
{            
for (int j = 0; j < n; i++); //执行n次
{
}
}

两个for循环嵌套

此时的时间复杂度为O(n*n).

3.空间复杂度:

空间复杂度是对一个算法在运行过程中临时占用存储空间大小的一个量度,同样反映的是一个趋势,
S(n)= O(f(n)),其中,n为问题的规模,f(n)为语句关于n所占存储空间的函数。

空间复杂度比较常用的有:O(1)、O(n)

O(1)
	int i = 1;
	int j = 2;
	++i;
	j++;
	int m = i + j;

代码中的 i、j、m 所分配的空间都不随着处理数据量变化,因此它的空间复杂度 S(n) = O(1)

O(n)
	int[] m = new int[n]
	for(i=1; i<=n; ++i)
	{
	   j = i;
	   j++;
	}

这段代码中,第一行new了一个数组出来,这个数据占用的大小为n,
这段代码的2-6行,虽然有循环,但没有再分配新的空间,
因此,这段代码的空间复杂度主要看第一行即可,即 S(n) = O(n)

二.冒泡排序

什么是冒泡排序

冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端,故名。
算法分析
冒泡排序算法是所有排序算法中最简单的(前面也提到过),在生活中应该也会看到气泡从水里面出来时,越到水面上气泡就会变的越大。在物理上学气压的时候好像也看到过这种现象;其实理解冒泡排序就可以根据这种现象来理解:每一次遍历,都把大的往后面排(当然也可以把小的往后面排),所以每一次都可以把无序中最大的(最小)的元素放到无序的最后面(或者说有序元素的最开始);

冒泡排序代码实现

下面是一段在VS2013环境下的代码

	unsigned int size = 5;	//定义数组大小
	int testArray[5] = { 4, 1, 2, 6, 8 };	//创建数组
	for (int i = 1; i < size; i++)	//外层循环
	{
		for (int j = 0; j < size - i; j++)	//内层循环
		{
			if (testArray[j]>testArray[j + 1])	//交换条件
			swap(testArray[j], testArray[j + 1]);
		}
	}
	for (int sp : testArray)	//遍历数组
	{
		cout << sp <<"	";	//输出结果
	}

运行结果:
在这里插入图片描述

冒泡排序时间复杂度

这个时间复杂度还是很好计算的:外循环和内循环以及判断和交换元素的时间开销;
最优的情况也就是开始就已经排序好序了,那么就可以不用交换元素了,则时间花销为:[ n(n-1) ] / 2;所以最优的情况时间复杂度为:O( n^2 );
最差的情况也就是开始的时候元素是逆序的,那么每一次排序都要交换两个元素,则时间花销为:[ 3n(n-1) ] / 2;(其中比上面最优的情况所花的时间就是在于交换元素的三个步骤);所以最差的情况下时间复杂度为:O( n^2 );
综上所述:
最优的时间复杂度为:O( n^2 ) ;有的说 O(n),下面会分析这种情况;
最差的时间复杂度为:O( n^2 );
平均的时间复杂度为:O( n^2 );

冒泡排序空间复杂度

空间复杂度
空间复杂度就是在交换元素时那个临时变量所占的内存空间;
最优的空间复杂度就是开始元素顺序已经排好了,则空间复杂度为:0;
最差的空间复杂度就是开始元素逆序排序了,则空间复杂度为:O(n);
平均的空间复杂度为:O(1);

三.选择排序

选择排序算法的原理:

先找出待排序的数中最大(降序)或者最小(升序)的数,将它跟第一个数进行交换,第一个数就是已排好序的数,然后在剩下的待排序的数中再找出最小或者最大的数跟已排好序的数的下一个数进行交换,直到待排序的数为0时所有的数就排序好了
第一次在这里插入图片描述
第一次循环:1,3,5,8,6,9,7,4,2,8
第二次循环:1,2,3,5,8,6,9,7,4,8
第三次循环:1,2,3,4,5,8,6,9,7,8
………………………………
结果:1,2,3,4,5,6,7,8,8,9

时间复杂度

时间复杂度就是循环的次数用O(n)表示:
选择排序每一次排好一个数,所以循环次数依次为n-1,n-2,n-3,....,1,所以就是:(n-1)+(n-2)+....+3+2+1 = (n-1+1)n/2 = n^2/2,所以选择排序的时间复杂度就是O(n ^2)

空间复杂度

空间复杂度就是在交换元素时临时变量所占的内存空间:O(1)

四.插入排序

1.什么是插入排序

插入排序就是将一个记录插入到已排好序的序列中,从而得到一个新的有序序列。
哪里有一个排好序的序列
那问题是我们要排序的是一个数组,哪里来一个排好序的序列呢?这时,我们可以把数组下标为0的元素想像成一个有序的数组,这个数组内只有他一个元素,所以,它总是有序的。后面的元素和他比较。
在这里插入图片描述

插入排序代码实现

在这里插入图片描述

时间复杂度:

乘法原则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积,因此这段代码时间复杂度为O(n) * O(n) = O(n * n) = O(n*n)。

空间复杂度:

直接插入排序中只使用了i,j这两个辅助元素,与问题规模无关,空间复杂度为O(1)。

五.快速排序

1.什么是快速排序

我理解的是,快速排序用的是分治法,运用的递归的算法,先挑选一个基准值,小于基准值的数放在左边,
大于基准值的数放在基准值的右边,这样就泾渭分明的三块;但是这三块是有序的,基准值左边右边的内
部数是无序的,所以,将基准值左右两端继续进行快速排序,直到区间长度为1,排序就完成了。

2.快速排序代码实现

下面使用vs2013实现快速排序:
主逻辑函数

在这里插入图片描述
输出为:在这里插入图片描述

3.快速排序时间复杂度

每种排序方式都会有最优的时间复杂度以及最差的时间复杂度,就像快速排序,你每次取出都取出整个数组
中最小/最大的那个元素,那就是冒泡排序了,他的时间复杂度为T[n] = n * (n-1) = n^2 + n;也就是O( n^2 )
那么最优情况下,时间复杂度如何计算呢

快速排序最优的情况就是每一次取到的元素都刚好平分整个数组(上方代码并不是最优情况);此时的时间复杂度公式则为:T[n] = 2T[n/2] + f(n);f[n] 就是第一次平分这个数组时所花的时间;T[n/2]是平分后的两边数组的时间复杂度,
下面来推算下,在最优的情况下快速排序时间复杂度的计算(用迭代法):

T[n] =  2T[n/2] + n              			 ----第一次递归

令:n = n/2  =  2 { 2 T[n/4] + (n/2) }  + n   ----第二次递归
            =  2^2 T[ n/ (2^2) ] + 2n
              
令:n = n/(2^2)   =  2^2  {  2 T[n/ (2^3) ]  + n/(2^2)}  +  2n-----第三次递归
=  2^3 T[  n/ (2^3) ]  + 3n

令:n = n/(  2^(m-1) )    =  2^m T[1]  + mn                   ----第m次递归(m次后结束)

当最后平分的不能再平分时,也就是说把公式一直往下跌倒,到最后得到T[1]时,说明这个公式已经迭代完了(T[1]是常量了)。
得到:T[n/ (2^m) ] = T[1] ===>> n = 2^m ====>> m = lgn;
T[n] = 2^m T[1] + mn ;其中m = lgn;
T[n] = 2^(lgn) T[1] + nlgn = n T[1] + nlgn = n + nlgn ;其中n为元素个数
又因为当n >= 2时:nlgn >= n (也就是lgn > 1),所以取后面的 nlgn;
综上所述:快速排序最优的情况下时间复杂度为:O( nlgn )

4.快速排序空间复杂度

快速排序的使用空间是O(1);其主要的空间复杂都在递归上了

最优的情况下空间复杂度为:O(logn) ;每一次都平分数组的情况
最差的情况下空间复杂度为:O( n ) ;退化为冒泡排序的情况

  • 27
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值