一. 实际问题的抽象与建模
如果我们需要研究一个实际问题,首先第一步就是对这个实际问题进行抽象,抽象是从众多的事物中抽取出共同的、本质性的特征,而舍弃其非本质的特征的过程。具体地说,抽象就是人们在实践的基础上,对于丰富的感性材料通过去粗取精、去伪存真、由此及彼、由表及里的加工制作,形成概念、判断、推理等思维形式,以反映事物的本质和规律的方法。抽象得到结果我们再对其进行数学建模,数学建模,就是根据实际问题来建立数学模型,对数学模型来进行求解,然后根据结果去解决实际问题。当需要从定量的角度分析和研究一个实际问题时,人们就要在深入调查研究、了解对象信息、作出简化假设、分析内在规律等工作的基础上,用数学的符号和语言作表述来建立数学模型。像之前我们解决的图的m色着色问题,第一步就是将地图板块抽象成一个个节点,地图版块与地图板块之间的连接抽象成节点的连接,最后我们得到了一个连通图,这就是抽象的意义:化繁为简。
二. 栈和队列在关于图的算法中的重要作用
栈是一种“后进先出”(Last In,First Out)的数据结构,也就是说最后进入栈中的元素最先弹出。在栈中只允许在一端进行插入和删除操作,这一端被称为栈顶。插入元素的操作被称为入栈,删除元素的操作被称为出栈。栈常用于实现递归算法、深度优先搜索(DFS)算法、表达式求值、函数调用栈等。
而队列则是一种“先进先出”(First In,First Out)的数据结构,也就是说最先进入队列的元素最先删除。队列有两个端点,分别为队头和队尾。数据插入在队尾,删除在队头,这就保证了队列的元素按照先进先出的顺序进行处理。队列常用于实现广度优先搜索(BFS)算法、仿真模拟等。
之所以会定义栈和队列这样的数据结构是因为他们有两大特性:
第一: 他们可以保存程序运行路径中各个点的信息,以便用于回溯操作或其他需要访问已经访问过的节点信息的操作。比如: 栈用于解决迷宫问题,就是用到了若线路不通,需要回溯到已访问过的结点,从那个结点再做一次与这次路径不同的选择。
第二: 先进后出和先进先出的次序。先进后出次序其实就是一种将序列反序操作的次序,先进先出次序 其实就是一种将序列顺序操作的次序。比如:利用栈的先进后出可以解决进制转化问题 ,即:先将个位余数进栈,再将十位余数进栈,然后百位,千位等 ,这样出栈的时候顺序就成了反序出栈,即:先千位,百位,然后十位,最后个位。
三. DFS深度遍历算法
图里面常用的算法之一,之所以要作为总结之一,是因为它的思想思维过程——回溯。首先一开始我们在图里面寻找条件满足的节点,一直向下寻找,直到最后一个节点没有再满足条件的节点,这个时候我们开始回溯。回到上一个节点是否还有满足条件的节点,若有则进行寻找;若没有则继续回溯直到所有的节点都遍历完毕。DFS使用的数据结构是栈,回溯是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
四. BFS广度遍历算法
图的广度优先遍历算法和树的层序遍历相类似。其思想是:从第一个结点开始遍历,访问第一个结点后,依次访问该结点的相邻结点,再把从这些访问过得结点中依次访问它们的相邻结点,直至访问完所有的结点。BFS的算法过程就是记录每一个访问过的节点的值,不需要回溯,所以在这里我们用到了另外一种数据结构——队列。
五. 邻接表与十字链表——图的另外两种记录方式
邻接矩阵是最好理解的一个记录图节点信息的数据结构,当然我们不可能否认其他存储图的数据结构这里我们有邻接表和十字链表。邻接表,存储方法跟树的孩子链表示法相类似,是一种顺序分配和链式分配相结合的存储结构。如这个表头结点所对应的顶点存在相邻顶点,则把相邻顶点依次存放于表头结点所指向的单向链表中。可以看出邻接表只用存储每个节点的出边即可,若我们设定一个图的<V,E>,则邻接矩阵的时间复杂度,邻接表的时间复杂度,当稀疏矩阵的时候,我们用邻接表的复杂度最低(最节省电脑空间)。二十字链表是邻接表的扩展,我们之前说邻接表只记录的节点的出边,那么十字链表就是既记录出边又记录入边。
六. 贪心算法
贪心算法(greedy algorithm,又称贪婪算法)是指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择。例如后面我们要总结的Dijkstra从节点i到节点j若每一步都是直接贪心计算的话,计算得到的结果可能并不是节点i到节点j的最小值。
七. Prim与Dijkstra的理解
贪心算法的直接应用,Prim算法首先寻找距离最近的节点(贪心),接着连成一个系统,找哪一个节点到这个系统的值最小。Dijkstra和Prim有异曲同工之妙,是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。主要特点是寻找距离最近的节点(贪心),连成一个系统,更新其余节点离源点的距离,接着寻找哪一个节点距离源点最近。两者的区别在于一个是寻找和系统最近的节点,后者是寻找距离源点最近的节点。
八. 通过构建辅助空间存储关键信息
深度优先遍历算法,广度优先遍历算法,Prim算法,Dijkstra算法,求关键路径这里面都构造了辅助空间类似于队列,栈这两个数据结构;还有其他存储节点信息的空间——像统计关键路径的出度、入度的矩阵;Prim算法,Dijkstra算法的访问矩阵,距离矩阵这些信息在编写计算机程序的时候提供了关键作用。
九. 多阶段操作中的信息更新
这里是因为关键路径的启发,计算每个节点的出度和入度这里不再赘述,后面我们根据入度为0的节点,再根据源点与节点之间的时间计算节点的最早时间。简而言之每当我们计算得到结果时,都可以更新信息来作为下一次计算的基础。
十. 暴力算法的剪枝操作
我们都知道搜索算法一般是基于两种方法来进行的( 深度优先 DFS 和广度优先 BFS ),而这两算法都是基于二叉搜索树的进行的。学过数据结构和算法的都知道二叉搜索树存在很多的分支,很难一次性拿到想要的结果,尤其是当输入参数较大时,二叉搜索树的分支大规模增加的时候,此时,由于搜索过程需要走很多条完全于与结果不相关的路线,所以剪枝思想就出现了。剪枝一种可以提高搜索算法时间和空间效率的技巧,经过剪枝和其他优化策略优化过的算法在执行效率上远超一般未经剪枝的算法。甚至有些暴力搜索过不了时限的算法,也可以通过各种剪枝+优化大大缩短算法运行时间,成功通过时效限制。由此可见剪枝对于搜索算法的重要性。