【算法基础总结】算法基础 (算法竞赛,面试...)

算法基础

​ (By: Skyed_blue 转载注明作者)

前言

本篇为算法基础整合,主要偏向于对基础算法的总结和整理。本篇将会介绍个人刷算法题的一些经验,基础算法的简要描述,基础算法整合的相关题目。因为整理的题目和题解时间都不一样,因此会有代码风格等差别。另外,默认大家都具备数据结构基础和STL函数库的使用,本篇不再讨论这些,虽然这些很重要(建议还没学会STL的可以先学STL,会有质的飞跃)。本篇相关题目在vjudge,leetcode,PTA,蓝桥杯等平台收录。

做算法题的思路:

  1. 先分析时间复杂度pick掉一部分算法,考虑这个时间复杂度我可以运用到哪些算法。比如说如果复杂度需要在nlogn,可以通过log想到二分,分治,堆,set等等。
  2. 然后以所求的结果为导向(要求出结果需要做哪些操作),可以通过样例或者自己举例子。比如说我要求C,时间复杂度在xxx,我觉得可以采用A步骤+B步骤。实现A和B都需要控制时间复杂度在xxx,如果我能在这个复杂度里求出A和B,那么这道题你完成一大半了。如果你想的A+B步骤时间复杂度超了,说明两点:这道题触及到你的知识盲区;这道题最佳步骤不是A+B。这时候你可以考虑想别的步骤。如果实在想不到还有什么其他的方法,果断看题解,不要浪费时间了。因为凭借刚才的思路你已经在你的知识和经验储备里尽可能发挥了,再往下想相当于“自己造算法”。如果是比赛,可以这样,也只能这样;如果是平时做题,不要这样。你去看题解,如果这个算法你没学过,那理所当然你做不出;如果这个算法你学过,那必然是你没想到原来可以这样用。前者是你的知识盲区,后者是你的经验不足。这时候,你就会补充你的知识和丰富你的经验。这道题对你的价值就在这里。
  3. 当你写完这道题并且过了样例之后,别急着提交。你需要冷静的思考:这道题有哪些坑点。你可以将达到这个结果中出现的各种过程造出样例,测试你的代码,你同时也要注意边界条件。比如走迷宫问题,我把地图扩到最大,无任何障碍,分析从左上走到右下的时间复杂度是否会超。总之就是要取一些极端的数据,比如0. 刷leetcode时一个很好的例子就是:int m = arr[0].size(). 这个m想求所给矩阵的列数。但是如果题目本身有arr.size() = 0,你这样做就是超界,直接RE了。这时就需要if(!arr.size()) return… 总的来说就考虑两点:各种过程到达结果是否都正确,适当造一些特例;边界条件是否注意到,数据范围是否会超。其实这两点可以用对拍试出来(不一定能试出来),对拍可以先不学,因为对拍生成数据还是有点麻烦的。
  4. 经过上面3个步骤,提交一发,依旧不一定能过。因为有一些情况是你自己没考虑到的。而OJ平台不会告诉你哪个样例没过。这时候你可能又会花很长的时间debug,甚至可能一个下午或者一个晚上只能写这一道题而且还不一定写的出来。等最后筋疲力竭,再去搜题解,又得分析别人的代码,然后在自己代码上又改改,最后提交。这整个过程很耗费时间,而且最后你可能会发现因为这道题特有的一种情况自己没考虑到导致自己一天花了大量精力的投入,愣是找不到bug. 这一天你可能几乎什么都没学到。很显然这个性价比很低。但是大家都是这样过来的。这种情况时常发生,看你怎么权衡了。

另外,想的时候可能会比较挣扎,但是不要用力扯头发。

如何学一个新的算法:

首先,学一个算法,理解原理,做例题。

然后,做这个算法相关的变型题,要想明白为什么这道题可以想到用这个算法,它的思路链(树)是怎样的,或者有"这个算法可以这样用,妙哉!"的想法。

最后,总结。总结中要把自己此时理解的思路写下来,确保以后忘记了翻回来看能快速捡起来这个算法。

学算法不一定每个算法都要达到可以自己写出来的程度。有些算法你可以先学到三四成,理解这个算法的原理和应用,以及它的时间复杂度和空间复杂度就可以了,因为ACM是可以带模板的。这样以广度优先学算法才是效率。之后你做某一题,发现这道题和我之前看到的xxx算法有点像,你这时候再去把这个算法模板拿来改改。这样你也成功做出来这道题,而且对这个算法有了更多的印象和经验。这样学才比较有效。

当然,对于基础算法,必须熟练。蓝桥杯,PTA都不能带模板,并且考的也都是一些基础算法。

关于做笔记:

做笔记我认为是非常有必要的。你好不容易学会了一个算法,结果过一个月回头看,发现跟没学一样,只知道这个算法名字了。

对于笔记,我认为有两个方面:算法知识笔记和题解。

算法知识笔记要自己理解了这个算法之后以“总结”的方式写。你的目的是“以后可以快速捡起来该算法”,因此你不能写太多,抓重点写。学这个算法哪个地方你卡住了,你就重点强调这里,方便以后回头看能快速理解这部分。另外,算法总结也要“模块化”。比如学一个算法,我自己做的笔记是:算法应用,算法原理,模板中一些重点难点,容易写错的部分,算法时间空间复杂度,算法题目整合。

关于题解,首先你要明白你自己为什么要写这道题的题解。因为这道题带给你xxx经验,某个点或者思路运用巧妙,自己当时做的时候没想到。你首先得分析为什么可以想到这个思路,然后用启发式的语言一步步写下去,而不是“原来这道题用到了这个方法,标注一下就行了”。看了题解之后你要重新回到自己的思路起点,一步步分析如何想到这个点,然后把这个分析的过程写下来。你在写的过程中,会进行语言组织的表达,思路的整理,仿佛你就是在教别人如何一步步推想到这个方法。这个真的非常重要。因为你写的时候你才会发现自己语言组织能力有多差。如果以后面试问你一道题,你回答出来了,但是当面试官问你“你是怎么想到的?”时,你会支支吾吾。因为你对这个过程没怎么注意。而写题解就是在帮助你理清思绪,整个从起点开始到想到这个方法的逻辑链你都很清晰,你自然就可以很顺畅的表达出来。就像我现在给你们写的这篇前言一样,逻辑清晰,令人信服。我以前写作文都没像现在写的如此丝滑,跟默写代码模板一样。这种清晰的逻辑链也会让你以后在思考一道题目的时候不会发散其他乱七八糟想法中断自己的逻辑,因为你想的每一步都是清晰的。

对于做笔记,个人不推荐写在纸上(这不是废话嘛,代码这么多)

那么,在电脑上如何做笔记呢?

我个人推荐一款软件“Typora”。良心软件,made in China,markdown轻量级文本文件,绝对比在word方便很多!你也可以将笔记写在博客里,分享自己的想(lao)法(sao),写完之后起码会让自己觉得“努力没白费”。因为写笔记和写博客是将自己的成果量化成肉眼可见的东西,算是心里安慰(自慰)吧。

ok,前言就先写到这。下面我会陆续总结基础算法的“简要知识点”和应用,也就是偏向于复习。我的博客:https://blog.csdn.net/Skyed_blue


dfs(深度优先遍历)

dfs,基础暴力搜索算法,关键在于找到每一层所必需的状态,以及能够枚举所有情况的代码。

**基础应用:**求N阶乘,斐波第N项,N个数全排列,枚举子集,组合穷举,遍历树,遍历图,记忆化搜索(备忘录dp),回溯(八皇后),走迷宫(输出路径)…

**高级应用:**图论里的各种Tarjan,欧拉回路(图论进阶可以先不看),floodfill着色法,容斥定理(了解概念,可以先不深究),环检测(dfs和拓扑排序),LCA(最近公共祖先,可以先不看)…

dfs技巧:

状态标记:vis数组,标记某状态已被遍历过,避免重复遍历形成死循环等。

方向数组:dx[4] = {0,0,1,-1},dy[4] = {1,-1,0,0} 可以直接一个for循环走四个方向

剪枝:通过当前状态避免一些不必要的遍历过程,相当于剪掉搜索树的某些枝条。一般分为可行性剪枝(如一个迷宫,问能否从起点到达目标点之类),最优性剪枝(最小步数,求最值,这个很好剪),迭代加深(A*,没怎么了解)适合深度不是很深,但是每次扩展的结点数很多的搜索问题。

【蓝桥杯】2019国赛B组 101串(dfs剪枝)

子集(枚举子集,可状态压缩)

棋盘问题(dfs)

迷宫问题(dfs,bfs)

【leetcode】串联字符串的最大长度(dfs回溯,可状态压缩)

统计封闭岛屿数目(floodfill)

HDU-1796 How many integers can you find (容斥定理+dfs)

【蓝桥杯】发现环(dfs搜环节点)

bfs(广度优先遍历)

bfs,基础暴力搜索算法,用队列(open-close表)实现。每个节点表示一个状态,若一个节点有多个属性,用pair或结构体改造节点。

基础应用:树的层次遍历,图的最短路(无权就是队列,有权就是小顶堆),最短路(最小值),拓扑排序,Floodfill…

高级应用:双向广搜,A*(启发式搜索,估价函数优化队列),差分约束(最短路本质,线性规划),AC自动机(字典树+KMP+BFS)…

bfs技巧:

状态标记,方向数组,和dfs技巧类似。

一般看到最短路问题,请直接选择bfs. 写dfs就是在浪费时间。

迷宫问题(输出路径)

【PTA】喊山(图bfs)

【PTA】7-36 社交网络图中结点的“重要性”计算 (bfs)

跳跃游戏III (基础dfs,bfs)

地图分析 (floodfill)

颜色交替的最短路径 (dfs,bfs, 好题目值得做)

穿过迷宫的最少移动次数(bfs,比较繁琐)

转化为全零矩阵的最少反转次数(dfs,bfs, 有各种优化,好题)

网格中的最短路径(bfs)

八数码(广搜,双广,A*,状态压缩,经典题目,绝对值得做)

二分查找(二分枚举)

二分查找是很常用的优化算法,时间复杂度在logn,前提条件是有序。二分一般应用:二分查找,二分查找下界(lower_bound),二分查找上界(upper_bound),二分枚举。我的这篇博客https://blog.csdn.net/Skyed_blue/article/details/103260295综合了很多题解加上自己的实践总结出,推荐看(0.0)。相关题目也在里面了。

并查集

并查集是一种树型的数据结构,用于处理一些不相交集合的合并(将两个元素所在集合合并成一个集合)及查询(两个元素是否在同一个集合)问题。还有一种理解是将所有元素看成图的一个节点,并把每个节点视为一个集合。此时假设有n个节点就相当于有n个集合,n个连通块。并查集就是查询(两个节点是否在同一连通块)及合并(将两个节点所在的连通块合并成一个连通块)。因此在分析一道题是否需要用到并查集时主要考虑这道题是否和集合或者连通块有关系。

并查集技巧:

路径压缩:return pre[x] = Find(pre[x]); 每查询完一遍&#x

  • 8
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值