数据结构<1>时空复杂度详解
文章目录
- 什么是时间复杂度和空间复杂度
- 前言(算法效率)
- 时间复杂度的计算
- 空间复杂度的计算
- oj练习
什么是时间复杂度和空间复杂度
前言(算法效率)
算法效率分析分为两种:第一种是时间效率,第二种是空间效率。
时间效率被称为时间复杂度,而空间效率被称作空间复杂度。 时间复杂度主要衡量的是一个算法的运行速度,而空间复杂度主要衡量一个算法所需要的额外空间.
时间复杂度的计算
时间复杂度是一个衡量算法时间的相对标准。他不是一个固定的时间例如多少分多少秒,而是一个大概的估计数值。比如同一个算法在不同的机器上运行的时间肯定不同。这时候就需要我们的时间复杂度来衡量算法的时间效率。
时间复杂度通常使用大O的渐进表示法(空间复杂度也是)
那么什么是大O的渐进表示法呢?
列举几个实例给搭建看看 O(N),O(N^2),O(1);
如上所示就是大O的渐进表示法。括号内就是算法执行的次数估计值,要记住时间复杂度计算的是执行次数
但是他不是一个准确的数字而是一个估计值,比如(N^2+2N+10)在N很大的时候除了最高次项其他的都可以忽略不计,这类似与数学中的极限思想,只保留对结果影响最大的一项。
推导法则如下:
1.若括号内是常数都写成O(1)
2.若是括号内是多项式例如O(N^2+3n+9)则只保留最高次项
3.若是最高次项的系数不为1则统一写成1
下面我们来看几个例子学习一下:
// 请计算一下Func1基本操作执行了多少次?
void Func1(int N)
{int count = 0;
for (int i = 0; i < N ; ++ i)
{for (int j = 0; j < N ; ++ j)
{++count;
}
}
for (int k = 0; k < 2 * N ; ++ k)
{++count;
}
int M = 10;
while (M--)
{++count;
}
printf("%d\n", count);
}
这个算法的具体执行次数是(N*N+2N+10)
而他的时间复杂度是O(N^2)
// 计算strchr的时间复杂度?
const char * strchr ( const char * str, char character )
{while(*str != '\0')
{if(*str == character)
return str;
++str;
}
return NULL;
}
要注意有的算法有最坏情况,最好情况和平均情况。比如上面这个算法是在一个字符串中寻找一个字符,最好情况就是一上来就找到了,时间复杂度是O(1),最坏情况是遍历完了字符串才找到或者没找到时间复杂度为O(N),平均情况为O(N/2);时间复杂度一般是取最坏情况,这个在我们生活中也会用到就是预期管理,以最坏情况为标准,那么大部分情况都会超出预期。也就是让你感觉比较舒服。
所以我们计算时间复杂度一般是以最坏情况
// 计算BinarySearch的时间复杂度?
int BinarySearch(int* a, int n, int x)
{assert(a);
int begin = 0;
int end = n;
while (begin < end)
{int mid = begin + ((end-begin)>>1);
if (a[mid] < x)
begin = mid+1;
else if (a[mid] > x)
end = mid;
else
return mid;
}
return -1;
}
上面这段代码是二分查找,每次查找都可以排除一半的元素,比如数组有N个元素我们查找了x次2^x = N所以
x = log2(N);
但是在算法中我们一般写成O(logN);
// 计算阶乘递归Factorial的时间复杂度?
long long Factorial(size_t N)
{return N < 2 ? N : Factorial(N-1)*N;
}
这是递归的时间复杂度计算。
每次递归的时间复杂度都是O(1),递归进行了N次,所以这个算法的时间复杂度是O(N);
插入一点:递归的效率在时间复杂度相同的情况下比循环低,因为递归需要创建函数栈帧,而循环不需要
//循环求斐波那契数列#include<stdio.h>int main()
{int n = 0;scanf("%d", &n);int* fib = (int*)malloc(n * (sizeof(int)));fib[0] = 1;fib[1] = 1;for (int i = 2; i < n; i++){fib[i] = fib[i - 1] + fib[i - 2];}for (int j = 0; j < n; j++){printf("%d ", fib[j]);}return 0;
}
该算法的时间复杂度是O(n);
int show(int *num,int m,int n)
{int x = 0;for(int i=0;i<m;i++){x^=num[i];}for(int j = 0;j<n;j++){x^=num[j];}return x;
}
上面的算法时间复杂度是O(m+n)要注意我们不明确m和n的关系所以不能乱写,比如若m>>n那时间复杂度就是O(m)反之就是O(n),如果m和n差不多那就是O(n^2) 或 O(m^2)
计算斐波那契数列的时间复杂度
int fab(int n)
{if (n <= 2)return 1;return fab(n - 1) + fab(n - 2);
}
递归求斐波那契数列,这个代码的时间复杂度是多少呢?
以这个图为例子,可以看到从上到下调用的次数我们假设每一层都是满的二叉树。当然右边有部分没有画出来,可以看到假设是从F(N)开始那么最后一层就是2(n-1)每次函数调用的执行次数都是常数次,所以总的时间复杂度就是20+21+22+23+…2(N-1)根据等比数列求和公式(或者是错位相减法),最后这段代码的时间复杂度就是O(2^n)这是一个超级大的数,n=10就是1024,20就是100w,30就是10亿
为了了解各个时间复杂度的差异我们来看一下图
图上我们可以看到logn与1非常接近,所以这两个都是近乎最好的算法复杂度。
空间复杂度的计算
上文提到,时间复杂度实际上就是计算代码执行次数
而空间复杂度实际上就是计算变量个数
这里需要我们先记住一句话时间是累积的空间是不累积的。后面会解释的
空间复杂度也是用大O的渐进表示法。例如:
// 计算BubbleSort的空间复杂度?
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;
}
}
形参也会计算在内,所以这段代码的变量共有5个根据大O的渐进表示法为O(1),这里要注意循环了n次并不是o(n)因为上面说到空间是不累积的,每次循环完之后变量都会销毁下一次循环在创建,可以理解为使用同一片空间。
// 计算阶乘递归Factorial的空间复杂度?
long long Factorial(size_t N)
{return N < 2 ? N : Factorial(N-1)*N;
}
递归的空间复杂度是O(n),因为每次递归都会创建一次栈帧,递归在没有递归到底的时候是不会销毁前面的栈帧的(这也就是递归太深会导致栈溢出的原因)。
//循环求斐波那契数列#include<stdio.h>int main()
{int n = 0;scanf("%d", &n);int* fib = (int*)malloc(n * (sizeof(int)));fib[0] = 1;fib[1] = 1;for (int i = 2; i < n; i++){fib[i] = fib[i - 1] + fib[i - 2];}for (int j = 0; j < n; j++){printf("%d ", fib[j]);}free(fib);return 0;
}
该代码的时间复杂度是O(n)因为malloc开辟的空间也要计算进去。虽然 最后释放了,但是这就类似于时间复杂度的最坏情况了,我们计算的是最多的时候创建了几个变量,占用多少内存。
oj练习
https://leetcode-cn.com/problems/missing-number-lcci/
消失的数字
这里的思路是,将给定数组的所有元素异或起来,然后再与标准的数组进行异或;因为相同数字异或(按位比,相同为0,不同为1)后为0,所以最后剩下的那个数字就是数组消失的数组。
注:相同数字异或为0
任何数字异或上0之后都不变
这里也可以用求和,给定数组求和,减去标准数组之和即是消失的数字。(有溢出的可能)
int missingNumber(int* nums, int numsSize){int n = 0;for(int i = 0;i<numsSize;++i){n^=nums[i];}for(int j = 0;j<numsSize+1;++j){n^=j;}return n;
}
https://leetcode-cn.com/problems/rotate-array/
旋转数组
相信大家一定能想到第一种思路就是
1.把最后一个数组元素保存起来,然后将数组元素右移一个单位,再把最后一个元素放到数组开头位置。但是这样是双层循环,时间复杂度为O(N)所以效率很低。
2.用空间换时间的思想,再创建一个数组,保存nums的后k个元素,然后再保存数组的前numSize-k个元素。这样是需要递归一次数组就可以了。时间复杂度是O(N);
3.最好的方法是,先逆置后k个数组元素,再逆置前numSize-k个数组元素,最后再整体逆置(大神想出来的方法)时间复杂度为O(N)空间复杂度是O(1)!!!
下面我们来实现一下第三种方法。
void reverse(int *nums,int left,int right)
{while(left<right){int tmp = nums[left];nums[left] = nums[right];nums[right] = tmp;left++;right--;}
}void rotate(int* nums, int numsSize, int k){if(k>=numsSize){k%=numsSize;}reverse(nums,numsSize-k,numsSize-1);reverse(nums,0,numsSize-k-1);reverse(nums,0,numsSize-1);
}
数据结构<1>时空复杂度详解相关推荐
- 数据结构与算法之时间复杂度详解
数据结构与算法之时间复杂度详解 目录 排序算法的介绍和分类 算法的时间复杂度概念 常见的时间复杂度解析 平均时间复杂度和最坏时间复杂度 空间复杂度介绍 1. 排序算法的介绍和分类 排序算法的介绍 排序 ...
- 数据结构殷人昆电子版百度云资源_数据结构精讲与习题详解(C语言版第2版清华大学计算机系列教材)...
导语 内容提要 殷人昆编著的<数据结构精讲与习题详解(C语言版第2版清华大学计算机系列教材)>是清华大学出版社出版的<数据结构(C语言版)>(第2版)的配套教材,对" ...
- (7)Java数据结构--集合map,set,list详解
MAP,SET,LIST,等JAVA中集合解析(了解) - clam_clam的专栏 - CSDN博---有颜色, http://blog.csdn.net/clam_clam/article/det ...
- 【数据结构】树状数组详解(Leetcode.315)
前言 最近做题时遇到一个关于树状数组的题力扣https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self/但是CSDN上仅有 ...
- dataframe drop_Pandas数据结构Series和DataFrame基础详解
作者:Zarten知乎专栏:python数据分析与挖掘深入详解知乎ID: Zarten简介: 互联网一线工作者,尊重原创并欢迎评论留言指出不足之处,也希望多些关注和点赞是给作者最好的鼓励 ! 概述 p ...
- c++数据结构中 顺序队列的队首队尾_数据结构与算法—队列图文详解
前言 栈和队列是一对好兄弟,前面我们介绍过数据结构与算法-栈详解,那么栈的机制相对简单,后入先出,就像进入一个狭小的山洞,山洞只有一个出口,只能后进先出(在外面的先出去).而队列就好比是一个隧道,后面 ...
- [数据结构]模式匹配算法--KMP算法详解
目录 一. 模式匹配 二. 模式匹配算法 1. 朴素模式匹配算法 2. KMP算法 1). KMP算法的优势 2). KMP算法的原理 3). next数组的构造 4). 利用next数组匹配的过程 ...
- C语言数据结构之树超全详解
目录 基础知识 二叉树的一些基本概念 树的性质 二叉树的一些基本性质 操作部分 二叉树的存储结构 1.顺序存储结构 2.链式存储结构 3.线索二叉树 前驱与后继 线索二叉树的存储表示 二叉树的遍历 先 ...
- 数据结构专题 -- 哈希思想详解
代码会存放在: https://github.com/sjmshsh/Data-Struct-HandWriting 通过阅读本篇文章,你可以学到: 哈希思想及其本质 使用C++实现简易的哈希表 哈希 ...
最新文章
- Centos7 下安装VIM编辑器
- 备份oracle中的大表
- phpgif图片包_php生成动态验证码gif图片
- 用python编写脚本计算linux_利用Python3实现Linux的脚本功能 !
- QQ for Linux启动闪退问题
- linux登录pg数据库密码,PostgreSQL:修改数据库用户的密码
- 【概念的辨异】—— ISO C 与 POSIX C(C standard library 与 C POSIX library)
- python刚出来多少薪资-Python最低薪资在北上深是多少你们知道吗?我已经整理好了哦...
- Java常见笔试题(99.9%必问)
- GNU开发工具——GNU Binutils快速入门
- 生意经之王安论断:网店营销当机立断才能抓住机遇
- Hybrid LSTM and Encoder-Decoder Architecturefor Detection of Image Forgeries的复现梳理
- 回顾12306 成长的烦恼
- window11 网络突然就用不了,系统更新网络就用不了了,DNS服务器可能不可用
- 微信小程序开发—— tabbar 配置
- python-opencv去除小面积区域/孔洞填充(二值图像)
- Localize a WPF application
- js小学生图区_js读取本地图片并显示
- linux 壁纸自动更换,linux设置自动更换壁纸
- 亚马逊云科技的十年创新之路