Ⅰ 回溯法的基本思想是什么
回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。 补充:
回溯法在问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树。算法搜索至解空间树的任意一点时,先判断该结点是否包含问题的解。如果肯定不包含,则跳过对该结点为根的子树的搜索,逐层向其祖先结点回溯;否则,进入该子树,继续按深度优先策略搜索。
Ⅱ 程序员都应该精通的六种算法,你会了吗
对于一名优秀的程序员来说,面对一个项目的需求的时候,一定会在脑海里浮现出最适合解决这个问题的方法是什么,选对了算法,就会起到事半功倍的效果,反之,则可能会使程序运行效率低下,还容易出bug。因此,熟悉掌握常用的算法,是对于一个优秀程序员最基本的要求。
那么,常用的算法都有哪些呢?一般来讲,在我们日常工作中涉及到的算法,通常分为以下几个类型:分治、贪心、迭代、枚举、回溯、动态规划。下面我们来一一介绍这几种算法。
一、分治算法
分治算法,顾名思义,是将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
分治算法一般分为三个部分:分解问题、解决问题、合并解。
分治算法适用于那些问题的规模缩小到一定程度就可以解决、并且各子问题之间相互独立,求出来的解可以合并为该问题的解的情况。
典型例子比如求解一个无序数组中的最大值,即可以采用分治算法,示例如下:
def pidAndConquer(arr,leftIndex,rightIndex):
if(rightIndex==leftIndex+1 || rightIndex==leftIndex){
return Math.max(arr[leftIndex],arr[rightIndex]);
}
int mid=(leftIndex+rightIndex)/2;
int leftMax=pidAndConquer(arr,leftIndex,mid);
int rightMax=pidAndConquer(arr,mid,rightIndex);
return Math.max(leftMax,rightMax);
二、贪心算法
贪心算法是指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。
贪心算法的基本思路是把问题分成若干个子问题,然后对每个子问题求解,得到子问题的局部最优解,最后再把子问题的最优解合并成原问题的一个解。这里要注意一点就是贪心算法得到的不一定是全局最优解。这一缺陷导致了贪心算法的适用范围较少,更大的用途在于平衡算法效率和最终结果应用,类似于:反正就走这么多步,肯定给你一个值,至于是不是最优的,那我就管不了了。就好像去菜市场买几样菜,可以经过反复比价之后再买,或者是看到有卖的不管三七二十一先买了,总之最终结果是菜能买回来,但搞不好多花了几块钱。
典型例子比如部分背包问题:有n个物体,第i个物体的重量为Wi,价值为Vi,在总重量不超过C的情况下让总价值尽量高。每一个物体可以只取走一部分,价值和重量按比例计算。
贪心策略就是,每次都先拿性价比高的,判断不超过C。
三、迭代算法
迭代法也称辗转法,是一种不断用变量的旧值递推新值的过程。迭代算法是用计算机解决问题的一种基本方法,它利用计算机运算速度快、适合做重复性操作的特点,让计算机对一组指令(或一定步骤)进行重复执行,在每次执行这组指令(或这些步骤)时,都从变量的原值推出它的一个新值。最终得到问题的结果。
迭代算法适用于那些每步输入参数变量一定,前值可以作为下一步输入参数的问题。
典型例子比如说,用迭代算法计算斐波那契数列。
四、枚举算法
枚举算法是我们在日常中使用到的最多的一个算法,它的核心思想就是:枚举所有的可能。枚举法的本质就是从所有候选答案中去搜索正确地解。
枚举算法适用于候选答案数量一定的情况。
典型例子包括鸡钱问题,有公鸡5,母鸡3,三小鸡1,求m钱n鸡的所有可能解。可以采用一个三重循环将所有情况枚举出来。代码如下:
五、回溯算法
回溯算法是一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。
许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。
典型例子是8皇后算法。在8 8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问一共有多少种摆法。
回溯法是求解皇后问题最经典的方法。算法的思想在于如果一个皇后选定了位置,那么下一个皇后的位置便被限制住了,下一个皇后需要一直找直到找到安全位置,如果没有找到,那么便要回溯到上一个皇后,那么上一个皇后的位置就要改变,这样一直递归直到所有的情况都被举出。
六、动态规划算法
动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。
动态规划算法适用于当某阶段状态给定以后,在这阶段以后的过程的发展不受这段以前各段状态的影响,即无后效性的问题。
典型例子比如说背包问题,给定背包容量及物品重量和价值,要求背包装的物品价值最大。
Ⅲ 数据结构中KMP算法为什么避免了不必要的回溯
因为它对匹配串也就是子串做了一个数组记录每个字符的值,每次匹配不成功时就查看对应的值,然后移动母串指针,解决问题的关键就是对匹配串做了标记值。
Ⅳ 常见算法思想6:回溯法
回溯法也叫试探法,试探的处事方式比较委婉,它先暂时放弃关于问题规模大小的限制,并将问题的候选解按某种顺序逐一进行枚举和检验。当发现当前候选解不可能是正确的解时,就选择下一个候选解。如果当前候选解除了不满足问题规模要求外能够满足所有其他要求时,则继续扩大当前候选解的规模,并继续试探。如果当前候选解满足包括问题规模在内的所有要求时,该候选解就是问题的一个解。在试探算法中,放弃当前候选解,并继续寻找下一个候选解的过程称为回溯。扩大当前候选解的规模,以继续试探的过程称为向前试探。
(1)针对所给问题,定义问题的解空间。
(2)确定易于搜索的解空间结构。
(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。
回溯法为了求得问题的正确解,会先委婉地试探某一种可能的情况。在进行试探的过程中,一旦发现原来选择的假设情况是不正确的,马上会自觉地退回一步重新选择,然后继续向前试探,如此这般反复进行,直至得到解或证明无解时才死心。
下面是回溯的3个要素。
(1)解空间:表示要解决问题的范围,不知道范围的搜索是不可能找到结果的。
(2)约束条件:包括隐性的和显性的,题目中的要求以及题目描述隐含的约束条件,是搜索有解的保证。
(3)状态树:是构造深搜过程的依据,整个搜索以此树展开。
下面是影响算法效率的因素:
回溯法搜索解空间时,通常采用两种策略避免无效搜索,提高回溯的搜索效率:
为缩小规模,我们用显示的国际象棋8*8的八皇后来分析。按照国际象棋的规则,皇后的攻击方式是横,竖和斜向。
皇后可以攻击到同一列所有其它棋子,因此可推导出每1列只能存在1个皇后,即每个皇后分别占据一列。棋盘一共8列,刚好放置8个皇后。
为了摆放出满足条件的8个皇后的布局,可以按如下方式逐步操作:
把规模放大到N行N列也一样,下面用回溯法解决N皇后问题:
执行:
Ⅳ 五大基本算法——回溯法
回溯法是一种选优搜索法(试探法)。
基本思想:将问题P的状态空间E表示成一棵高为n的带全有序树T,把求解问题简化为搜索树T。搜索过程采用 深度优先搜索 。搜索到某一结点时判断该结点是否包含原问题的解,如果包含则继续往下搜索,如果不包含则向祖先回溯。
通俗来说,就是利用一个树结构来表示解空间,然后从树的根开始深度优先遍历该树,到不满足要求的叶子结点时向上回溯继续遍历。
几个结点:
扩展结点:一个正在产生子结点的结点称为扩展结点
活结点:一个自身已生成但未全部生成子结点的结点
死结点:一个所有子结点已全部生成的结点
1、分析问题,定义问题解空间。
2、根据解空间,确定解空间结构,得 搜索树 。
3、从根节点开始深度优先搜索解空间(利用 剪枝 避免无效搜索)。
4、递归搜索,直到找到所要求的的解。
1、子集树
当问题是:从n个元素的集合S中找出满足某种性质的子集时,用子集树。
子集树必然是一个二叉树。常见问题:0/1背包问题、装载问题。
遍历子集树时间复杂度:O(2^n)
2、排列树
当问题是:确定n个元素满足某种排列时,用排列数。常见问题:TSP旅行商问题,N皇后问题。
遍历排列树时间复杂度:O(n!)
通俗地讲,结合Java集合的概念,选择哪种树其实就是看最后所得结果是放入一个List(有序)里,还是放入一个Set(无序)里。
剪枝函数能极大提高搜索效率,遍历解空间树时,对于不满足条件的分支进行剪枝,因为这些分支一定不会在最后所求解中。
常见剪枝函数:
约束函数(对解加入约束条件)、限界函数(对解进行上界或下界的限定)
满足约束函数的解才是可行解。
1、0/1背包问题
2、TSP旅行商问题
3、最优装载问题
4、N-皇后问题
具体问题可网络详细内容。
Ⅵ 六、递归与回溯算法
在计算机领域里面,很多问题都可以要采用递归算法来解决。递归中,最长用到的方法就是回溯法。我们具体分析问题的时候,可以发现这类问题本质是一个树的形状。
递归算法的本质还是将原来的问题转化为了更小的同一问题,进行解决。一般注意两点:
1、递归终止的条件。对应到了递归算法中最基本的问题,也是最最简单的问题。
2、递归过程。递归过程需要将原问题一步一步的推到更小的 同一 问题,更小的意思就是子问题解决起来就更加的简单。有写情况是能够找到一个递推的公式的。这个过程中就需要透彻的去理解递归函数的意义。明确这个函数的输入和输出是什么,这样来写的话,就清晰多了。
因为有了这样的递归公式,那么我们就很容易的能看出来递归的终止条件就是我们知道的f(0)和f(1)了。有了f(0)和f(1)之后,我们就能够继续的递推出f(3)一直到f(n)了。
但是我们现在才用一个递归算法的思想来解决这个问题:
像我们常见的数据结构如链表、树、图等都是有着天然的递归结构的,链表由于是一个线性的结构,那么通常我们也是能够直接循环遍历就能解决问题的,但是这里我们用递归法来解决一下LeetCode上面的问题
LeetCode 203 移除链表元素
分析:链表的结构可以理解成一个节点连接这一个更短的链表,这样依次一直看下去,直到最后一个节点None,那么我们这个时候的递归终止条件就是head指向None了,返回的就是None
深入的理解递归算法之后,我们就开始进行回溯法的学习。通过LeetCode上面的几道题,我们来深入的探讨一下递归与回溯法的应用。
持续更新中...
数据结构与算法系列博客:
一、数据结构与算法概述
二、数组及LeetCode典型题目分析
三、链表(Linked list)以及LeetCode题
四、栈与队列(Stack and Queue
五、树(Trees)
六、递归与回溯算法
七、动态规划
八、排序与搜索
九、哈希表
参考资料
1、
2、
3、
Ⅶ 请问什么是回溯算法
回溯(backtracking)是一种系统地搜索问题解答的方法。为了实现回溯,首先需要为问题定义一个解空间(solution space),这个空间必须至少包含问题的一个解(可能是最优的)。
下一步是组织解空间以便它能被容易地搜索。典型的组织方法是图(迷宫问题)或树(N皇后问题)。
一旦定义了解空间的组织方法,这个空间即可按深度优先的方法从开始节点进行搜索。
回溯方法的步骤如下:
1) 定义一个解空间,它包含问题的解。
2) 用适于搜索的方式组织该空间。
3) 用深度优先法搜索该空间,利用限界函数避免移动到不可能产生解的子空间。
回溯算法的一个有趣的特性是在搜索执行的同时产生解空间。在搜索期间的任何时刻,仅保留从开始节点到当前节点的路径。因此,回溯算法的空间需求为O(从开始节点起最长路径的长度)。这个特性非常重要,因为解空间的大小通常是最长路径长度的指数或阶乘。所以如果要存储全部解空间的话,再多的空间也不够用。