哪类网站赚钱 优帮云,盐城seo快速排名,百度不收录我的网站,山东省建设监理协会官方网站1. 算法效率
如何去衡量一个算法的好坏#xff1f;
通常我们从时间效率和空间效率两个方面去分析算法的好坏。时间效率即时间复杂度#xff0c;空间效率被称为空间复杂度。时间复杂度主要是衡量一个算法的运行速度#xff0c;而空间复杂度主要衡量一个算法所需要的额外空间…1. 算法效率
如何去衡量一个算法的好坏
通常我们从时间效率和空间效率两个方面去分析算法的好坏。时间效率即时间复杂度空间效率被称为空间复杂度。时间复杂度主要是衡量一个算法的运行速度而空间复杂度主要衡量一个算法所需要的额外空间。 常见的复杂度大小比较O(2^N) O(N^2) O(N*logN) O(N) O(logN) O(1) 在计算机发展的早期计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展计算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注一个算法的空间复杂度。因为现在的内存不像以前那么贵所以经常听到过牺牲空间来换取时间的说法
事后统计法
**这种方法可行但不是一个好的方法。**该方法有两个缺陷一是要想对设计的算法的运行性能进行评测必须先依据算法编制相应的程序并实际运行二是所得时间的统计量依赖于计算机的硬件、软件等环境因素有时容易掩盖算法本身的优势。
事前分析估算的方法
因事后统计方法更多的依赖于计算机的硬件、软件等环境因素有时容易掩盖算法本身的优劣。因此人们常常采用事前分析估算的方法。 在编写程序前依据统计方法对算法进行估算。一个程序在计算机上运行时所消耗的时间取决于下列因素 (1) 算法采用的策略、方法(2). 编译产生的代码质量(3) 问题的输入规模(4) 机器执行指令的速度。
2. 时间复杂度 定义在计算机科学中算法的时间复杂度是一个数学函数它定量描述了该算法的运行时间。一个算法执行所耗费的时间与其中语句的执行次数成正比例算法中基本的执行次数即算法的时间复杂度。 通常在计算算法的时间复杂度时并不一定要计算精确的执行次数而只需要计算大概执行次数我们通常使用大 O 渐进法来表示。 1. 用常数 1 来取代运行时间中所有的加法常数 2. 只保留最高的阶项 3. 如果最高项存在且不是 1则去除与这个项目相乘的常数得到的结果就是大 O 阶. 代码演示如下:
//请计算一下func1基本操作运行了多少次
void func1(int N){int count 0;for (int i 0; i N ; i) {for (int j 0; j N ; j) {count;}}//这个for循环运行 N * N 次for (int k 0; k 2 * N ; k) {count;}//这个for循环运行 N 次int M 10;while ((M--) 0) {count;}//这个操作运行 10 次System.out.println(count);
}对于上述代码进行时间复杂度分析 第一个嵌套 for 循环的执行次数为 N^2第二个 for 循环的执行次数为 2N第三个 while 循环的执行次数是 10, 则 F(N)F(N^2) F(2N)10根据大 O 渐进表示法该算法的时间复杂度为 O(N^2). 算法的时间复杂度存在最好、平均、最坏情况 最好情况任意输入规模的最大运行次数上界 平均情况任意输入规模的期望运行次数 最坏情况任意输入规模的最小运行次数下界 注意: O(1) 表示基本语句的执行次数是一个常数一般来说只要算法中不存在循环语句时间复杂度就为 O(1)。 下面选取一些常见的进行讲解
常数阶 O(1)
无论代码执行了多少行只要是没有循环等复杂结构那这个代码的时间复杂度就都是 O(1)如
int i 1;
int j 2;
i;
j;
int m i j;上述代码在执行的时候它消耗的时候并不随着某个变量的增长而增长那么无论这类代码有多长即使有几万几十万行都可以用 O(1) 来表示它的时间复杂度。
线性阶 O(n)
这个在最开始的代码示例中就讲解过了如
for(i1; in; i)
{j i;j;
}这段代码for 循环里面的代码会执行 n 遍因此它消耗的时间是随着 n 的变化而变化的因此这类代码都可以用 O(n) 来表示它的时间复杂度。
对数阶 O(logN)
int i 1;
while(in)
{i i * 2;
}从上面代码可以看到在 while 循环里面每次都将 i 乘以 2乘完之后i 距离 n 就越来越近了。我们试着求解一下假设循环 x 次之后i 就大于 2 了此时这个循环就退出了也就是说 2 的 x 次方等于 n那么 x log2^n 也就是说当循环 log2^n 次以后这个代码就结束了。因此这个代码的时间复杂度为O(logn)
线性对数阶 O(nlogN)
线性对数阶 O(nlogN) 其实非常容易理解将时间复杂度为 O(logn) 的代码循环 N 遍的话那么它的时间复杂度就是 n * O(logN)也就是了 O(nlogN)。
就拿上面的代码加一点修改来举例
for(m1; mn; m)
{i 1;while(in){i i * 2;}
}平方阶 O(n2)
平方阶 O(n²) 就更容易理解了如果把 O(n) 的代码再嵌套循环一遍它的时间复杂度就是 O(n²) 了。 举例
for(x1; in; x)
{for(i1; in; i){j i;j;}
}这段代码其实就是嵌套了 2 层 n 循环它的时间复杂度就是 O(n*n)即 O(n²) 如果将其中一层循环的 n 改成 m即
for(x1; im; x)
{for(i1; in; i){j i;j;}
}那它的时间复杂度就变成了 O(m*n)
立方阶 O(n³)、K 次方阶 O(n^k)
参考上面的 O(n²) 去理解就好了O(n³) 相当于三层 n 循环其它的类似。
指数阶 O(2^N)
递归的时间复杂度计算: 递归的次数 * 每次递归后代码的执行次数 (也是用大 O 渐进法表示)
int Fibonacci(int N)
{return N 2 ? N : Fibonacci(N-1)Fibonacci(N-2);
}上面的代码是一个计算斐波那契数列的方法使用的是递归的方法我们知道递归的方法来计算斐波那契数列是非常低效的最好还是使用循环的方法但是递归的时间复杂度是多少呢 我们知道每一次调用这个方法时我们的时间复杂度是一个常数那么这个递归的时间复杂度就是我们一共调用了多少次方法现在我们来分析一下我们到底调用了多少次这个方法。 其调用结构如图所示 当我们输入 N 时方法会进行两调用然后不断地调用其调用的结果如图所示在右下角是有一个空缺区域的但是我们可以把这一块空缺的区域看作一个常数假设它是满的那么我们执行调用的总次数为 F(N)2⁰2¹2²…2N-12N-1(使用等比数列得出结果)所以该算法的时间按复杂度为 O(2^N)。 由以上的计算我们就可以发现用递归来算斐波那契数的算法的时间复杂度太高了也就说明了这个算法的低效。
除此之外其实还有 平均时间复杂度、均摊时间复杂度、最坏时间复杂度、最好时间复杂度 的分析方法有点复杂这里就不展开了。
3. 空间复杂度 空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度。空间复杂度不是程序占用了多少 bytes 的空间因为这个也没太大意义所以空间复杂度算的是变量的个数其计算规则与时间复杂度类似也采用大 O 渐进表示法. 注意函数运行时所需要的栈空间 (存储参数、局部变量、一些寄存器信息等) 在编译期间已经确定好了因此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定。 1. 一个算法在计算机上占用的内存包括程序代码所占用的空间输入输出数据所占用的空间辅助变量所占用的空间这三个方面程序代码所占用的空间取决于算法本身的长短输入输出数据所占用的空间取决于要解决的问题是通过参数表调用函数传递而来只有辅助变量是算法运行过程中临时占用的存储空间与空间复杂度相关 2. 通常来说只要算法不涉及到动态分配的空间以及递归、栈所需的空间空间复杂度通常为 O(1) 代码演示如下:
void bubbleSort(int[] array) {
for (int end array.length; end 0; end--) {
boolean sorted true;
for (int i 1; i end; i) {
if (array[i - 1] array[i]) {
Swap(array, i - 1, i);
sorted false;
}
}
if (sorted true) {
break;
}空间复杂度O(1) 这里需要理解算法在运行过程中临时占用存储空间额外大小的量度这里这开辟 endsorted ,i 三个零时变量因为大 O 渐进表示法3 为常数所以为 1 。 空间复杂度 O(n)
int[] m new int[n]
for(i1; in; i)
{j i;j;
}这段代码中第一行 new 了一个数组出来这个数据占用的大小为 n这段代码的 2-6 行虽然有循环但没有再分配新的空间因此这段代码的空间复杂度主要看第一行即可即 O(n)