关于算法优化的思路启发

拿到一个算法题无从下手?怎么办?

我们总是会习惯于责怪自己的知识不足,掌握的数据结构和各种算法不够多,然而,刷题并不是需要等你学完所有的知识才回头进行的过程。

我们需要明确的几件事:

1.暴力、模拟、枚举、dfs等方法最简单有效却最慢

2.上述方法慢的原因是多了很多不必要过程,这些过程经过优化就是所谓的高级算法

举个很简单的例子:

现给出n个数,需求是给出i,要你求1~i位置上的最大值,设查询次数为m。

暴力法:每次询问遍历一遍记录最大值,O(m*len)。

优化思路:假设我们现在有1~i位置的最大值,那么在求1~i+1的查询结果时,只需要max(result(1,i),A[i+1])就可以得到结果。

那么如果查询的i不是依次间隔为1上升呢?

如果题目不是要求强制在线,那我们为什么不能将所有的查询升序排序,现假设第i次的查询为m(i),那么由mi推到m(i+1),只需要遍历得到m(i)+1~m(i+1)上的最大值,和mi的查询结果取max即可,这就是莫队算法(优雅暴力),复杂度就是遍历一遍整个数组O(n)。

如果题目要求结果按照输入的顺序输出结果,你是不是要叹气没法优化了呢?

哎呀呀,它又不是让你一定要查询一次输出一次,那么你只要把查询和它的输入顺序绑定,将查询排序后,按照上述的优化求解,最后把结果放到某个数组中和该查询绑定的输入顺序的索引上,不就可以了吗。

如果再变态一点,要求你一次查询一次输出怎么办呢?

等一下,别总是叹气呀,谁说没有办法了。刚刚我们的优化全是在查询上做的,现在人家只是不让你在查询上动手脚,那可以找别的方法嘛!

你想想,他的mi的取值范围是[1,n],所以你只要把这n种情况处理一下,最后查询的时候直接从所有的解里面拿出需要的不就好了,n种情况处理的复杂度就是遍历一遍数组,复杂度是O(n)。

 

那么更高级一点的优化呢?那就和数据结构扯上关系了。我们知道没有一种数据结构可以解决所有的问题【你发明了,下一届图灵奖就是你的】。各种数据结构都有它擅长解决的问题,也有它自身的缺陷。

优化和数据结构有什么关系呢?当然有咯。

举个栗子叭,现在有n个空队列,现在有如下几种操作:1.翻转队列  2.将某个队列接到另一个队列的后面  3.从队列头取值(空队列输出-1)

先从暴力模拟看起:暴力模拟它的行为,开n个队列,按照相应的操作进行维护。可以手动实现队列,也可以用语言自带的,不过终究是常数级的优化罢了,不改暴力之面目。

说好的优化呢?我们看一下这几个操作,取值O(1),无法优化,翻转O(len),有点费时哦,想想办法,重接链表O(len),看来优化的重点自然是后面两个了。

首先呢,对于翻转,我们不要学乖,让你翻转你偏不翻转,找一些别的方法,比如,打个标记reverse,如果标记为真,表示已经翻转,那么这个时候找头即是找尾,复杂度瞬间变成O(1)了耶!

再来:重接队列。emmm,没有办法嘛?nonono,一样可以打个标记over嘛,如果队尾的over为真,表示这个尾巴不是真的尾巴,而这个值,可以表示真的尾巴所在的队列的索引,然后去那个队列里面找。等一下!这样的话,接过来的那个队列不就不能用了嘛???题目让你开n个你就开n个嘛,就不会多开点,然后建立一张索引表,建立起 队列号->真索引 的映射,又解决了不是,O(1)。嗯,乍看没问题,可是,如果重链接操作过多,数组有可能不够用呢,怎么办呢?

我们绝对不能去复制,这样就成了O(len)的算法,怎么办,有没有适合重接的数据结构呢?链表哇!!!这次可是基于数据结构的优化呢!!!再细细抠一下,因为存在先翻转后重接的情况,所以需要建成双向链表。题解完毕。

怎么样,虽然没有说到一些数学上的优化,但是这些优化对付一般的题目完全足够了哦。

附:如果一个题目的数据状况足够特殊,一般会暗示着与数据状况有关的优化。

例:先给出一个m*n矩阵,矩阵每行,每列都呈单调递增性质,要求你在这个矩阵内找到某个元素x。

A.暴力法:遍历矩阵,找到x则跳出,复杂度O(mn)。

B.让我们先单独考虑一个点A(i,j):

1.假设比x小,并且A(i,j+1)比x大,即A(i,j)是第i行比x小的最大元素,那么如果我们要找x应该去A(i,j)的下方去找,也就是A(i+1~m,j)。

2.假设比x小,并且A(i+1,j)比x大,即A(i,j)是第i列比x小的最大元素,那么如果我们要找x应该去A(i,j)的右方去找,也就是A(i,j+1~n)。

3.假设比x大,那么此时应该是向左或者向上找。

查询起点的选定:我们可以明确的事是A(1,1)是这个矩阵中的最小元素,A(m,n)是最大元素,A(1,n)在第1行最大,第n列最小,A(m,1)在第m行最小,第1列最大。如果选取A(1,1)或者A(m,n)为起点的话,我们在初始的时候就会有两个方向可选,这样很容易在矩阵内画圈行走。

所以我们可以考虑A(1,n)为起点,然后规定行中行走是按照递减的,列中按照递增的,于是我们可以知道,这个约束就是让路线的终点指向A(m,1),行走约束变更:

1.在行中,向左找到一个不小于x的最小元素,如果这个元素等于x,返回坐标,否则下一步向下行走

2.在列中,向下找到一个不小于x的最小元素,如果这个元素等于x,返回坐标,否则下一步往左走

时间复杂度就是O(m+n)。

加速策略:在行中, 由于矩阵的存储是二维数组,可以用二分的方法加速,复杂度O(m+logn)。

再优化:如果我们另外用一个help矩阵将矩阵的列按行存,我们在对列二分的时候,可以借用help矩阵,复杂度O(logmn)。

考虑空间浪费的情况下请自行取舍。

 

你可能感兴趣的:(关于算法优化的思路启发)