时间复杂度主要衡量的是一个算法的运行速度,而空间复杂度衡量的是一个算法的运行空间。在计算器前期,空间缺乏。所以对空间复杂度很在乎。但是经过计算机行业发展,由摩尔定律,硬件每十八个月就会翻一番,所以手机的内存越来越高。
(一)时间复杂度:算法的大概基本执行次数
1.大O的渐进表示法:用于描述函数渐进行为的数学符号
常数操作:int a=air[i];一条指令即可。而int b=list.get[i];得遍历整个列表,直到找到i
如果一个操作和样本的数据量没有关系,每次都是固定时间内完成的操作称为常数操作
评价一个算法的时间好坏,先看时间复杂度的指标,再分析不同数据样本下的实际运行时间,也称为常数项时间。
1)如果最高项前面的系数存在并且不为1,就去除这个系数。
O(n^2)和O(2*n*n)没区别
2)在修改后的运行次数函数中,只保留最高项。
3)用常数1取代运行时间中的所有加法常数。
for(k=1;k<100;k++)
{
printf("hehe");
}// O(n)=1
4)但是有些时间复杂度的计算会存在最好,最坏和平均的情况。
比如在数组中查找一个数,最好是一次找到,最差N次找到,平均是N/2次找到。但是一般情况下,我们关注的都是最坏情况 。
(二)排序算法具体剖析
1.冒泡排序:
最坏F(n)=1+2+3+4+...n-1=n^2 ; 最好F(n)=n;
void BubbleSort(int *a,int n)
{
assert(a);//检查指针是否为空
for(size_t end=n;end>0;--end)
{
int exchange=0;//检查是否已经排好顺序了
for(size_t i=1;i<end;++i)
{
if(a[i-1]>a[i])
{
swap(&a[i-1],&a[i]);
exchange=1;
}
}
if(exchange==0){
break;
}
}
2 选择排序
#include<iostream>
using namespace std;
int main()
{
int a[10]={0};
for(int i=0;i<10;i++)
{
cin>>a[i];
}
int min=a[0]
for(int i=0;i<n-1;i++)
for(int j=i+1;j<n;j++)
{
if(a[j]>a[i])
swap(*a[j],*a[i])
}
for(i=0;i<n-1;i++)
{
cout<<a[i]<<endl;
}
每次排序:第一次看数字看了N眼,比了(N-1)次,1次swap
第二次看数字看了(N-1)眼,比了(N-1)次,1次swap
则总共的常数操作位:a*N^2+b*N+c 时间复杂度位O(n^2)
3、插入排序
时间复杂度不同,数据状况不同。最好O(n),最差O(n^2)
下标:分别使得下标0~0,0~1,0~2...0~size-1所代表的值有序
void turnxu(int arr[],int size)
{
if(arr==NULL||size<2)
return;
int i=0,j=0;
for(i=1;i<size;i++)
for(j=i-1;j>=0&&arr[j]>arr[j+1];j--)
{
swap(arr,i,j);
}
}
4.归并排序
T(n)=2T( n/2)+O(n)=O(N*logN)//优点:没有浪费比较行为。变成了比较有序的部分去和下一个部分merge。空间复杂度O(N)
void xu(int arr[],int L,int R)
{
if(L==R)
return;
int mid=L+((R-L)>>1);
xu(arr,L,mid);//对左边排序
xu(arr,mid+1,R);//对右侧排序
merge(arr,L,mid,R);//对整体排序 外排序
}
void merge(int arr[],int L,int M,int R)
{
int help[]=new int[L-R+1];
int i=0,p1=L,p2=M+1;
while(p1<=M&&p2<=R)
help[i++]=arr[p1]<arr[p2]?arr[p1]:arr[p2];//从左右俩个数组里面找小的放进去
while(p1<=M)
help[i++]=arr[p1++];//p2越界而p1没有越界
while(p2<=R)
help[i++]=arr[p2++];
for(i=0;i<L-R+1;i++)
arr[L+i]=help[i];//合并到一个数组里面
delete help[];
}
5,快排
void quicksort(int arr[],int L,int R)
{
if(L<R)
swap(arr,L+(int)(rand()*(R-L+1)),R);//等随机选一个数字和最右位置数字交换
int p[]=partition(arr,L,R);
quicksort(arr,L,p[0]-1);//<区域 p[0]-1得到小于区域的右边界
quicksort(arr,p[1]+1,R);//>区域
}
//处理arr的函数,默认把arr[r]作为划分
//返回值为这个区域的左右边界,长度为2的数组 res[0],res[1]
int partition(int arr,int L,int R)
{
int less=L-1;//小于区的右边边界
int more=R;//大于区域的左边界
while(L<more)
{//用L表示当前数字的位置 arr[R]划分
if(arr[L]<arr[R})
swap(arr,++less,L++)
else if(arr[L]>arr[R])
swap(arr,--more,L);
else{
L++;
}
swap(arr,more,R);
return new int[]={less+1,more};
}
快排的空间复杂度是O(logN) 类似于满二叉树的展开。 最差为O(N)
6.堆排序
堆在逻辑上是一颗完全二叉树结构(满二叉树或者从左往右依次遍满)(数组从0开始的一段可以对应成完全二叉树)
大根堆:每某个结点为头的树的最大值是这个结点。
小根堆:每一个头结点的值是其所属这棵树的最小值。
//堆排序
void headinsert(int arr[],int index)
{
while(arr[index]>arr[(index-1)/2])
swap(arr,index,(index-1)/2);//跳出循环的时候是最大的确实是父节点或者是NULL
index=(index-1)/2;
}
问题1: 返回最大值并且把最大值移除之后仍然是大根堆
//返回最大值并且把最大值移除之后仍然是大根堆
int heapfy(int arr[],int index,int heapsize){
//用index记录从哪里开始
int left=index*2+1;//记录左孩子下标
while(left<helpsize)
{//保证下面还有数字
//俩个孩子中谁的值大给leftlar
int large=arr[left]>arr[left+1]?left:left+1;
//父亲和最大儿子比较
large=arr[index]>arr[large]?index:large;
if(large==index)
break;
swap(arr,index,large);
index=largest;
left=index*2+1;
}
}
(三)递归函数的T(n)
master公式:T(n)=a*T(n/b)+O(N^d)
使用条件:子问题的规模等量
log以b为底a的对数<d T(n)=O(N^d)
log以b为底a的对数>d T(n)=O(N的log以b为底a的对数)
log以b为底a的对数=d T(n)=O(N^d*logN)
1)T(n)代表母问题有n个子问题
2)a是每个子问题调用的次数
3)n/b代表每个子问题的规模
4)O(N^d)代表除子问题外调用的过程
1.斐波拉契数列
//递归算法复杂度=递归次数*每次递归函数的次数//O(n)
long long f(size_t N)
{
return N<2?N:f(N-1)*N;
}
long long f(size_t N)
{
return N<2?N:f(N-1)+f(N-2);
}//画图可得O(2^n)
改进斐波拉契数列
long l0ng*fi(int N)
{
long long*f=malloc(sizeof(long long)*(N+1));
f[0]=0;
if(N==0)
return f;
f[1]=1;
//以空间换时间
for(int i=2;i<N;i++)
{
f[i]=f[i-1]+f[i-2];
}
return f;
}
long l0ng*fi(int N)
{
long long*f=malloc(sizeof(long long)*(N+1));
f[0]=0;
if(N==0)
return f;
f[1]=1;
//以空间换时间
for(int i=2;i<N;i++)
{
f[i]=f[i-1]+f[i-2];
}
return f;
}
2. 例:求数组的最大值 T(n)=O(n)
int ismax(int arr[],int L,int R)//求[L,R]上数组的max
{
if(L==R)
return arr[L];
int mid=L+((R-L)>>1);//求中点 直接求会导致越界,用L+(R-L)/2
int leftmax=ismax(arr,L,mid);
int righemax=ismax(arr,mid+1,R);
return max(leftmax,rightmax);
}