算法相关

算法相关。借鉴各种资料以及《数据结构与算法经典问题解析java语言描述》。以前有个大佬说,语言只是工具。

大O:复杂度的上界,一般考虑这个,因为现实很残酷,只能考虑最差情况。

Ω:复杂度的下界,没什么意义。

Θ:上面两者的平均值。

递归:

    递归需要有终止条件,这里就可以很好的理解栈的大小决定了函数循环引用的深度这个问题了(Xss)。当递归调用的深度太深了,就算没有栈溢出也会占用很多运行时间(曾经测试过斐波那契函数),为了避免出现这种情况,可以使用迭代。迭代就是用while循环进行处理。

链表:

    跳表,可以增加查找的速度。

    另外提到了一种高效的双向链表存储方法,很有意思。通常的双向链表都需要前后两个指针来进行关联,而提到的这种方法只需要一个指针,利用前后两个节点的地址进行“”异或“”操作,就可以通过前地址知道后地址,通过后地址知道前地址。开头和结尾的需要异或null,也就是0。

    问题九:判断一个链表是否有环。1、使用集合散列表等,保存地址。2、或者使用Floyd方法,两个指针不同跨度向下走,出现了两个指针地址相等的情况就是有环。

    问题十七:两个链表交汇处。1、两个指针,两个集合或者散列表,如果A指针的值出现在了B的集合中或者相反,则这个点为交汇点。2、遍历并用两个栈保存数据,弹出的时候最后一个相同的值就是交汇点。

    链表可以结合栈来逆操作。

栈:队列:

树:

    使用递归或者迭代

    二叉搜索树BST,左边的小于父节点,右边的大于父节点

    平衡二叉树AVL树,二叉搜索树会变成倾斜树,如果节点的左右字数高度差为0,则为完全平衡二叉树,如果高度差为1,则为平衡二叉树AVL树。平衡二叉树在插入节点的时候,可能会带来不平衡则需要旋转。

    插入的情况有:

        左子树的左孩子LL   单旋转

        右子树的右孩子RR  单旋转

        左子树的右孩子LR  双旋转

        右子树的左孩子RL  双旋转

LL如下图所示,其中6是平衡节点(本处为根节点),9才是不平衡节点,所有旋转需要围绕不平衡节点进行,需要做的是,将左孩子的右节点赋给不平衡节点的左节点(左孩子与不平衡节点断了联系,此处为null),左孩子的右指针指向不平衡节点(联系重新建立),并根节点指向左孩子,左孩子取代不平衡节点。

LR如下图所示。新插入的节点是7,但是5是平衡节点,8是不平衡节点。首先旋转左子树,6的左子树给5作右子树,6的左子树用5替代,6取而代之5,这是一旋,出现了以8 为不平衡节点的LL情况。与上类似,要用左子树来替代。上图为null的赋值就体现价值了,6的右子树作为8的左子树,8作为6已空的右子树。平衡完成。

节点定义至少包含:value,height、*left、*right,height很重要关系到树是否真正平衡。

问题七十五:给定高度h,创建最少节点的avl树。只需要创建两棵子树,一颗高度h-1,一颗高度h-2即可。

抓紧递归的思想,叶子节点左右子树为null的思想

红黑树:我选择放弃

 

优先队列:

    堆:如果不符合最大堆或者最小堆,那就是二叉树,而不是正经的堆。

    插入,向上渗透,删除,向下渗透。最简单的适用于数组的实现方法,链表的话需要双向链表才能找到父节点.

    问题十七:找到最大堆的第x大数。善于利用其它手段,比如建立一个容量为x的最大堆。

    问题二十八、二十九、三十:28滑动窗口之和最大值:蛮力法咯;29滑动窗口最大值:可以用多个指针,可是我一直没搞懂,也不打算纠结了,改用最大堆。30:就是28没搞懂的那个方法...坑还是要填的:源数组A,tempB,只要A删除的数据不等于B那就大胆删除&添加小于等于的新数据到tempB!这才是关键!但是如果是等于,那必须删除。

    

图:

数据实现有链表、邻接矩阵。

 

拓扑排序:

按照前缀为空的顺序排序,用邻接矩阵合适,删除的节点,把他当前缀的节点数据也置为0,即可。拓扑排序结果可以为:7/5/3/11/8/2/9/10,也可以为3、5/7/8/11/10/9/2。可用于某件事的先决条件的判断,如选课。

最短路径算法:

无权图最短路径,类似于广度优先搜索BFS,查到了就结束。

有权图最短路径,Dijkstra可以往读过的回写,并且有改动的,回溯都重新改一次!这样可以得到最优解。如果A-Bscore为5,那个B-Ascore就为10,肯定不会覆盖;如果A-C为2,B-C为10,那C回溯就会出现C-B为7,比B-C的距离更小,结果更优。

例子:一个矩形,从右上角到左下角的做小和。fuck,就是这个Dijkstra算法。

最小生成树:由联通图可以产生很多棵树,而权值最小的树就是最小生成树。 懒得深究了

图的问题没有细看。。。。。。。需要补充上。。。。。。。

 

排序:

冒泡:

选择:选择最X的,放到指定顺序的位置。

插入:前面的已经排好序了,接下来这个继续插入。

希尔:升级版的插入排序,一般从n/2为跨度缩小。

归并:升级版的已经排序好的两个数组进行合并。

快速:类似于二分法排序。

堆:如前节,向上渗透等。

-------

计数:太麻烦,不懂

桶排序:数据间差别跨度不大,用数组记录出现的次数。一直理解为计数排序,如果桶数量不够,那就要结合归并排序了。

外部排序:100M内存排900M数据,每次获取100M排好,再每个节点获取10M排好。循环。

问题三十三:A数组m+n大小m个数,B数组n,合并到m。这里有个点就是A移动到后面,前面的数据就是废的,可以被覆盖,所以A的大小永远可以包含AB所有的数据。

 

散列:

布隆过滤器:元素一定不在(有一个为0)或者可能在(都为1)。

 

字符串算法:

待看

 

算法设计技术:

引出三大算法设计思想:贪婪、分治、动态规划。

贪婪算法:每一阶段,获取局部最优解

分治算法

    分:大事化小

    治:一般是分与治,求解

    合并:二分查找不需要合并,快速排序需要合并

   例子:x的n次方二分查找快速排序斐波那契(1、自上而下,需要递归;2、自下而上,while就行;3、有个除以根号下5的方法,不晓得是什么;4、利用矩阵:如下图);

      矩阵乘法(1、划分为4个的两个block的普通矩阵乘法,就是分治法,复杂度为n的3次方;2、斯特拉森矩阵乘法,复杂度为n的2.81次方,比较有意思有空可以看看)。

      快速排序注意:

          1、如果是从小到大,请先找小的

          2、i=j,判断下value和base,正常是直接换,但是如果只有两个元素,还是要对比一下(guan jian)。

      芯片中的单位怎么排列,占用的面积最小,有个比较有意思的例子:如图所示:

 

动态规划算法???又忘了???

 

贪婪思想:

贪婪算法的应用:

    选择排序、拓扑排序、堆排序:都是找到局部最值,然后再一次一次循环等

    Dijkstra算法:很典型的找到当前最值

    背包问题:按照性价比排序

    赫夫曼编码:按照频率排序,赋值为fuck等。

    赫夫曼例子:

        问题:普通文件读取的时候,每次读取8位表示一个字符,而现实就是,并不是所有的8位都有用,有的频率高有的频率低,所以这种效率比较低。据统计,e的频率是f的10倍,如果用1表示e用0001 0000表示f,那么就省了很多的存储空间和网络带宽。

        原理:假设有以下的字符,且频率已知。

构建一颗二叉树,从最小值的节点开始,那么bc为兄弟节点,父节点值为9;9节点与a节点兄弟节点,父节点为21;de小于21,兄弟节点,父节点为27;21-27,父节点为48;48-85,父节点为133。如下图所示。

由于133是所有点公共的,可以不用赋值,赋值了也是浪费。左边为0,右边为1(也可以反过来)。所以得到c为0001,f为1,所有的节点都不互为前缀。频率高的长度小,节省了大量空间。当然,具体节省了多少空间,还是需要具体判断。

如eeabbcd为011011001000000000001010,取值的时候,就按照图来索引,准能找到对应的值。长度24 vs 7*8

问题三:贪婪算法,先排序,找到最小的和,再一步一步往上叠加。贪婪思想要称为根本!!!

问题十一:兑换货币:1、5、10、15、20、25 、50兑换40,用最少的数量。贪婪算法最优解为25 、15 、5,而实际最优解为20 、20。据说动态规划能解此惑。

 

 

 

 

分治思想 Divid and Conquer:

分治思想的体现:归并排序、快速排序、递归、forkjoin

分开

递归

合并

三个套路。一般用来和迭代相比较。迭代就是while条件的那个实现。

 

 

 

 

动态规划思想Dynamic Programming:

动态规划需要和备忘录进行配合。所以Dynamic是动态,Programming是备忘录表格。备忘录表格记录动态规划得到的各个值。(为了避免大量的重复计算,节省时间,我们引入一个数组,不管它们是否对最终解有用,把所有子问题的解存于该数组中,这就是动态规划法所采用的基本方法

DP = 递归 + 备忘录,我觉得不能称之为递归,而是应该称之为 迭代 +  备忘录 。

自顶向下、自底向上两种方法,使用动态规划就是怎么使用递归,并且能不能用备忘录来减少重复计算。

比如feibo5=feibo4+feibo3=feibo3+feibo2  +  feibo2+feibo1 = feibo2+feibo1 + feibo1。。。等,这里feibo2就是重复的。

普通递归:feibo5 = feibo4+feibo3;自顶向下

备忘录:for(int i = 0; i < 5; i++){feibo[i] = feibo[i-1] + feibo[i-2]} return feibo[5];自底向上,这就是上文说的迭代么。。。

使用场景:

背包问题、旅行商问题、最长公共子序列、最长递增子序列、最长公共子串等

问题四:数组中和最大的子序列。如arr -2 、11 、 -4、 13、 -5、 2。

      方法一:,创建一个备忘录A[n],如果arr[0]> 0,A[0]=arr[0],否则A[0]=0,然后循环arr(i-1)+Ai得到值X,如果X > 0则赋值给Ai,如果X< 0,Ai=0;从而得到A 0、 11、 7、 20、 16、 18循环的i-3- 20,所以长度为20不停减去13 -4 11 直到0,得到结果。如果arr都为正,Amax肯定就是最后一个;如果A都为0,那arr都为负,得循环arr找到最大单值。

      方法二:arr[i]开始加,如果sum>0就保留,如果sum<0就丢弃重下一个开始重新计算;期间判断Max的与sum的大小,最终的Max就是需要的Max。

问题五:arr[] = {3,1,4,1,5,9,2,6,5}的最长递增子序列长度。

      方法一:

Arr: 3、 1、 4、 1、 5、 9、 2、 6、 5

A:   1     1     1     1     1     1     1    1     1

A:    1     1     2     1     3     4     2   4    3

例如9,5小于9,则1+3=4;继续,1小于9,则1+1=2小于4忽略;4小于9,则2+1=3小于4忽略。最终得到9值为4即可。

      方法二:排序号arr得到arr2,再按照最长公共子序列马拉车方法得到公共子序列。因为有排序的arr2保证了递增的顺序。

     

 

 

 

背包问题:按照价值体积比计算,是贪心算法。

个人认为,贪婪、分治、动态规划都是思想,递归、快速排序、等都是实现方式。

 

 

 

 

 

 

 

 

 

 

 

 

杂谈

1、某个数字n第k位是否是1,将第k位置1等。

书本直接为 n&(1<<k-1) = true or false。个人觉得错误,应该是k为偶数,不用减一,k为奇数,需要减一。这样可以保证只受第k位的影响,不受其他位的影响。

例如,0111,按照书本,k=4,true。所以k为偶数的时候,不能减一。

2、第k位清0,需要用到补位(取反)。

n&上面的流程取反。

3、取模:n%k  =  n&(k-1)。

4、螺旋输出二维数组:迭代每一个最外圈。

5、数组翻转问题,如果没有额外内存空间,考虑下分段翻转+整体翻转。

问题5:将一段文字的所有空格转移到左边。这个开头有意思,ij两个指针,i为前驱,只要i不是空格,就将i值赋给j,j只有在赋值之后,才移动一位。这样i到0的时候,j就是一个完整的串。如果ij是文字开头,也可以交换,只有一个word,不影响效率。也可以用效率更高的类似于快速排序的方法, 后面段遇到空格就卡顿,直到前面段有字符可以来进行替换。前者顺序不变,后者顺序会变。

 

 

 

 

 

 

 

 

 

 

 

 

有意思的算法s

1、manacher算法

    manacher算法用于最大回文串的查找。一般查找方法是按顺序以某个点为基础,从两边扩散。如果回文是BAB型可以直接用,如果回文是ABBA型就得多做一次处理。manacher的思想是,将待查找的串中的每一个字符都添加一个符号,包括开头和结尾。将符号作为一部分进行两边扩散,这样就成为了#A#B#A# 和#A#B#B#A#,可以直接拿过来用,不考虑奇数或者偶数类型。

2、最长公共子序列

    使用动态规划算法,在局部找出一个解,那么最终解=剩下的部分的解+这个解的组合。

    子序列不是子串,子串需要连续子序列不需要连续。

      回溯,自己举列子试下。例子就是上面的,注意一个点:右下角的左、上值是否相等,Yes:下一步左上角;No:上、左是否相等,yes:确定一个方向,continue,no:选择大的值,continue!

3、归并排序

      通常我们拿到一个数组进行归并排序,我们想到的是使用分治法,先分开、治理(递归)、合并,这是自顶向下的思路,通常的理解。如果我们从底向上,就可以只用治理、合并,比上面的少了“分开”这一步,似乎简单很多

4、尾递归

      正常:Feibonache(n) = Feibonache(n-1) + Feibonache(n-2);

      尾递归:从尾巴开始,Feibonache(n){ int i = 0; int  a, b; while(i < n){ b = b + a;} return b;} 类似于这种

 

 

 

整合算法设计http://open.163.com/newview/movie/free?pid=M6UTT5U0I&mid=M6V2UIKLL相关

动态规划

    LCS longest-common-subsequent最长公共子序列。

    使用的是动态规划思想:

        假设有两个字段X、Y,他们的公共子序列长度为C,则有如下的关系:

            C[i, j] = C[i-1, j-1] + 1; if X[i] = Y[j];

            C[i, j] = max{C[i-1, j], C[i, j-1]};if X[i] != Y[j];

        这里可以看到,前面是什么不管,反正只看最后一个,次最后的答案交给次后的决定,动态规划的思想。

    实现的话,使用二维数组C作为备忘录,记录。初始化为0,如果有相等的数,则值为左上角+1,如果没有,则取左和上的最大值。

      一个二维数组 m * n ,每个块都有权值V(或每条边都有权值),从右上角到左下角的最大乘积,S。

      同样的道理:

          S[i, j] = Vij + max{S[i-1, j], S[i, j+1]}; if 节点处于中间;

          S[i, j] = Vij;if i = 0 && j = n; 起点

          S[i, j] = Vij + S[i, j+1]; if i = 0 || i = m; 只能左移

          S[i, j] = Vij + S[i-1, j]; if j = 0; 只能下移

       最终,例子1直接得到值或者回溯得到路径,例子2直接得到值或者怎么得到路径,请继续

Hash

    开放寻址:我们通常是一个hash函数,碰撞了就是使用链表接着继续保存。这里是使用多个hash函数,有一个顺序,如果碰撞了就按照顺序继续直到找到slot或者value。没什么卵用。。。还是记录下吧

    linear hash,线性hash,多个hash函数是什么关系:h(k, i) = (h(k, a) + i) mod m

    double hash: h(k, i) = (h1(k) + i * h2(k)) mod m

    据说double 的冲突比linear的少很多,性能更加优秀。m为一个大素数。

平衡搜索树

    包括:AVL树(高度可以差1),2-3,2-3-4,B 树, Red-Black 树, skip-list等

    红黑树为重点,假设A为black, AA为red。其特性有:

        节点为红色 或者 黑色

        根节点和叶子节点(nil)为黑色

        红色节点的父节点为黑色

        某节点到叶子节点的黑色节点数目,左右子树相同

贪心算法----最短路径 Shortest-Path

    Dijsktri算法:

        一种贪婪算法greeny,得到每个局部的最优的解,比如A到BCD的最优解,再获得BCD到EFGH的最优解。从而得到最优解。

        不能有负值的环,比如A到B为5,B到A为-5,且A到B为最短路径,那么就会陷入死循环。

        估计Dijsktri算法是以点为基础的。

    BallMan算法:

        同样的贪婪算法,初始化的时候,给每一个边预定一个顺序,初始化为无穷大,从边#1开始,与上相同,保存最小值。n个节点则循环n-1次,每次都从#1开始。因为比如第一的时候,BC都是无穷大并没有进行任何优化操作。

        这里可以有负值的环,接下里有什么效果,请继续

 

估计: 动态规划 = V(x, y) + 1 而贪心算法 = V(x, z) + V(z, y)???

      

 

        

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值