递归算法

N皇后问题(回溯递归思想)

房东的猫 提交于 2020-01-26 22:53:23
八皇后问题,是一个古老而著名的问题,是 回溯算法 的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即 任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 由此再来发展到N皇后问题 先求其解的个数://回溯算法也就是穷举法 1.每一行放一个皇后 2.放下一行的时候,进行判断:会不会出现同列,会不会出现同一斜线。斜线的判断可以归于斜率判断。 //递归回溯代码如下:#include<iostream> using namespace std; #include<math.h> #define N 16 int n; //皇后个数 int sum = 0; //可行解个数 int x[N]; //标记皇后放置的列数 bool Agree(int k) { int i; for (i = 1; i < k; i++) if (abs(k - i) == abs(x[k] - x[i]) || x[k] == x[i]) return false; return true; } int queen(int t) { if (t > n&& n > 0) //当放置的皇后超过n时,可行解个数加1,此时n必须大于0 sum++; else for (int i = 1; i <= n; i++) { x[t]

递归示例(一):遍历二叉树

心已入冬 提交于 2020-01-26 18:46:30
最近做项目经常用到递归,刚开始很久没用,不太熟悉,现在研究了下,并写下了学习笔记及开发经验总结。 递归热身 一个算法调用自己来完成它的部分工作,在解决某些问题时,一个算法需要调用自身。如果一个算法直接调用自己或间接地调用自己,就称这个算法是递归的(Recursive)。根据调用方式的不同,它分为直接递归(Direct Recursion)和间接递归(Indirect Recursion)。 比如,在收看电视节目时,如果演播室中也有一台电视机播放的是与当前相同的节目,观众就会发现屏幕里的电视套有一层层的电视画面。这种现象类似于直接递归。 如果把两面镜子面对面摆放,便可从任意一面镜子里看到两面镜子无数个影像,这类似于间接递归。 一个递归算法必须有两个部分:初始部分(Base Case)和递归部分(Recursion Case)。初始部分只处理可以直接解决而不需要再次递归调用的简单输入。递归部分包含对算法的一次或多次递归调用,每一次的调用参数都在某种程度上比原始调用参数更接近初始情况。 函数的递归调用可以理解为:通过一系列的自身调用,达到某一终止条件后,再按照调用路线逐步返回。递归是程序设计中强有力的工具,有很多数学函数是以递归来定义的。 如大家熟悉的阶乘函数,我们可以对n!作如下定义:f(n)= 1 (n=1) n*f(n-1) (n>=2) 一个算法具有的特性之一就是有穷性

算法导论第7章习题

纵然是瞬间 提交于 2020-01-26 00:34:46
做题要猜出题目的意图。 7.1-1 略 7.1-2 r-1, add if q=r-1, then $q = \lfloor (p+r)/2 \rfloor$。这说明元素全都一样的时候,quicksort伤不起。worst-case了,要$\Theta(n^2)$了。快排弱点1. 7.1-3 pivot要比较所有的数组元素,自然是n 7.1-4 PARTITION中Line4的<= changed to >= 7.2-1 T(n)=T(n-1)+\Theta(n)=T(0)+n*\Theta(n)=\Theta(n^2) 7.2-2 同7.1-2 7.2-3 These exercises demonstrate the weakness of the original quicksort. It’s the worst case. T(n) = T(n-1)+\Theta(n)=\Theta(n^2)。快排弱点2. 7.2-4 银行喜欢时间排序,用户喜欢支票号排序。这道题又说明了在nearly sorted list中,插入排序beat了快排。快排弱点3.因为用户使用多张支票是连号的,所以在一个时间段内,顶多是这几张支票打乱了顺序。而不是一天24小时之内所有的支票全部乱序。所以这个序列是差不多有序的。如果n个用户每次使用k张支票,插入排序最多需要O(nk)时间,如果k=1

算法运行时间复杂度

試著忘記壹切 提交于 2020-01-25 18:23:57
算法的运行时间复杂度分析,一般是求输入规模作为自变量,运行时间作为因变量的函数。并不是求所有语句执行的真实代价,是考虑算法运行时间的增长率(增长的量级),只求出公式中的最高次项,忽略低次项和系数。 经常的情况是,输入规模相同,但某种输入会使算法的运行时间其他输入更长。所以算法的时间复杂度可能会有个定语修饰。 最坏情况下:某种输入下,运行时间最长的情况 平均情况下:概率分布分析,算法时间复杂度的期望 最好情况下:某种输入下,运行时间最短的情况 时间复杂度分析用到的渐进记号 O:算法运行的渐近上界 当得到算法在最坏情况下运行时间上界为O(f(n)),可以说算法在任何情况下运行时间上界是O(f(n)). 因此特性,多数时候要求找到算法的渐近上界。 Ω:算法运行的渐近下界 当得到算法在最好情况下运行时间下界为 Ω(f(n)),可以说算法在任何情况下运行时间下界是 Ω(f(n)). θ:算法运行的确界 算法运行时间T(n)=θ(f(n))当且仅当算法时间复杂度T(n)=O(f(n))且T(n)=Ω(f(n)) o记号:非渐近紧确的上界 比如:T(n)=2n,有T(n)=O(n).那么n^2是T(n)的上界,但不是紧确的上界,有T(n)=o(n^2) ω记号:非渐近紧确的下界,ω和Ω的关系如o和O的关系 几种常见的算法运行时间渐近上界,根据函数性质可知运行时间对比(输入规模足够大): O(1)

python递归详解+汉诺塔小案例

旧街凉风 提交于 2020-01-25 18:09:17
递归 什么是 递归 ? 递归式方法可以被用于解决很多的计算机科学问题,因此它是计算机科学中十分重要的一个概念。绝大多数编程语言支持函数的自调用,在这些语言中函数可以通过调用自身来进行递归。计算理论可以证明递归的作用可以完全取代循环,因此在很多函数编程语言(如Scheme)中习惯用递归来实现循环。递归的强大之处在于它允许用户用有限的语句描述无限的对象。因此,在计算机科学中,递归可以被用来描述无限步的运算,尽管描述运算的程序是有限的。下面是对Python递归函数的简单了解: # 类似与栈的先进后出模式 # 递归的两个必要条件 # 1.要有递推关系 # 2.要有临界 def digui(num): print('$'+str(num)) # 临界值 if num >0: # 这里用的是调用本身的函数(递推关系) digui(num-1) else: print('='*20) print(num) digui(3) 输出结果为: $3 $2 $1 $0 ==================== 0 1 2 3 汉诺塔 什么是 汉诺塔 ? 汉诺塔算法介绍 其实算法非常简单,当盘子的个数为n时,移动的次数应等于2^n – 1(有兴趣的可以自己证明试试看)。后来一位美国学者发现一种出人意料的简单方法,只要轮流进行两步操作就可以了。首先把三根柱子按顺序排成品字型

递归1--小游戏

不羁岁月 提交于 2020-01-25 08:51:29
递归1--小游戏 零:本题总结 1、回溯迷宫问题 2、扩充边界 一、递归基本思想 定义:函数直接或者间接调用自身 应用场景:原问题复杂,但是可以划分成许多性质相同的子问题,子问题容易求解 递归写法:1、先写出问题的递推公式 2、递归部分的边界条件就是递推公式中的边界条件 3、递归部分的主体部分就是递推公式中的主体部分 递归在内存中的实现方式:系统通过栈来实现(通过栈去讲) 递归简单实例:http://www.cnblogs.com/Renyi-Fan/p/6914840.html 二、递归实例题目 题目: 有一个w * h 个正方格子的矩形板,每个正方格子上可以有一张游戏卡片, 当然也可以没有,当下面的情况满足时,认为 两个游戏卡片之间有一条路径相连:1、路径只包含水平或者竖直的直线段;2、路径不能穿过别的游戏卡片;3、但是允许 路径临时的离开矩形板。判断是否存在一条满足题意的路径能连接给定的两个游戏卡片。 输入: 输入包括多组数据: 一个矩形板对应一组数据 第一行包括两个整数 w和h (1 <= w, h <= 75),分别表示矩形板的宽度和长度 下面的h行, 每行包括w个字符, 表示矩形板上的游戏卡片分布情况: 使用 ‘X’ 表示这个地方有一个游戏卡片,使用空格 表示这个地方没有游戏卡片 之后每行上包括4个整数: x1, y1, x2, y2 (1 <= x1, x2 <= w

Java动态规划

别说谁变了你拦得住时间么 提交于 2020-01-25 05:31:18
1. 介绍 动态规划典型的被用于优化递归算法,因为它们倾向于以指数的方式进行扩展。动态规划主要思想是将复杂问题(带有许多递归调用)分解为更小的子问题,然后将它们保存到内存中,这样我们就不必在每次使用它们时重新计算它们。 要理解动态规划的概念,我们需要熟悉一些主题: 什么是动态规划? 贪心算法 简化的背包问题 传统的背包问题 Levenshtein Distance LCS-最长的共同子序列 利用动态规划的其他问题 结论 本文所有代码均为 java 代码实现。 2. 什么是动态规划? 动态规划是一种编程原理,可以通过将非常复杂的问题划分为更小的子问题来解决。这个原则与递归很类似,但是与递归有一个关键点的不同,就是每个不同的子问题只能被解决一次。 为了理解动态规划,我们首先需要理解递归关系的问题。每个单独的复杂问题可以被划分为很小的子问题,这表示我们可以在这些问题之间构造一个递归关系。 让我们来看一个我们所熟悉的例子: 斐波拉契数列 ,斐波拉契数列的定义具有以下的递归关系: 注意:递归关系是递归地定义下一项是先前项的函数的序列的等式。 Fibonacci 序列就是一个很好的例子。 所以,如果我们想要找到斐波拉契数列序列中的第n个数,我们必须知道序列中第n个前面的两个数字。 但是,每次我们想要计算 Fibonacci 序列的不同元素时,我们在递归调用中都有一些重复调用,如下图所示

算法浅谈——递归算法与海盗分金问题

孤街醉人 提交于 2020-01-25 03:02:19
本文始发于个人公众号: TechFlow 最近看到一道很有意思的问题,分享给大家。 还是老规矩,在我们聊算法问题之前,先来看一个 故事 。 传说中,有 5个海盗 组成了一支无敌的海盗舰队,他们在最后一次的寻宝当中找寻到了 100枚价值连城 的金币。于是,很自然的,这群海盗面临分赃的问题。为了防止海盗内讧,残忍的海盗们制定了一个奇怪的规则: 他们决定按照功劳大小对五个人进行编号,由编号小的海盗先提出分配方案。 如果方案能够得到大多数人的同意,那么就按照他提出的方案进行分配 。如果不能通过,说明他已经失去了威望,海盗们会残忍地将他投入海中 喂鲨鱼 。 在一个朦胧的早上你一觉醒来,突然发现自己成了一号海盗,那么你应该如何分配才能获得最多的金币,又不会被喂鲨鱼呢? 在我们思考之前,我们先完善一下题意, 增加几个条件 。 首先,每一个海盗都非常残忍。这意味着,在不影响收益的情况下,他们会更倾向于杀人。 其次,每一个海盗都极其聪明,都能想到最佳答案。 这两个条件一出来,问题就比较明显了,这是 博弈论 题目才有的架势。 既然这是一道博弈论的问题,那么我们通过常规的思路是无法找到答案的,我们需要 另辟蹊径 才行。 那么,怎么另辟蹊径呢? 一个比较常规的做法是先不考虑原问题,先 假设一个和原问题差不多,但是规模小很多的子问题 。通过对子问题的求解来摸索原问题的解法。 举个例子,在这题当中

数据结构与算法——暴力递归

偶尔善良 提交于 2020-01-25 00:41:15
暴力递归就是尝试 1, 把问题转化为规模缩小了的同类问题的子问题 2, 有明确的不需要继续进行递归的条件(base case) 3, 有当得到了子问题的结果之后的决策过程 4, 不记录每一个子问题的解 一定要学会怎么去尝试,因为这是动态规划的基础,这一内容我们将在提升班讲述 汉诺塔问题 打印n层汉诺塔从最左边移动到最右边的全部过程 public static void hanoi(int n) { if (n > 0) { func(n, "左", "右", "中"); } } // 1~i 圆盘 目标是from -> to, other是另外一个 public static void func(int N, String from, String to, String other) { if (N == 1) { // base System.out.println("Move 1 from " + from + " to " + to); } else { func(N - 1, from, other, to); System.out.println("Move " + N + " from " + from + " to " + to); func(N - 1, other, to, from); } } 打印一个字符串的全部子序列,包括空字符串 public static

单向链表_找出倒数第K个节点

只谈情不闲聊 提交于 2020-01-24 22:15:07
实现一个算法,找出单向链表中倒数第K个结点。 解法: 实现这个算法,可以用递归和非递归。因为单向链表只能向后遍历,所以一般会想到用递归这种方法,然后当递归遍历到链表末端时,该方法会回传一个置为0的计数器。 之后的每一次调用都会将这个计数器加1,当计数器等于K时,表示我们访问的是链表倒数第K个元素。 方法A public static int nToLast(LinkedListNode head,int k){ if(head==null){ return 0; } int i = nToLast(head.next,k)+1;//当遍历到最后一个元素时,计数器i开始自增。</span> if(i==k){ System.out.println(head.data); } return i; } <span style="color:#33cc00;">//但是这个方法只能打印倒数第K个值,不能返回该元素,因为我们无法用一般的返回语句回传一个结点和一个计数器。</span> 方法B: 可以通过引用传值,这样一来,我们就可以返回结点值,而且也能通过传递指针更新计数器。 node* nToLast(node* head,int k,int& i){ if(head==null){ return null; } node* nd = nToLast(head.next,k,i); i=i