基本概念:
1.问题规模:算法求解问题输入量的多少,是问题大小的本质表示,一般用整数n表示。
2.语句频度:一条语句的重复执行次数。
3.基本语句:算法中重复执行次数和算法的执行时间成正比的语句。
4.算法的渐近时间复杂度(简称时间复杂度):一般情况下,算法中基本语句重复执行的次数 是问题规模的某个函数f(n),算法的时间量度记作T(n)=O(f(n)),它表示随问题规模n的增大, 算法执行时间的增长率和f(n)的增长率相同,称作算法的渐进时间复杂度。(O表示数量级)
时间复杂度的表示方法
经典函数举例
1、单层循环
【例题1】给定 n(n≤1000)个元素 ai,求其中 奇数 有多少个。
int countOdd(int n, int a[]) {
int cnt = 0;
for(int i = 0; i < n; ++i) {
if(a[i] & 1)
++cnt;
}
return cnt;
}
a & 1
等价于a % 2
,代码a
模 2 的余数;(作者:英雄哪里出来)
此题用了单层循环,这里的 n 是一个变量,随着 n 的增大,执行次数增大,执行时间就会增加,所以就有了时间函数的表示法如下:
f(n)=n
当 f(n)=n,我们称这个算法拥有线性时间复杂度,记作 O(n);
2、双层循环
int countOddPair(int n, int a[]) {
int cnt = 0;
for(i = 0; i < n; ++i) {
for(j = i+1; j < n; ++j) {
if( (a[i] + a[j]) & 1)
++cnt;
}
}
return cnt;
}
-
双层循环,它的时间函数表示法如下:
f(n)=n(n−1)/2 -
当 f(n)=n(n−1)/2 ,我们称这个算法拥有平方级时间复杂度,记作 O(n^2)
-
3、三层循环 三层循环,它的时间函数表示法如下:
-
常见时间复杂度
- (二分法概念:二分法查找,也叫折半法,是一种在有序的数组中查找特定元素的搜索算法。注意:使用二分法的前提条件,数组内的数字必须是有序的!举例:首先有一组数字{1,2,3,4,5,6,7,8,9,10} ,分别给予每个数组内的数字一个下标,二分法就是每次取数组的中间值与我们所要查找的数字进行比较,例如:我需要查找的数字是“7”
中间值下标=做左端的值的下标加上做右边的值的下标除以2;mid = (left+right);第一次使用二分法:mid = (0+9) / 2 = 4。下标为4的值为5,5与7比较,5比7小,那么我们的 left = mid + 1 = 4 + 1 =5。第二次使用二分法:mid = (5 + 9) / 2 = 7,下标为7的值为8,8与7比较,8比7大,那么我们的rigth = mid - 1 = 7 -1 = 6。第三次使用二分法:mid = (5 + 6) / 2 = 5,下标为5的值是6,6与7比较,6比7小,那么我们的left = mid + 1 = 5 + 1 = 6。第四次使用二分法:mid = (6 + 6 ) / 2 =6,下标为6的值是7,7与7相等,找到了!int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; int k = 0; printf("请输入你需要查找的数字\n"); scanf("%d", &k); int sz = sizeof(arr) / sizeof(arr[0]); int left = 0;//左下标 int right = sz - 1;//右下标 while (left <= right) { int mid = (left + right) / 2;//中间元素下标;mid = left+(right-left)/2 if (arr[mid] < k) { left = mid + 1; } else if (arr[mid] > k) { right = mid - 1; } else { printf("找到了下标是:%d", mid); break; } if (left > right) printf("不存在该数字\n"); } return 0; }
如何判断时间复杂度
(笔记来源:
链接:https://leetcode.cn/leetbook/read/data-structure-all-in-one/w7bqd5/
来源:力扣(LeetCode)
作者:英雄哪里出来 -
(补充)最好最坏和平均时间复杂度
-
(1)for(i=0;i<n;i++)
(2) if(a[i]==e) return i+1
(3) return 0
此算法中,语句2的频度不仅与问题规模n有关,还与a[i]的各元素值及e的取值有关。
习题