算法2B----分而治之
divide-and-conquer
- 把问题一分为二
- 如果一分为二后,两部分的耦合性还很强,用动态规划
- 每个子问题的解,还需要做汇总的工作,从而得到原始问题的答案
- 过程中需要利用到递归,而减而治之不如分而治之更适合递归,有爆栈风险
- 两个部长分别做完各自的工作,主席需要做完整体的工作
master theorem 主定理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3A0QBeum-1606029750227)(C:\Users\liusiping\AppData\Roaming\Typora\typora-user-images\image-20200922024039563.png)]
-
抽象对抽象
-
T(n) = a*T(n/b) +O(f(n))
-
关注的是a,分成的子任务数,T(n/b)是子任务的复杂度,例如归并排序在此处就是T(n/2),O(f(n))是合并任务时所产生的
-
logb(a) b表示基底
-
O(f(n)) 通过f(n)可以确定一个上界,不含等于是小o,含等于是大O
-
上图的解释 比较f(n)和n^(logb(a)) ,第一个是小于,第二个是平取,第三个是大于
- n^(log4(2)) = sqrt(n) kd-tree
- n^(log2(1)) * logn = log(n)
greatest slice
- 找到一个子序列,greatest slice就是最大的一个子序列
- 举例,整个投资生涯里面,某个时间段的收益最大,表现最佳
- 可能存在多个最大收益子段,长度都是最短,收益都是最大
- 避免歧义,找出最大收益
- 存在一种歧义,在末尾再加个0,通过取最短
brute force-蛮力算法、直觉的
-
找出任意一个i,j段,然后再把这个区间段里面的每一段累加出来
-
一个外层嵌套的两层for循环,里面例举i,j的子段又用了一个for循环
-
三层for循环
-
时间复杂度O(n^3)
incremental strategy -算法改进一
-
计算一连串的值,值与值之间有相关性,不用再去重新求,可以利用前一个值来计算下一个值
-
与上一步相比,只需O(1)就可以求出子段
-
这样将第三层for循环改成前缀和
-
时间复杂度变为O(n2/2),但是时间复杂度仍然是O(n2)
-
先用一个循环把前缀和全部求出来,存起来
-
再for一个双层for循环,遍历i,j的范围,通过sum(j)-sum(i)即可求出这一段的区间和
divide-and-conquer-分而治之
-
主席从lo到hi的一整段,从中间切分成两段。
-
左主席在左边的子段中找出greatest slice,右子段在右边的子段中找出对应的greatest slice
-
最终解有三种可能
- 落在左边的部长负责左区间
- 落在右边的部长负责右区间
- greatest slice是跨界的,这个事主席做
- 贪心的去找两段
- 所有的以中间点为终点的最大区间和所有的以中间点为起点的最大区间,然后无缝连接起来
- T(n) = 2*T(n/2) +O(n/2) +O(n/2)
- n^(log2(2)) == n ,是打平手的情况,需要乘一个logn,就是O(n*logn)
算法实现
-
long maxSubSum3(int a[],int left,int right) //求a[left..right]序列中最大子段和 { int i,j; long maxLeftSum,maxRightSum; long maxLeftBorderSum,leftBorderSum; long maxRightBorderSum,rightBorderSum; if (left==right) //子序列只有一个元素时 { if (a[left]>0) //该元素大于0时返回它 return a[left]; else //该元素小于或等于0时返回0 return 0; } int mid=(left+right)/2; //求中间位置 maxLeftSum=maxSubSum3(a,left,mid); //求左边 maxRightSum=maxSubSum3(a,mid+1,right); //求右边 // 找出以mid为尾的左区间的后缀最大值 maxLeftBorderSum=0,leftBorderSum=0; for (i=mid;i>=left;i--) //求出以左边加上a[mid]元素 { leftBorderSum+=a[i]; //构成的序列的最大和 if (leftBorderSum>maxLeftBorderSum) maxLeftBorderSum=leftBorderSum; } // 找出以mid开头的右区间的前缀最大值 maxRightBorderSum=0,rightBorderSum=0; for (j=mid+1;j<=right;j++) //求出a[mid]右边元素 { rightBorderSum+=a[j]; //构成的序列的最大和 if (rightBorderSum>maxRightBorderSum) maxRightBorderSum=rightBorderSum; } return max3(maxLeftSum,maxRightSum, maxLeftBorderSum+maxRightBorderSum); }
decrease-and-conquer-减而治之
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sRpOvLe4-1606029750228)(C:\Users\liusiping\AppData\Roaming\Typora\typora-user-images\image-20200922031422009.png)]
- GS(lo,hi) = A[i,j)
- 假设k是第一个让k到hi的举例小于等于0的位置
- 反证,上面两段有相交,起于k,终于j
- s(k,j)必须大于0,不然gs就不是最大子段了
- 所以s[j,hi)<0
- 把s[j,hi)删除掉,对gs这样一个目标子串是没有影响的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xeC4P7NU-1606029750229)(C:\Users\liusiping\AppData\Roaming\Typora\typora-user-images\image-20200922031850655.png)]
-
一趟循环
-
邓公演示
-
上三角矩阵
-
从n,n开始,如果发现(x,y)的后缀和是负数,就跳到(x-1,x-1)的位置去重新开始
multiplication:naive
-
大数乘法
-
两个数相乘,可以拆成(a+b)*(c+d)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oB1IAmSy-1606029750231)(C:\Users\liusiping\AppData\Roaming\Typora\typora-user-images\image-20200922034025621.png)]
- 分成4个子任务
- 这不是乘法运算,这是移位运算
- n(log2(4)) = n^2
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ipfEvde8-1606029750233)(C:\Users\liusiping\AppData\Roaming\Typora\typora-user-images\image-20200922034422452.png)]
- b*c + a * d不用重复计算,4个乘法简化为3个乘法
归并排序
- 将需要排序的数据分成两段
- 接下来继续将需要排序段切分成两段
- 分到一个数,变得不值一提时,作为递归机,它的答案已经获得了
- 更关键的步骤是merge,将两部分部分有序的合成一个整体有序
algorithm
-
mergesort{
mergesort(左)
mergesort(右)
merge
-
}
2-way merge
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wa3ZrTGl-1606029750234)(C:\Users\liusiping\AppData\Roaming\Typora\typora-user-images\image-20200922035419472.png)]
- 二路归并
- 假想是两个队列
- 贪心的把队首两个最小的数更小的数放到空队列中
- 然后把移除队首的队列向前移动一次,顺次替补,暴露出新的队首
- 然后再做比较,如此反复下去,一个个摘出最小的,放入空队列中
- 说服自己,这只需要线性的时间
- T(n) = 2*T(n/2) +O(n) 为什么是O(n),因为每经过一个时间都可以插入一个元素
- n(log2(2)) =n 对等,所以是n*logn
排序比较
-
选择排序
- 后缀有序,前缀找出最大的,再和界桩交换
- 没有最好的情况,n步迭代,每一个都要走到头,
- O(n^2)
-
插入排序
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kVEgjXJS-1606029750234)(C:\Users\liusiping\AppData\Roaming\Typora\typora-user-images\image-20200922040810773.png)]
- 取出来的每一张都进行排序,
- 向左发射线,找到合适的位置插入
- 插入的复杂度刚好是射线的宽度,刚好是k这个元素所对应的锅的总数I,有多少个逆序对
- 时间复杂度是O(n+I) I是逆序对总数
- 有更好的情况,输入的序列正好有序,此时时间复杂度是O(1)的
- 具有输入敏感性,带有随机性,可能直接就是有序的
-
无序的队列中至少有一对逆序对(inversion)
-
越混乱,逆序对越多
-
inversion = I
-
等于第k大的数的逆序对的总和
-
第k大的数的逆序对个数,等于前面比它大的数的数目
-
怎么求逆序对?
- 最多的逆序对个数是n里面2的组合数
-
counting inversions
- 怎么把最后跨两个区域的逆序对计算出来?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wE0fpcO7-1606029750235)(C:\Users\liusiping\AppData\Roaming\Typora\typora-user-images\image-20200921210546880.png)]
-
把整个数据段分成两个部分,让两个部长去计算各自的逆序对
-
作为主席,统计跨越两者之间的逆序对,再加上两者算出来的各自逆序对,就是所有的逆序对
-
实际利用的是mergesort,做一些附加动作
-
2-way merge注意在头上,搬走最小的,LL已经被归并掉了,LR是参与下来的,i(k)就是k这个节点的逆序对数目,在右侧的队伍有元素要出队时,此时左侧的队伍总数就是逆序对数目
Majority
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ieQStYjo-1606029750235)(C:\Users\liusiping\AppData\Roaming\Typora\typora-user-images\image-20200922041941652.png)]
- 集合中若有一半以上元素同为m,则称之为众数
减而治之
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hln4109v-1606029750236)(C:\Users\liusiping\AppData\Roaming\Typora\typora-user-images\image-20200922042041789.png)]
- 减成P段,和A-P段
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0oVS6isE-1606029750236)(C:\Users\liusiping\AppData\Roaming\Typora\typora-user-images\image-20200922042405167.png)]
- 当maj 加加减减等于0的位置的那一段就可以减除
linearSelect(A,k)–线性选择
-
既是分而治之,也是减而治之
-
首先需要选择一个不太大的常数Q
-
算法是递归的
-
如果A的绝对值比Q小,把n分成Q段
-
对每一段排序,找出每一段的中位数
-
找出中位数中的中位数M,自己去调用自己,是一个递归
-
通过M值将数据段切分成三段,
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kchQmvbg-1606029750237)(E:\ITSoftware\typora\documents\interview\summaryByCuiWei\image-20200921213249745.png)]
- L是比它小的,减而治之,把E和G减除掉
- E是等于它的,骑墙的,把L和G减除掉
- G是大于它的,把L和E减除掉
-
两个linearSlect 找出中位数的是O(n/Q) 切割成三段后的时间复杂度是O(3n/4)
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5S5d71Q8-1606029750238)(C:\Users\liusiping\AppData\Roaming\Typora\typora-user-images\image-20200922044037798.png)]
-
可以减除蓝色或者绿色部分,是四分之一
-
虽然称是O(n),但是前面的系数很大,只有理论意义,但是实际很大,在实际中更推荐quickSelect,虽然最坏的很坏,但是平均的效果还是很好的
Diameter of A binary tree 二叉树的直径问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VN5lXwpA-1606029750238)(C:\Users\liusiping\AppData\Roaming\Typora\typora-user-images\image-20200922044252244.png)]
-
图中的最长直径,简化成二叉树中的最长直径
-
左右子树分别找出相应的直径
-
领导不是简单的比较左右孩子,取其大者,而是找出左右孩子最深的叶子节点,相加再加2,这样就有了三个候选
-
找出这三个候选的最大者
closest pair(最近邻)(计算几何)
-
平面上一系列点,哪两个点之间的欧式距离最短
-
O(n^2)的时间复杂度很糟糕,要去除这种思路
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rPSThFQ6-1606029750239)(C:\Users\liusiping\AppData\Roaming\Typora\typora-user-images\image-20200922044756586.png)]
-
分而治之,不要想着不劳而获,你自己需要去找跨国婚姻的解
-
branch-and-bound
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RcYoQac9-1606029750240)(C:\Users\liusiping\AppData\Roaming\Typora\typora-user-images\image-20200922044934087.png)]
-
分支定界(利用之前的结果来计算后面的值)
-
找出了一个delta L 和 delta R,其中更小的是delta,从而封闭出一个条带
-
如果所有的点都分布在条带区域?
-
- 八段拐,最少不能少于delta
- 充其量不过6个点
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PpLiJ8xn-1606029750240)(C:\Users\liusiping\AppData\Roaming\Typora\typora-user-images\image-20200921220113091.png)]
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5dWaSMhs-1606029750241)(C:\Users\liusiping\AppData\Roaming\Typora\typora-user-images\image-20200921220326250.png)]
- 对于左边的点,在移动窗口,找出对面的点,不会超过6个
-