2月末小结

距离我开始学PTA已经差不多一月有余的时间了,这段时间总体的感受是:编程很快乐。这却不代表我一直是顺风顺水过来的,各方各面的问题层出不穷,学得快、忘得也快,刷到某块进阶内容,书要重新翻过来看,谈不上高效率,不过是龟速进展中。
然而,还有8天就要考试了,不可以说没有压力。
近一个月的时间,主要是对《算法笔记》里的内容进行学习,然后再刷它配套的习题集,再有就是写一些博客,记录下刷题感想,总体而言过得比较充实与轻松,但接下来一周多的时间就要开始上强度了,不然怕是对不起我的报名费。
当然,即便最后因为种种原因没有发挥好我也会坦然接受结果,我的要求只有一个:地狱周必须挺过来。我不关心结果如何,但过程绝不能有任何差池,加油吧小家伙。
由于昨晚没睡好,今早又有讲座要参加,我今天的学习状态并不好,吸取教训,今晚戴耳塞,不然这样糟蹋下去恐怕小命难保。
回顾一下今天的内容,刷了两个专题,脑子里还是有点乱:

一些通用的注意点写在前面:
1.在做图的题目时,要搞清楚两个东西,一个为点集、一个为边集,图就是由其点集和边集构成的,而不同的题目背景里这两者的给出方式又是千变万化的,需要留意;
2.图的问题中,要注意题目中的边是有向边还是无向边,假如是无向边要注意对称处理,否则很有可能出错;
3.题中给了顶点数范围或边数范围你需要依照着开宏变量的时候,要多取几个,一般多十个,防止接收数据出错。具体而言,如题目给了点的编号不超过1000,你想开点的数组,数组的最大范围最好设为1010,即表示最大顶点数的宏变量要设为1010。一般是不会内存超限哒,但如果出现数量级上的差异就另说了。
最短路径专题涉及以下几个算法:单源最短路的Dijkstra算法、全源最短路问题的Floyd算法、同样解决单源最短路问题但同时能处理负边权的BF算法,及其队列优化版本的SPFA算法。现在回顾一下它们各自的核心思想:
1.Dijkstra:算法总体而言是一个点集扩展的过程,依据的规则是每次取点集(s)外的离源点最短距离(记du)最小的点(记u),加入点集中,并对由这个点出发的边依次进行松弛操作,所谓松弛操作,就是考察当这个点(u)作为中继的时候,能否使集合外某个点(v)离源点的最短距离(dv)变小,如果可以的话,就以u为中继更新v的最短距离dv。再进入下一轮循环,如此不断扩展点集,直至所有点都在点集s中。
2.Floyd算法:该算法可以计算图中任意两点间的最短距离,算法主体是一个三重循环,其主要进行的是松弛操作,遍历所有中继点(记k),再遍历起点(记i)和终点(记j),三者范围均为图中所有点,若k为中继可以使i到j的距离变小,则对d[i][j]进行松弛,具体为:d[i][j] = d[i][k]+d[k][j]。
3.BF和SPFA算法:其处理的与Dijkstra同为单源最短路径问题,但是边权为负数时Dij是不能处理的,而这两者可以,同时能判断图中是否存在负环,假如图中存在负环,会导致最短距离无限变小,结果就不是收敛的。Dij遍历点,BF则遍历边,其思想与Floyd相似的,只不过因为源点确定而少了一个维度。SPFA是对BF算法的优化,基于这样一个事实:如果v的最短距离dv发生变化,那么一定是因为与其相连的某条边uv的另一个端点u的最短距离du发生了更新,这种明显的先后关系提示我们可以引入队列来解决,将发生更新的点入队,只有这些点相连的顶点的d值有可能被松弛。
第二个专题是最小生成树,涉及的主要算法有:Prim、Kruskal,两者适用于不同性质的问题,具体而言,Prim适用解决稠密图,即点少边多,可见它主要是处理点的算法;Kruskal则适用于解决稀疏图,即边多点少的图,它主要处理的是边。下面具体说说两个算法:
1.Prim:它与Dijkstra是相似的,两者的区别仅有d数组的含义不同,在Dij中,d指的是点到源点的最短距离,而在Prim中,d指的是点到当前的最小生成树(记为点集s)的最短距离。事实上,Dij的d是一条路径的长度,而Prim的d则是跨越两个连通集的“桥”的长度,其一定等于图中某条边的权值。算法其他部分与Dij一致,无甚可说。
2.Kruskal:这个算法是比较简单的,它首先把所有边按权值从小到大排序,把每个点各自初始化一个连通集(只有这个点自己的集合),接下来按序访问每条边,如果边的两点不属于一个集合,就将边加入最小生成树,否则舍弃,即不处理,当加入到树中的边数达到n-1时算法结束,最小生成树已生成,若结束时边数不为n-1,说明图本来就不连通。在这个算法中用到了并查集,即UFS(union,find and set)。
好了,今天的说完了,再总结一下这一个月以来的学习体系:
第一部分是一些基础的数学问题、图形输出问题、日期处理问题、排序与查找问题等,其中比较让人头大的有大整数运算问题、贪心算法、各类排序算法、二分法和两点问题,这里我要单独说说二分法和两点问题:二分法的主要应用有二分查找、计算根号值、快速幂等,每次都按照某种规则将问题规模缩小一半,达到logn级别的复杂度,两点法是一个完全不同的概念,一开始它只是被用来解决合并问题,给两个数组设两个指针,按照某种规则分别移动。但由于二分法和两点法常常结合起来解决问题,比如归并排序和快速排序等,导致我对这两个概念常常傻傻分不清。
第二部分涉及到一些特殊的存储结构:栈、队列、优先队列(后面有道题赋予了其单调队列的概念,但其实单调队列主要用于解决滑动窗口问题,目前还没有接触过),主要就是C++标准模板库的常用结构,要注意对优先队列、set存储结构体类型的时候重载操作符的处理方法,关键词:友元、重载操作符。
第三部分,也按存储结构讨论吧:
1.链表:包括单链表、双向链表、循环链表和静态链表,涉及的主要操作有链表的创建、元素的查找、元素的插入和有序插入、元素的删除和链表的合并等。感觉链表的问题很灵活,但代码写出来都差不多,印象已经不太深了,后面还要巩固。
2.DFS和BFS,虽然这俩不是啥数据结构,但是是引入后面树和图重要前置内容。他俩涉及一些很有意思的经典问题(classic puzzle),举例而言:DFS主要是递归,可解决走迷宫问题,DFS能够输出所有合法路径,DFS解决的一个经典问题是8皇后,或n皇后,我解决的时候听了北大郭炜老师的讲解,老师给的代码很简洁,我自己写了一整晚都没搞出来有的时候真的不如放弃=^=;BFS解决的问题就更丰富了,注意它用到队列,且分层扩展搜索,有时空间代价难以承受,它只会给出一组最优解,不像DFS那样可以给出所有解,BFS如何记录得到最优解的路径也是一个值得深思的问题,参考别人的博客,我自己用的是其中的结构体父节点回溯法。这篇博客写得很不错,对有趣的点进行了比较详细的说明,但问题是我记忆力真的很差,现在我已经完全回想不出来几句话了,怎会如此!BFS解决的一些经典问题有装水问题(其实跟BFS关系不大,但用到了队列,故形式上有相近之处)、令人作呕的8数码问题,多少带点个人感情了,这个本来也想看郭老师的讲解,但是这个老师讲的不够细,代码也过于复杂了,最后是自己搞出来的,也参考了一些博客,但没啥用哈哈,还有魔板问题,与8数码问题有相似之处,解决前者这就容易多了。
3.树:这也是个大专题,花了两天时间在上面,主要包括树的遍历与重建,可看我之前的博客,有写、二叉完全树的一些数字游戏、二叉搜索树的建立与遍历,与给你遍历序列重建一棵树不同,二叉搜索树需要按规则将新结点插入到合适的位置,注意区别,二叉搜索树有一个特殊的性质,就是其中序遍历序列总是单调的,所以判断两个二叉搜索树是否一样,在两者元素依然相同的情况下,只要判断其前序或后序遍历序列是否一致即可。此外,平衡二叉树的基本操作、并查集、堆和最后的赫夫曼树与赫夫曼编码,有几个要关注的点:平衡二叉树有两个基本操作,左旋和右旋,操作里面会更新树高,又有四个基本树形(LL、LR、RR、RL),树型的判断依据树和其左右子树的平衡因子,具有高度的对称性,平衡操作也具有高度对称性,多写写就记住了:P
堆是用数组实现的,堆的向下调整算法是核心算法,其建立过程又是自底向上进行的。堆排序交换首末元素并将堆的尾指针前移,这样不断操作后堆的尾巴后面就会逐渐增长出一个有序序列。堆是一种数据结构,而堆排序是利用堆实现的一种排序算法,不要搞得不清不楚的,堆的应用范围也不局限于排序。此外,若要向堆中添加元素,还要设计一个向上调整算法,并在尾部插入新元素。
最后,赫夫曼树和赫夫曼编码,主要用于解决合并果子及其衍生问题,这类问题只涉及最短带权路径的求解,用一个优先队列能够轻松实现,但注意要定义优先级设定规则,本博客是篇个人笔记就不做这些科普啦。求赫夫曼编码则完全不是一个量级的问题了,首先要建立这棵赫夫曼树,赫夫曼树用静态数组实现,但数组元素是单独的结构体,至少要包括权值、双亲结点编号和左右孩子编号这几个成员,然后基于这棵树生成赫夫曼编码,有自底向上和自顶向下两种思路,两种方法区别还是很大的,这里不展开,仅提几个点:自顶向下地方法中利用标记进行跳转、回溯,本质上是一个DFS算法,总体设计的很巧妙,这种方法也很有趣,但目前还不能自如运用。
写了一个半小时了,好像没变清楚多少,但没关系,又不会整理这一次,总会慢慢变清晰的,今天就到这,溜啦溜啦~

  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值