我一直在申请工作,每当我听到有关算法时间/空间复杂性的问题时,我都会畏缩和磕磕绊绊。无论我读了多少,我的大脑似乎都被编程为没有得到任何一个,我认为原因在于我因为逃学而导致数学背景很低。这可能不是通常的S.O问题,甚至可能因为从根本上讲数学而被删除,但至少我希望我能找出下一步该问题的去处。
答案 0 :(得分:5)
虽然了解微积分,总结系列和离散数学都是好事,但从你的问题和我在工业界的有限经验来看,我怀疑你的面试官是否期望达到这种理解水平。
在实践中,您可以制作有关时间和空间复杂性的有用的大O语句,而无需进行太多的数学思考。以下是基础知识,我将在时间复杂度方面进行讨论,只是使语言不那么抽象。
大O时间复杂度告诉您算法的最坏情况运行时间如何随其输入的大小而缩放。从big-O函数得到的实际数字是指示算法在给定大小的输入上执行的常数时间操作次数。
因此,big-O函数只计算算法将执行的常量时间操作的数量。
O(k)= O(1),对于任何常数k。
O(f)+ O(g)= O(f + g)
n * O(f)= O(n * f)
O(f)* O(f)* ... * O(f)= O(f ^ n),其中左侧有n个术语
经典的big-O函数是log(n),它总是对应于“包含n个项目的平衡树的高度”。只要知道排序是O(n log(n)),就可以逃脱。
最后,您只报告big-O函数中增长最快的术语,因为随着输入的大小增加,这将主导所有其他术语。任何常数因子也被丢弃,因为我们只对结果的缩放属性感兴趣。
例如,O(2(n ^ 2)+ n)= O(n ^ 2)。
以下是两个例子。
冒泡排序n项
每次遍历项目(至少)将一个项目排序到位。因此,我们需要n次遍历来对所有项目进行排序。
O(bubble-sort(n)) = n * O(traversal(n))
= O(n * traversal(n))
每次遍历项目都涉及n - 1个相邻的比较和交换操作。
O(traversal(n)) = (n - 1) * O(compare-and-swap)
= O((n - 1) * O(compare-and-swap))
比较和交换是一个恒定时间操作。
O(compare-and-swap) = O(1)
收集我们的条款,我们得到:
O(bubble-sort(n)) = O(n * (n - 1) * 1)
= O(n^2 - n)
= O(n^2)
合并排序n项
合并排序可以自下而上,将项目合并成对,合并为四个,四个合并为八个,依此类推,直到列表排序为止。将每组这样的操作称为“合并遍历”。由于n = 2 ^ log_2(n),因此最多可以有log_2(n)个合并遍历,并且在每个级别,我们将合并的子列表的大小加倍。因此,
O(merge-sort(n)) = log_2(n) * O(merge-traversal(n))
= O(log_2(n) * merge-traversal(n))
每次合并遍历遍历所有输入数据一次。每个输入项是至少一个比较和选择操作的主题,并且每个比较和选择操作选择一对项中的一个来“发射”。因此
O(merge-traversal(n)) = n * O(compare-and-select)
= O(n * compare-and-select)
每次比较和选择操作都需要恒定的时间:
O(compare-and-select) = O(1)
收集条款,我们得到
O(merge-sort(n)) = O(log_2(n) * n * 1)
= O(n * log_2(n))
= O(n * log(n)), since change of log base is
multiplication by a constant.
Ta daaaa!
答案 1 :(得分:4)
我不知道为什么工作人员会这样做,所以这里只是几个例子。整个“复杂性”的事情只是提供算法使用多少时间(或内存)的指示。
现在,如果您有一个包含值的数组,则访问给定索引处的值为O(1) - 常量。数组中有多少个元素并不重要,如果你有一个索引,你可以直接获得元素。
另一方面,如果你正在寻找一个特定的值,你将别无选择,只能看看每一个元素(至少在找到一个元素之前,但这与复杂性无关) 。因此,在随机数组中搜索是O(n):运行时对应于元素的数量。
另一方面,如果你已经对数组进行了排序,那么你可以进行“二分搜索”,即O(log n)。 “Log n”是二次对数,它基本上是2 ^ n的倒数。例如,2 ^ 10是2 * 2 * 2 * 2 ... * 2 10倍= 1024,log2(1024)是10.因此,具有O(log n)的算法通常被认为是相当不错的:找到一个使用二进制搜索的排序数组中的元素,如果数组最多有1024个元素,那么二进制搜索只需查看其中的10个元素即可找到任何值。对于1025-2048个元素,它最多是11个偷看,2049-4096它将是12个,依此类推。因此,添加更多元素只会慢慢增加运行时间。
当然,事情会变得更糟。一个简单的排序算法往往是O(n ** 2),这意味着对于只有2个元素的数组需要2 ^ 2 = 4个“操作”,如果数组有3,4 ^ 2 = 3 ^ 2 = 9 16如果数组有4个元素,依此类推。实际上非常糟糕,考虑到只有1000个元素的数组已经需要1000 * 1000 = 100万比较排序。这被称为指数增长,当然它可能会变得更糟:O(n ^ 3),O(n ^ 4)等越来越差。
“好”排序算法是O(n * log n)。假设一个包含1024个元素的数组,这将是1024 * 10 = 10240比较 - 比我们之前的100万个要好得多。
只需将这些O(...)作为运行时行为(或内存占用,如果应用于内存)的指示符。我确实插入了实数,你可以看到数字如何变化,但这些并不重要,通常这些复杂性是最坏的情况。尽管如此,通过查看数字,“恒定时间”显然是最好的,指数总是很糟糕,因为运行时(或内存使用)突然爆发。
编辑:同样,你对常数因素并不感兴趣;你通常不会看到“O(2n)”。这仍然是“O(n)” - 运行时直接与元素数量相关。答案 2 :(得分:3)
分析算法的时间/空间复杂性 - 高中知识应该没问题。我在大学研究过这个。在我的第一个学期,我很好。
基础知识的兴趣领域是:
以上情况适用于分析算法的复杂性。计算问题的复杂性是一个更深层次的领域,仍在研究中 - theory of complexity。这需要有关集合论,计算理论,高级微积分,线性代数等方面的广泛知识。