递归算法

一招教你巧用递归解决矩阵最大序列和问题

血红的双手。 提交于 2020-01-13 23:46:28
之前同事问了一道需要点脑洞的算法题,我觉得蛮有意思的,思路可能会给大家带来一些启发,在此记录一下 题目 现有一个元素仅为 0,1 的 n 阶矩阵,求连续相邻(水平或垂直,不能有环)值为 1 的元素组成的序列和的最大值。假设有如下矩阵 则此矩阵连续相邻值为 1 的元素组成的序列和分别为 4, 3,(如图示),可知这个矩阵符合条件的序列和的最大值为 4 解题思路 要算序列和的最大值,我们可以先找出所有可能的序列,自然就找到了序列和的最大值,那怎么找这些序列呢?首先我们发现,每个序列的起点和终点必然是 1,我们可以遍历矩阵的每一个元素,如果元素值为 1,则将其作为序列的起点开始查找所有以这个元素为起点的序列,我们知道序列是可以向垂直和水平方向延伸的,所以我们可以以这个元素为起点,查找它的上下左右值为 1 的元素,再以找到的这些元素为起点,继续在元素的上下左右查找值为 1 的元素,以此类推(递归),如果找不到符合条件的值,则序列终止,在遍历过程中保存每条序列遍历的元素,即可求得每条符合条件的序列,从而求得序列和的最大值 文字说得有点绕,接下来我们就以查找如下矩阵的最大序列和为例来详细看一下如何查找最大序列和 1. 从左到右,从上到下遍历所有值为 1 的元素,第一个符合条件的元素在右上角,所以以这个元素为起点来查找序列 2. 以这个元素为起点,查找这个元素上下左右为值为 1 的元素

替罪羊树(重量平衡树)入门

泪湿孤枕 提交于 2020-01-13 07:30:20
扯 学校清明竟然给放两天假期,心血来潮突然想去学习平衡树。 可是我太弱了学不会有旋转操作的treap和splay,这可怎么办啊qaq。 诶?我以前好像看过一种叫做替罪羊树的平衡树可以不用旋转操作,那还是学这个吧…… 替罪羊树 用处 替罪羊树是一种平衡树,支持插入,删除,查找第k小元素,查找元素的排名等操作 什么数据结构优雅? 暴力即是优雅! 替罪羊树就是一种暴力平衡树,旋转?不存在的! 替罪羊树保持平衡的方法就是暴力重构,即当树不平衡时拍扁重新建树,那么如何才能知道一棵树是否平衡呢? 在替罪羊树中选用了一种叫做平衡因子的东西,听起来很高端,其实就是一个0.5~1之间的任意浮点数,保持平衡的方法是这样的: 如果一棵树的左子树/右子树的 存在的 节点数量大于这棵树的 存在的 节点数量*旋转因子,那么就要重构这棵树 为什么我特意标出了是存在的节点数呢?是因为替罪羊树的删除不是真正的删除,而是惰性删除。 所以我们就可以写出代表替罪羊树的每个节点的结构体 1 const double alpha = 0.75; //旋转因子 2 struct Node { 3 Node* ch[2]; //左右子节点 4 int key,siz,cover; //key是值,siz是以该节点为根的树的存在的节点数,cover是所有节点数量 5 bool exist; //exist标志该节点是否被删除 6

Algorithms_算法思想_递归篇

*爱你&永不变心* 提交于 2020-01-12 21:55:33
文章目录 引导案例 递归的定义 什么样的问题可以用递归算法来解决 递归如何实现以及包含的算法思 斐波那契数列代码实现 递归的时间复杂度(`2^n`)和空间复杂度(`2^n`) 引导案例 案例一: 分销系统的返利: 比如B是A的下线,C是B的下线,那么在分钱返利的时候A可以分B,C的钱,这时候我们是不是就要分别找B,C的最后上级。这个问题我们一般怎么来解决呢? C–>B–>A 案例二: .斐波那契数列: 1 1 2 3 5 8 13 21 . . . . . . 有什么特点? 从第三个数开始 就等于前面两个数相加; 数论思想:利用数学公式或者定理或者规律求解问题; 算法思想中最难的点:递归+动态规划 树论中(比如二叉树,红黑树)和递归密不可分,所以递归一定要弄明白了。 递归的定义 递归算法是一种直接或者间接调用自身函数或者方法的算法。 通俗来说,递归算法的实质是把问题分解成规模缩小的同类问题的子问题,然后递归调用方法来表示问题的解。 举个生活中的例子 比如我们在某窗口排队人太多了,我不知道我排在第几个,那么我就问我前面的人排第几个, 因为知道他排第几我就知道我是第几了。但前面的人也不知道自己排第几那怎么办呢?他也可以继续往前面问, 直到问到第一个人,然后从第一个人一直传到我这里 我就很清楚的知道我是第几了 。 以上这个场景就是一个典型的递归

函数嵌套和递归调用

泪湿孤枕 提交于 2020-01-12 18:16:46
C语言函数调用形式有两种:嵌套调用、递归调用。 C语言函数嵌套调用: 在调用一个函数的过程中,又调用另一个函数,称为函数嵌 套调用,C语言允许函数多层嵌套调用,只要在函数调用前 有函数声明即可。 1 #include <stdio.h> 2 int fa(int a,int b); //fa函数原型 3 int fb(int x); //fb函数原型 4 int main(){ 5 int a=5,b=10,c; 6 c=fa(a,b); 7 printf("%d\n",c); 8 c=fb(a+b); 9 printf("%d\n",c); 10 return 0; 11 } 12 13 int fa(int a,int b){ 14 int z; 15 z=fb(a*b); 16 return z; 17 } 18 19 int fb(int x){ 20 int a=15,b=20,c; 21 c=a+b+x; 22 return c; 23 } C语言函数递归调用: 函数直接或间接调用自己称为递归调用。C语言允许函数递 归调用,如图(a) 所示为直接递归调用,如图(b)所示为间接递归调用。 注意事项: 1.在函数递归调用时,递归函数每次调用其本身, 一个新的函数栈就会被使用,这个新函数栈里的形参、 变量和该函数的另-个函数栈里面的形参、变量是完全不同 的内存单元。 2

递归同步AD账号

Deadly 提交于 2020-01-12 09:19:54
思路:因为AD本身就是一棵树,而且.Net Framework中提供了对AD的操作对象(DirectoryEntry).该对象包含children和parent属性.所以利用这些属性使用递归算法可以批量生成Insert语句.获得这些SQL语句后,你就可以按照你想要的方式来执行了. 假设你的部门表有三个字段:DeptID,DeptName,ParentDeptID.根据一般情况,DeptID分类两种类型:整型和GUID类型.DeptName为字符型,ParentDeptID跟DeptID一致. 先说DeptID为GUID类型: 这种情况比较简单,因为window os 中的AD存储时,默认使用GUID作为唯一标识.而且DirectoryEntry对象正好包含GUID属性.这样我们就可以方便的遍历出AD中的所有项,而且因为使用递归算法,遍历完生成的SQL已经包含了层级关系.看代码: static StringBuilder sbDepts = new StringBuilder (); public static string GetBatchSQLFromSRC(DirectoryEntry entry) { DirectoryEntries entries = entry.Children; foreach (DirectoryEntry item in entries){

【算法导论】第4章 分而治之 (1)

蹲街弑〆低调 提交于 2020-01-11 22:49:19
Introduction to Algorithms - Third Edition Part I. Foundations Chapter 4. Divide-and-Conquer 当子问题足够大以进行递归求解时,称其为 递归情况 (recursive case)。一旦子问题变得很小,以至不再递归的程度,就说递归“结束(bottom out)”了,已经回到了 基本情况 (base case)。 递归式 递归式与分治法范式提供了一种自然的方式来表征分治算法的运行时间。 递归式 (recurrence)是一组等式或不等式,其描述的函数是用在更小的输入下该函数的值来定义的。 本章介绍三种解递归式的方法,即找出解的渐近 Θ \Theta Θ 或 O O O 界的方法。 代换法 (substitution method):先猜测一个界,再使用数学归纳法证明猜测的正确性。 递归树方法 (recursion-tree method):将递归式转化为树,树中的结点代表不同递归层次付出的代价。使用边界求和的技巧来解递归。 主方法 (master method):给出递归形式 T ( n ) = a T ( n / b ) + f ( n ) T(n) = aT(n/b) + f(n) T ( n ) = a T ( n / b ) + f ( n )       (4.2) 的界,其中 a ≥

初识递归--基本概念和方法

风格不统一 提交于 2020-01-11 08:01:15
递归(Recursion) 定义:方法自己调用自己,每一次调用传入不同的变量。递归有助于编程者解决复杂的问题,同时让代码变得更加简洁 递归调用规则 当程序执行到一个方法时,就会开辟一个独立的栈空间。 每个空间的数据(局部变量),是相互独立的,不会相互影响,打印问题。独立的局部空间和变量 如果方法中使用的是引用类型的变量,就会共享该引用类型的数据,共用的空间。 递归必须包含推出递归的条件,避免死循环,出现StackOverflowError,栈溢出 当一个方法执行完毕后,或者遇到一个return,就会返回,遵循谁调用就将结果返回给谁。 递归能够解决的问题 数学问题:死亡八皇后问题,汉诺塔问题,阶乘问题,迷宫问题,球和篮子的拜访问题 各种算法,快排,二分查找,分治算法 将用栈解决的问题,用递归解决更加简洁 运用递归必需的两个条件 递归终止条件 递归方法体 变量的分类–局部变量和公共变量 基本代码: public class Recursion1 { static int m = 4 ; public static void main ( String [ ] args ) { System . out . println ( add ( 4 ) ) ; } public static int add ( int n ) { if ( n > 1 ) { System . out .

五大常见算法策略之——递归与分治策略

岁酱吖の 提交于 2020-01-08 11:50:46
递归与分治策略 递归与分治策略是五大常见算法策略之一,分治策略的思想就是 分而治之 ,即先将一个规模较大的大问题分解成若干个规模较小的小问题,再对这些小问题进行解决,得到的解,在将其组合起来得到最终的解。而分治与递归很多情况下都是一起结合使用的,能发挥出奇效(1+1>2),这篇文章我们将先从递归说起,再逐渐向分治过渡,主要讲解方式是通过9个例题来说明问题的,问题都是根据难度由简到难,由浅入深,对递归与分治能有个大概的了解雏形,当然最重要还是要做大量练习才能掌握。 0、递归    0.0、Fibonacci数列(易)    0.1、阶乘(易)    0.2、小青蛙跳台阶(易)    0.3、全排列问题(偏难)    0.4、整数划分(偏难) 1、分治策略    1.0、归并排序(一般)    1.1、二分查找(易)    1.2、棋盘覆盖(偏难)    1.3、日程表问题(偏难) 递归 我们第一次接触递归一般都是在初学C语言时候的一道题目——Fibonacci数列中看到的,可能刚开始感觉有点不可思议,函数居然可以调用自己!Amazing!但事实如此,它确实存在,而递归也为我们某些算法的设计提供很大的便利,一般来说递归函数在理解起来并不是很难,甚至可以通过数学归纳法给予证明,但一直让人诟病的一点莫过于Debug的时候了,有时候调试一个较为复杂的递归函数能把人逼疯。 我们在这里将会

算法导论笔记(一):复杂度,分治,随机

做~自己de王妃 提交于 2020-01-07 18:59:15
示例与概念 插入排序 归并排序 最坏情况分析与平均情况分析 函数增长的渐进记号 (O(n)) 表示函数增长的上界。即,花费时间不会高于线性增长。 (Theta(n)) 同时表示上界和下界。即,花费时间一定是这个线性增长的。 (Omega(n)) 表示增长的下界。 (o(n)) 表示不渐进紧确的上界。如, (2n =O(n^2)) , (n^2=O(n^2)) , (2n=O(n^2)) ,但 (n^2neq o(n^2)) (omega(n)) 与 (o(n)) 类似,表示不紧确的下界。 此外,常用 (T(n)) 表示所需的实际时间的函数。 分析分治算法,以归并排序为例 归并排序最坏运行时间的递归式: [ T(n)=begin{cases}Theta(1)&text{若} n=1,\ 2T(n/2)+Theta(n)&text{若} n>1. end{cases} ] 除使用主定理外,还可以这样理解递归式的值:将递归过程看做一个二叉树。递归调用中的每一层的总代价均为 (cn) ,其中 (c) 为常数。而二叉树的层数应为 (log_2n+1) 。故,整个算法的代价期望为 (Theta(nlog_2n)) 。 分治法 分治法求最大和的子数组 分解。将数组划分为两个子数组。此时,只存在三种子数组: 全部位于中点左侧的子数组 全部位于中点右侧的子数组 跨越中点的子数组 解决

复杂度分析之空间复杂度

自古美人都是妖i 提交于 2020-01-07 17:46:36
几种空间复杂度 1. 常量空间 当算法的存储空间大小固定,和输入规模没有直接的关系时,空间 复杂度记作O(1)。例如下面这段程序: void fun1(int n){ int var = 3; … } 2. 线性空间 当算法分配的空间是一个线性的集合(如数组),并且集合大小和 输入规模n成正比时,空间复杂度记作O(n)。 例如下面这段程序: void fun2(int n){ int[] array = new int[n]; … } 3. 二维空间 当算法分配的空间是一个二维数组集合,并且集合的长度和宽度都 与输入规模n成正比时,空间复杂度记作O(n2)。 例如下面这段程序: void fun3(int n){ int[][] matrix = new int[n][n]; … } 4. 递归空间 递归是一个比较特殊的场景。虽然递归代码中并没有显式地声明变量或集合,但是计算机在执行程序时,会专门分配一块内存,用来存储“方法调用栈”。“方法调用栈”包括进栈和出栈两个行为。当进入一个新方法时,执行入栈操作,把调用的方法和参数信息压入栈中。当方法返回时,执行出栈操作,把调用的方法和参数信息从栈中弹出。 下面这段程序是一个标准的递归程序: void fun4(int n){ if(n<=1){ return; } fun4(n-1); … } 假如初始传入参数值n=5,那么方法fun4