本系列是看了牛课网老师左程云http://www.nowcoder.com/live/11的BAT面试算法精品课的学习记录,我争取用我能做到的最容易理解的方式用文字表达出来,欢迎一起学习~
1. 最长递增子序列O(n^2)版
例如:求 2 1 6 4 5 2 7 4 这8个元素的最长递增子序列的长度是多少?
无脑dp:
- 1.用h[i],记录第i个位置的最长子序列的个数,例如h[7]就是上述问题的解。
- 2.h[i+1],通过比较前面i个的数值与当前数值的大小,如果前面的那个数k比当前的数小,那么当前h[i+1]就可以为h[k]+1,遍历完取最大值即可,例如h[0]=1,h[1]=1表示前面一个数 2 或者俩个数 2 1的最长递增子序列的个数都为1,那么h[2],由于6>1,2,那么h[2]可以为h[0]+1,也可以为h[1]+1,总之取最大值即可。
- 3.由于每查看一个数,都要把它前面所有的数都比较一番,查看玩所有的数后总代价为O(n)。
无脑dp代码
2.最长递增子序列O(nlogn)版
还是求 2 1 6 4 5 2 7 4 这8个元素的最长递增子序列的长度是多少?
有脑dp:
1.假设用一个数列h[i]来保存这“最完美” 状态的最长递增子序列,先来讲”最完美“版最长递增子序列怎么跟新的好了:
原则:初始为第一个元素h={2},之后的每个元素,找到h中第一次比它大的元素并替换它,如果没有则加在后面。
- 1.检查到2: h[]={2}
- 2.检查到1:h[]={1}
- 3.检查到6:h[]={1,6}
- 4.检查到4:h[]={1,4}
- 5.检查到5:h[]={1,4,5}
- 6.检查到2:h[]={1,2,5}
- 7.检查到7:h[]={1,2,5,7}
- 8.检查到4:h[]={1,2,4,7}
2.那么我们输出h的最大的元素个数4即使答案,为什么呢?
- 1.事实上,最长递增子序列应该是1,4,5,7。其实从上述第5步时,我们也可以稍微看出一点1,4,5的影子,那么为啥来了个2就把4替换了呢?(看第6步和第5步的差别 )替换有啥用处?以及替换后会影响最长递增子序列个数的结果吗?
- 2.第6步和第5步的差别,为啥来了个2就把4替换了?这里,h[0,1]={1,4}变为h[0,1]={1,2}表示,在还未检查下一个之前,长度为2(俩个元素)的最小的组合为{1,2}(我们可以查看原数列,1,2和1,4长度为2的递增子序列都是存在的,最小的为1,2),这样就可以给后面再来的数字提供更大的空间,即h[i]表示递增子序列长度为i+1时,最后一个数字能渠道的最小值的情况,可以一直更新,为后面来的数字提供空间。
- 3.替换后会影响最长递增子序列个数的结果吗?我们只是替换第一个比它大的数,为后面的可能留空间,并不会影响最长的结果。
2.最长递增子序列进阶O(nlogn)版
把元组(1,2)(3,7)(4,3)(5,4)(6,5),摞起来,(a1,b1),(a2,b2)满足a1<a2,b1<b2
即可把第二个摞在第一个上面,求最长能够摞几个要求时间复杂度为O(nlogn)?
解决办法
- 1.先根据a,按从小到大排序,如果a相同,b则按照从大到小排序,然后每个元组只取b的值,将所有取出来的b根据上述排序排成一列,那么这个新数列的最长递增子序列就是原问题的解。
- 2why?,首先先按a从小到大排序,解决了后面的a比前面的a大的问题,再者,对于b,如果不从大到小排列,那么b最开始就取一个很大的值,那么就可能把一些可能情况排除掉,例如(1,1)(2,9)|(3,4)(2,3)和(1,1)(2,3)(3,4)|(2,9),如果b不按从大到小,第一种,只能摞前俩个进去,后面就不能放上去了,第二种,给后面留了空间,就可以把(3,4)也摞进去了,并且不会影响(1,2)(2,9)这种情况的存在,万一这种也可能时最长的一种是吧?
- 3.总之一句话,a从小到大排列解决了a的问题,b从大到小排列,决解了所有可能的情况,并给后面的一个个b留足了空间。