算法-2B

算法-2B

接之前的算法-2A,后的第四次大课

分而治之——1

在学完简单数据结构,贪心算法,减而治之后,我们来到了我们的分而治之章,这是算法里同样很重要的一个章节。
在这里插入图片描述
这是分而治之的一个简单演示图,把一个规模为n的问题,分为两个规模为n/2的子问题,通常我们一般是分为两个子问题(当然具体问题具体分法),这两个子问题的规模合一定不能超过原问题,不然就越分越多了,通常两个子问题之间也尽量独立,不要有联系,如果两个子问题之间存在联系,可以考虑用我们后面学的动态规划来解题试试。
在这里插入图片描述
提到了动态规划,就不得不提到主定理,主定理是我们在进行分治时计算时间复杂度绝好的一个方法,也有人叫大师定理。
在这里插入图片描述
如图,式子大概意思是,把一个n规模的问题,分成a个子问题,每个子问题的规模是n/b,分治时其它操作的复杂度是f(n)。那么时间复杂度该如何来计算呢?这里用到了log b a,a和b的位置也特别好记,因为b可以理解为base基底的意思。
在这里插入图片描述
这里的small-o和大O的唯一区别就是小o是严格小于的非渐进上界,而大O是小于等于的渐进上界,希达则为紧确界。主定理的主要内容就是,比较分治步和其它操作步的时间大小,分治步的时间大小算法是n的log b a次方,而其它操作步的时间大小就是f(n)。如果分治步的n的log b a次方大,则总复杂度就是n的log b a次方。如果操作步的f(n)大,则总复杂度就是f(n)。如果两个一样大,则总复杂度就是n的log b a次方(或f(n))log n。注意log n是固定的,不会因为n的log b a次方(或f(n))的大小而变化,小心log n的n的理解。
在这里插入图片描述
w是非渐进紧确下界,Ω是渐进紧确下界。来看这图中的几个例子,会发现我们的主定理特别好用。
在这里插入图片描述
现在让我们来看看一些实例,比如这里是一个相当经典的最大切片问题,题意如图。找到一个序列中总和最大的区段。如果我们用最蛮力的办法可以发现,用一层循环固定起始的i一层循环固定末尾的j,再一层循环来进行区间内的和,则可以暴力算出所有结果,代码如图。方法是可行的,但代价实在太大,是n的三次方的,明显不是一个好算法。
在这里插入图片描述
那么接下来我们来看看有什么优化方法嘛?在第三层循环的时候,我们是遍历区间相加的,仔细思考会发现这里我们做了很多重复的工作,因为我们每次区间相加时,每个区间之间相差就一个元素,我们只要在前一次的基础上,对一个元素进行处理就可以了,这很好的帮我们消除了第三层循环,把时间复杂度降到了n的平方。
在这里插入图片描述
那么想想,还有更好的办法嘛?肯定是有的,我们今天讲的分治思想还没用到呢,那么我们来看看用分治思想后我们的时间规模是多少。我们每次把问题分为两部分,分别去求两个子区间里的最大总区段,这时候有人肯定想到,最大总区段除了在左边区间和右边区间外,还有一种可能,就是总区段处于中间,横跨两个子区间,这时候,就是我们除了分这一步外,其它操作需要顾及到的了,我们以分割的红线为起点,分别向左和向右找到起点固定,但终点不固定的最大总和区段,这个规模我们不得不承认是线性也就是f(n)为n的,然后把这两个起点固定终点不固定的最大总和区段合起来,和我们的左边区间的最大总和区段和右边区间的最大总和区段进行比较,这三个值里,最大的那个就是我们所想得到的最大总和区段。那我们来看看复杂度为多少呢?用上主定理后算出n的log b a次方也为n和我们的f(n)一样大,那么最终结果就是n
log n的复杂度。这个分治的思想相比两个方法上明显时间复杂度小很多。
在这里插入图片描述
但惊人的是,这还不是最好的结果,最好的结果是可以达到线性,也就是n的规模。前提是我们发现一个规律,在我们的问题规模上,最短的非正后缀一定与我们的最大总和区段无交。我们很容易用反证法证出。如果存在最短非正后缀与最大总和区段有交,那么交集一定是大于0的,则最短非正后缀除去交集部分一定是小于0的,那我们就找到了一个更短的非正后缀,这与我们最开始的最短非正后缀是相违背的。所以最短的非正后缀一定与我们的最大总和区段无交这个规律成立。那么知道这个规律有什么用处呢?则我们可以从最末端开始相加,如果加起来的后缀小于0,则之间切掉该后缀,并把切掉后的问题当成一个新问题,继续从后缀开始。加的时候记录期间最大值,一直加到lo端也就是从末端加到了起始端,记录的最大值就是我们想要的结果最大值。而且我们只是从后往前遍历了一遍,时间规模是为n的,比分治的方法还要小,而这里我们用的是减治。所以没有哪个算法是最好的,具体问题具体分析。
在这里插入图片描述
这是减治的代码实现过程,也很是简单简洁。
在这里插入图片描述
减治的演示过程,这个图超直观,大家可以品味一下,从右到左每一列都是切除非正后缀后我们得到的新问题。

分而治之——2

在这里插入图片描述
分治另一个典型的应用就是大数乘法,当两个乘数规模很大时(假设都是n规模),我们需要的时间也就很多,这不利于我们快速得到结果。那么用分治的方法呢?把第一个乘数分为A和B两部分,第二个问题分为C和D两部分。最后我们只要把四个值相加就好,这部分明显是n规模的(其中AC乘BD规模个10时,乘10运算在计算机内部特别方便,只要移位就好了,我们可以把这步理解为n规模内可以达到)。但运用主定理,发现分的这一步,规模还是n平方的,那么总规模还是n平方的,这可让我们苦恼不已。
在这里插入图片描述
别急,方法总会有的,这时候我们发现中学学过的一个分解式,帮助了我们简化这步运算,如图的式子,这样我们我们本来的四个子问题,现在只要三个子问题的乘法就够了。再用主定理,发现时间规模也比n平方小了,很是神奇。
在这里插入图片描述
另一个分治思想的伟大应用就是我们的归并排序。如演示图所示,我们只要不断的二分,二分到最底层时,递推回去只要加一个n规模的比较大小操作,如果主定理记得熟了,很快发现分的规模和其它操作规模都是n的,那么总规模就是nlog n的。这是分治思想的一个伟大应用,产生了一个绝好的排序方法。
在这里插入图片描述
伪代码也很简洁,稍微复杂一点点的操作是归并那一步,读者可好好品味。
在这里插入图片描述
递推回次顶层时那一步的演示图。
在这里插入图片描述
二路归并排序是个伟大的算法,因为它是第一个达到小于n平方的排序算法,快排是在其之后产生。
在这里插入图片描述
在讲分治的时候,邓老师回顾了我们之前讲到过的插入排序和选择排序,插入排序比选择排序有着好很多的性质,插入排序具有输入敏感性,如果输入的例子足够好,则n规模内就可以排好序,哪怕最坏的结果也是n平方的,这是选择排序没有的优点。而决定插入排序每一次插入要交换多少步的因素是,插入的那张牌的前面有多少逆序对,逆序对越多,时间越高。(这里要注意不能用快速查找的log n来代替每一步插入的需要的n,因为交换移动一定是n规模的)
在这里插入图片描述
那么我们知晓了这个,来解决计算n规模的问题里,有多少逆序对就很方便了。正常情况我们会考虑暴力计算,需要n平方的时间。而我们知晓逆序对的计算,分治和归并排序算法,我们发现,在分到最底层,递推回去时,两个子问题中后面的子问题,比前面的子问题中的元素先进队列时前面子问题中有多少个元素,则就存在多少个逆序对,这样递推回最高层,我们途中进行累计,就可以计算出整个问题规模的逆序对。这也是一个极其巧妙的方法。
在这里插入图片描述
在nlog n的时间内就可以得到我们想要的结果。

分而治之——3

在这里插入图片描述
再来看集合里的众数问题,集合中若有一半以上的元素同为m,则称之为众数。那么怎么来求一个集合里的众数呢?(如果规模不是很大,可以用哈希表)我们也找到一个规律,如果A有众数,前缀p中,元素x出现的次数刚好是一半,则可以除去p,因为无论x是不是我们的众数m,它都不会对后面的A-P存在众数m产生影响。这个和我们上面的最大切片的减而治之很像,这里也是减而治之的思想。
在这里插入图片描述
在我们找到后,要检验它是不是众数,因为我们的方法不能判别没有众数的情况。但检验的方法也很简单,只要遍历一遍就可以了,在n的规模内就可以达到。检验的代码图中没写。
在这里插入图片描述
再来看一个我们熟悉的问题,找到一个集合中第k大的数,这个我们之前讨论过,用猜法可以大概在nlog n的时间内找到,但是如果是最坏情况,每次排除的部分都很小,那么时间复杂度还是可能达到n平方,虽然这种可能性很小很小。那我们来看一种新的方法,线性选择,我们给定一个Q=5,把n规模分为n/Q个问题,每个问题规模为Q(也就是5),在n/Q个问题里选出中位数,不可否认这一步是常数规模内的,因为每个子问题规模为5特别小,常数内可以找到中位数,所以这一步总规模又是为n的。然后对该n/Q个中位数递归该操作,知道最后剩下的元素不超过5,则找到最后的中位数,这里有着很多个n规模的操作,在我们找到最终的中位数后,进行划分,存在三种可能,如图所示,黄色的部分是我们找的中位数。后面的过程和我们猜法的过程一样,如果是第k大的元素则返回,不是则减除再迭代。
在这里插入图片描述
这个方法和猜法的区别就在于,避免猜法的最坏可能,这里选的最终中位数,都是我们用了很多个n规模得到的,它有自己的意义,它绝不会是比较坏的结果,它一定可以让我们减除掉最少n/4规模的问题。证明如下图:
在这里插入图片描述
我们的m一定比4/n规模的元素大,一定比4/n规模的元素小,固处于较中间的一个位置,每次可以除去相当可观规模的数据,这是我们的减治思路。那总体规模呢?如图中式子所示,前面c个n规模,后面要想让n/Q+3n/4<n则Q最小为5。所以最终规模为线性n(这里的式子这样相加,不很理解为什么这样)。但由于这里的c其实是非常大的,甚至超过常数范围,所以这个只是理论上线性的,很多时候我们还是用猜法更好一些。
在这里插入图片描述
在找二叉树的最大直径问题里,我们也很好的可以运用我们的分治思想,如图所示,最终只要比较三个值就可以。
在这里插入图片描述
再来看一个最近邻的问题,在n个点内,找到距离最短的两个点。(这是一个几何问题)当然暴力的思路还是n平方的。
在这里插入图片描述
那么分治呢?分治思想的话,分为两边,两个子问题,但其它操作的规模是n/2*n/2的还是n平方的,并不能很好的降低我们时间复杂度。
在这里插入图片描述
但是因为是几何问题,在我们找到左边最短,和右边最短时,选出一个更短的,然后中分隔线附近,我们处理其它操作(两个点在两边)时,可以淘汰大部分点,只考虑点在离分割线不超过子问题更小值的范围内。这样其它操作的时间复杂度就降低了。
在这里插入图片描述
但是如果不好的情况,作用两边的点都在该范围内,只是纵坐标不同怎么办呢?不用着急,因为我们找的是两个子问题中更小值,那么对于一边而言,该边的任一一点,对另一边需要考虑计算的点最多只有6个,而6是常数,那么这一步最多就是6n也就是n,所以其它操作的复杂度也就是n规模的。
在这里插入图片描述
通过主定理可以计算出,总复杂度比之前的n平方,现在只要nlog n。
在这里插入图片描述
对于该边的每一个点 对 另一边常数点的计算,邓老师给出一个窗口的概念,并提示可以通过归并的方法完成我们窗口的入点和出点。感兴趣的同学可以细细品味。

结尾

算法艰难,坚持就有可能让你在以后某些时候,更容易一些。开启你的55开人生。

猜你喜欢

转载自blog.csdn.net/qq_42529477/article/details/106802739
今日推荐