<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Recursive &#8211; HU Xiaoxu</title>
	<atom:link href="https://blog.ihuxu.com/tag/recursive/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.ihuxu.com</link>
	<description>a software engineer&#039;s blog</description>
	<lastBuildDate>Sat, 21 Jun 2025 01:44:23 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.6.2</generator>
	<item>
		<title>递归</title>
		<link>https://blog.ihuxu.com/recursive/</link>
					<comments>https://blog.ihuxu.com/recursive/#respond</comments>
		
		<dc:creator><![CDATA[HU Xiaoxu]]></dc:creator>
		<pubDate>Wed, 04 Dec 2019 16:38:43 +0000</pubDate>
				<category><![CDATA[Basic Algorithm]]></category>
		<category><![CDATA[Computer Science]]></category>
		<category><![CDATA[Data Structure and Algorithm]]></category>
		<category><![CDATA[Original]]></category>
		<category><![CDATA[Recursive]]></category>
		<guid isPermaLink="false">http://blog.ihuxu.com/?p=11712</guid>

					<description><![CDATA[递归是什么？ 递归（英语：Recursion），又译为递回，在数学与计算机科学中，是指在函数的定义中使用函数自身的方法 。 维基百科 简单说，就是自身调用自身。 为什么使用递归？ 往往面对一类问题时，如果它的规模足够小或者说达到既定的边界条件时，我们可以直接获取答案。但是，当这类问题的规模比较大时，却往往无法直接获取答案。那么，这个时候就可以通过“自身调用自身”的方式，来不断地减小问题的规模，直到问题的规模被缩减到足够小时，直接将答案返回上层的调用者，最终获取到原问题的解。如果将求解的过程逆过来，那么就是所谓的递推。 通过这种方式，我们可以写出“优雅”的代码去解决规模比较大的问题。进而，避免了通过递推的方式，在每一次递推时产生的复杂的条件判断的问题。 上文中提到经过递归调用，会不断地减小问题的规模，有些作者认为这是一种减治法。 递归的特性 自身调用自身 在上文中，已经提到了这个特性，而且也非常好理解，不再赘述。 回溯时还原现场 在使用递归方法时，其中有一个不得不提的特性——回溯时还原现场。 通过递归调用，程序将执行到极限触达边界条件时，就需要将当前层的调用跳出“调用栈”，在跳出“调用栈”时，需要将一些状态信息还原到上一层场景所属的状态，即所谓的回溯时还原场景。 举个例子 有一颗二叉树，求解。代表以root为根的树的最大高度，即。 首先，我们要定义一个递归函数。在定义函数之前，我们要明确两个重要的事情： 函数的含义，代表了递归函数能为我们解决什么样的问题。在这里，我们定义函数的含义为求解某一个子树的高度。 函数的参数，代表了递归函数求解的问题的规模。在这里，我们定义函数的参数为当前需要遍历的节点 —— 以当前节点为根的子树（问题规模）。 在明确了这两件事情之后，面对一个规模较大的、复杂的问题就会变的简单得多。 下面，我们来看看在使用递归函数解决当前问题的整个过程。 递归求解树的最大高度 记忆化递归 通过上面简单的例子，我们了解了如何通过递归解决一个较大规模的问题。但是，我们会发现，使用递归函数解决的每个子问题的解仅仅被使用了一次。然而，在某些复杂的场景，子问题的解可能被使用若干次。那么这个时候，可以考虑加上备忘录法进行优化，即记忆化递归。 递归 —— 这种方法的思考方式是自顶向下的，也就是说符合我们常人在解决问题时的正向思考的过程。这与动态规划 —— 自底向上的方法恰恰相反（记忆化递归与动态规划的问题，我将在后续的篇章中讲解）。 三个经典问题 递归实现指数型枚举 从 ~ 这 个整数中随机选取任意多个，输出所有可能的选择方案。 在最开始学习算法时，面对这种类似的问题我思考了好久。当打开大牛的代码时，才恍然大悟，原来可以用这么简洁的代码解决 —— 递归。 那么，先来分析下这个“小”问题。我们需要在一个序列中随机选取任意多个数字，也就是说每一个数字都有两种可能——选择或不选择。那么，一共的可能的方案总数即 —— 那么，根据上文提到的方案，我们先来确认两件事情： 明确这两件事情之后，我们还需要一个额外的全局变量（），用来保留递归过程中产生的状态信息 —— 当前调用栈被选中的数字。 时间复杂度 每一个数字有两种可能 —— 选择或不选择 递归实现组合型枚举 从 ~ 这 个整数中随机选出 个，输出所有可能的选择方案。 这个问题与上面的问题的唯一区别是限定了选定元素的个数。 很自然地我们可以想到，在输出答案时我们只需要判断下结果集变量 —— 的大小是不是即可。当然，我们也可以在每次搜索时进行判断 —— 剪枝法，这样可以带来性能上的提升。 时间复杂度 我们可以通过判断当前调用栈中的中的数字个数如果大于，或者加上剩余的所有数字都达不到个的话，那么直接返回，跳出调用栈，进行其他情况的搜索。这样，避免了无谓的搜索，只关心满足条件的结果集。所以时间复杂度就从 降到了 。 递归实现排列型枚举 把 ~<div class="read-more"><a class="btn read-more-btn" href="https://blog.ihuxu.com/recursive/">Read More</a></div>]]></description>
		
					<wfw:commentRss>https://blog.ihuxu.com/recursive/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://qoogle.top/wp-content/uploads/2019/12/演示文稿-1.mp4" length="1596806" type="video/mp4" />

			</item>
	</channel>
</rss>
