如何设计营销 网站建设,wordpress漂流瓶插件,网站如何三合一,建设一个网站的流程图本章重点#xff1a;
1.算法效率 2.时间复杂度 3.空间复杂度 4. 常见时间复杂度以及复杂度oj练习
目录
1.算法效率
1.2算法的复杂度
2.时间复杂度
2.1 时间复杂度的概念
2.2 大O的渐进表示法
2.3常见时间复杂度计算举例
3.空间复杂度 4. 常见复杂度对比
5.复杂度…本章重点
1.算法效率 2.时间复杂度 3.空间复杂度 4. 常见时间复杂度以及复杂度oj练习
目录
1.算法效率
1.2算法的复杂度
2.时间复杂度
2.1 时间复杂度的概念
2.2 大O的渐进表示法
2.3常见时间复杂度计算举例
3.空间复杂度 4. 常见复杂度对比
5.复杂度的oj练习
5.1消失的数字
5.2旋转数组 1.算法效率 1.1 如何衡量一个算法的好坏 如何衡量一个算法的好坏呢比如对于以下斐波那契数列:
long long Fib(int N)
{
if(N 3)
return 1;
return Fib(N-1) Fib(N-2);
}
斐波那契数列的递归实现方式非常简洁但简洁一定好吗那该如何衡量其好与坏呢 1.2算法的复杂度 算法在编写成可执行程序后运行时需要耗费时间资源和空间(内存)资源 。因此衡量一个算法的好坏一般 是从时间和空间两个维度来衡量的即时间复杂度和空间复杂度。时间复杂度主要衡量一个算法的运行快慢而空间复杂度主要衡量一个算法运行所需要的额外空间。在计算 机发展的早期计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展计 算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注一个算法的空间复杂度。
2.时间复杂度
2.1 时间复杂度的概念 可以将算法的时间复杂度看成是一个函数类似于一个函数式子 F(N) N算法中的基本操作的执行次数为算法的时间复杂度。即找到某条基本语句与问题规模N之间的数学表达式就是算出了该算法的时间复杂度。
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;
}
这个函数执行的基本操作次数可以用函数式子
来表示当N 变化时候
N 10 F(N) 130 N 100 F(N) 10210 N 1000 F(N) 1002010
对应的函数结果是不同的 那怎么衡量他的时间复杂度呢实际中我们计算时间复杂度时我们其实并不一定要计算精确的执行次数而只需要大概执行次数那么这里我们使用大O的渐进表示法
2.2 大O的渐进表示法 大O符号Big O notation是用于描述函数渐进行为的数学符号。 推导大O阶方法 1、用常数1取代运行时间中的所有加法常数。 2、在修改后的运行次数函数中只保留最高阶项。 3、如果最高阶项存在且不是1则去除与这个项目相乘的常数。得到的结果就是大O阶。 使用大O的渐进表示法以后Func1的时间复杂度为
N 10 F(N) 100 N 100 F(N) 10000 N 1000 F(N) 1000000
通过上面我们会发现大O的渐进表示法去掉了那些对结果影响不大的项简洁明了的表示出了执行次数。 另外有些算法的时间复杂度存在最好、平均和最坏情况 最坏情况任意输入规模的最大运行次数(上界) 平均情况任意输入规模的期望运行次数 最好情况任意输入规模的最小运行次数(下界) 例如在一个长度为N数组中搜索一个数据x 最好情况1次找到最坏情况N次找到 平均情况N/2次找到 在实际中一般情况关注的是算法的最坏运行情况所以数组中搜索数据时间复杂度为O(N)
2.3常见时间复杂度计算举例
实例1 // 计算Func2的时间复杂度
void Func2(int N)
{
int count 0;
for (int k 0; k 2 * N ; k)
{
count;
}
int M 10;
while (M--)
{
count;
}
printf(%d\n, count);
}
最差情况下 运行 2N 10 大O渐进法 去掉影响因素较小的 以及系数所以时间复杂度为ON 实例2:
// 计算Func3的时间复杂度
void Func3(int N, int M)
{
int count 0;
for (int k 0; k M; k)
{
count;
}
for (int k 0; k N ; k)
{
count;
}
printf(%d\n, count);
}
这个程序中并没有介绍M 和N 的大小 所以时间复杂度为O(M N).
实例3:
// 计算Func4的时间复杂度
void Func4(int N)
{
int count 0;
for (int k 0; k 100; k)
{
count;
}
printf(%d\n, count);
}
所有常数的时间复杂度都可以优化到O(1)。
实例4:
// 计算strchr的时间复杂度
const char * strchr ( const char * str, int character );
寻找字符串函数 最差情况就是O(N)
实例5:
// 计算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;
}
} 最好的情况下是顺序的 只需要两两比较只需要O(N)如果不是有序的 需要每个比较 那就是ON方
实例6
// 计算BinarySearch的时间复杂度
int BinarySearch(int* a, int n, int x)
{
assert(a);
int begin 0;
int end n-1;
while (begin end)
{
int mid begin ((end-begin)1);
if (a[mid] x)
begin mid1;
else if (a[mid] x)
end mid;
else
return mid;
}
return -1;
}
二分查找前提是有序 就像折纸一样最悲观的情况 1 * 2 *2 *2 .......x N 总共运行了x次
根据指数公式 x 实例7:
// 计算阶乘递归Fac的时间复杂度
long long Fac(size_t N)
{
if(0 N)
return 1;
return Fac(N-1)*N;不为零 就要运行一次 一直运行到N 0; 一共N 1次 去掉没用的那就是O(N)
实例8:
// 计算斐波那契递归Fib的时间复杂度
long long Fib(size_t N)
{
if(N 3)
return 1;
return Fib(N-1) Fib(N-2);
} 悲观计算法 时间复杂度 就是O(N) 3.空间复杂度
空间复杂度也是一个数学表达式是对一个算法在运行过程中临时占用额外存储空间大小的量度 。 空间复杂度不是程序占用了多少bytes的空间因为这个也没太大意义所以空间复杂度算的是变量的个数。 空间复杂度计算规则基本跟实践复杂度类似也使用大O渐进表示法。 注意函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了因 此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定 实例1
// 计算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;
}
}额外变量只有一个 所以空间复杂度是O(1)
实例2
// 计算Fibonacci的空间复杂度
// 返回斐波那契数列的前n项
long long* Fibonacci(size_t n)
{
if(n0)
return NULL;
long long * fibArray (long long *)malloc((n1) * sizeof(long long));
fibArray[0] 0;
fibArray[1] 1;
for (int i 2; i n ; i)
{
fibArray[i] fibArray[i - 1] fibArray [i - 2];
}
return fibArray;
}
额外申请了n 1 个空间 所以空间复杂度为O(N)
实例3
// 计算阶乘递归Fac的空间复杂度
long long Fac(size_t N)
{
if(N 0)
return 1;
return Fac(N-1)*N;
}
递归调用了N次开辟了N个栈帧每个栈帧使用了常数个空间。空间复杂度为O(N) 4. 常见复杂度对比 5.复杂度的oj练习
5.1消失的数字
#includestdio.h
// 利用异或的知识点交换律不改变最终结果所以 定义一个变量 初始值为0与数组异或后在与给定数组异或剩下的值就是我们要找的
int missingNumber(int* nums, int numsSize)
{int x 0;for (int i 0; i numsSize; i)// 不缺失所以正常数组大小比给定数组大小大1{x ^ i;}for (int i 0; i numsSize; i){x ^ *(nums i);}return x;
}
int main()
{int nums[100] { 0 };int num sizeof(nums) / sizeof(nums[0]);for (int i 0; i num; i){scanf(%d, nums[i]);}printf(%d, missingNumber(nums, num));return 0;
}
5.2旋转数组
// 先封装一个转置函数
void reverse(int* pa, int left, int right)
{while (left right){int temp 0;temp *(pa left);*(pa left) *(pa right);*(pa right) temp;left;--right;}
}void rotate(int* nums, int numsSize, int k)
{if (k numsSize){k % numsSize;}// 将前 numsSize - k - 1 个数 转置reverse(nums, 0, numsSize - k - 1);// 将后 k 个数 转置reverse(nums, numsSize - k, numsSize - 1);// 将整体转置 个数 转置reverse(nums, 0, numsSize - 1);}