目录
1.什么是递归
1.1二叉树的遍历
1.2快速排序
1.3归并排序
2.为什么会用到递归
3.如何理解递归
4.如何写好一个递归
5.什么是搜索
5.1深度(dfs)优先遍历&优先搜索
5.2宽度(bfs)优先遍历&优先搜索
6.回溯
1.什么是递归
我们呢下面介绍一下递归的几个使用的场景,这个里面不会介绍像这个斐波那契数列那样的递归(就是数学函数,很容易理解),我们就拿数据结构里面的排序算法和二叉树的遍历作为例子熟悉一下这个过程
1.1二叉树的遍历
我们这个地方是以后序遍历作为例子。对于一个二叉树而言,这个后序遍历的时候我们先遍历根节点的左子树,再去遍历根节点的右子树。最后遍历这个根节点;
在遍历左子树的时候,我们要先遍历这个左子树的根节点的左子树,再去遍历这个左子树根节点的右子树,以此类推,对于任何一个子树,我们都会先遍历这个根节点的左子树,再去遍历这个根节点的右子树;
1.2快速排序
快速排序和下面介绍的这个归并排序对于初学者而言很相似(我就是这个感觉),但是两个排序算法还是有很多的差异的;
快速排序之所以敢这么叫这个名字,自身的这个时间消耗上面肯定是可以的,它是有使用价值的,并不想其他的一些排序算法只有教学意义,没有实践意义;
快排需要先确定一个基准元素,把小于这个元素的放到左边,大于这个元素的放到右边,分别对于这个左边的和右边的进行排序,还是选择基准元素,按照上面的方法进行下去;
1.3归并排序
归并排序就和上面的快速排序有点不同了,因为归并排序是直接从中间分开,然后再把这个自己左右分开,直到分成一个一个的元素,最后把这个元素有序的组合起来即可;
下面的这个就是归并排序的过程展示:就是分开之后合并的过程,这个合并的时候我们是使用的双指针的方法合并的(通过指针的移动);
2.为什么会用到递归
我们在解决一个问题的时候,把这个问题分解为诸多的子问题,可以使用一个方法解决这个主问题,但是解决子问题的时候同样可以使用这个方法解决;
3.如何理解递归
3.1递归展开细节图:这个是初学的时候,老师经常搞的一种方法,但是这个并不一定会简化我们对于这个递归问题的理解;
3.2二叉树的题目:我们在二叉树学习的时候,尤其是涉及到这个二叉树的遍历,因为二叉树的遍历里面遍历任何一个子树的方法都是一样的,他们都是若干个子问题,我们就可以直接使用递归解决这个问题;
3.3宏观看待递归过程:我们跳出上面的递归细节展开图,找准子问题,只需要关注一个问题的实现,再去套用这个方法解决其他的问题;
下面我们按照这个思路简单的写一下dfs(深度遍历)和归并的伪代码:
我们没有实现,但是我们先把这个root->left作为函数的参数,root->right作为函数的参数,最后判断这个结束条件(到达叶子结点就结束);
void dfs(treenode* root)
{
//结束条件:遇到叶子结点
if (root == NULL)
{
return;
}
dfs(root->left);
dfs(root->right);
printf("%d", root->val);
}
我们先计算这个mid值大小,再把这个mid作为参数传递进去,分别传递这个左边区间,和右边区间,使用这个函数进行排序,最后合并两个有序数组,结束条件就是left>=right,结束递归的过程;
void merge(int* nums, int left, int right)
{
//递归结束的出口
if (left >= right)
{
return;
}
int mid = (left + right) / 2;
merge(nums, left, mid);
merge(nums, mid + 1,right);
//合并两个有序数组
}
4.如何写好一个递归
4.1函数头的书写:找到一个主问题里面的字问题,看看是否可以使用相同的方法解决子问题;
4.2函数体的书写:只关注某一个子问题,来进行函数体的实现;
4.3结束条件:找这个递归的出口,作为结束递归的条件;
5.什么是搜索
5.1深度(dfs)优先遍历&优先搜索
深度就是一条路走到尽头之后再去折返回去,这个里面遍历只是过程的一种形式,搜索才是真正想要达到的目的;
5.2宽度(bfs)优先遍历&优先搜索
宽度就是你一层一层的进行,按照这个二叉树的层状结构进行遍历,这一层结束之后进行下一层;
6.回溯
回溯就是深度搜索,我们可以举例一下这个走迷宫的问题帮助我们理解一下,当我们走到一个迷宫的某一个节点的时候,我们有多个选择,我们肯定是只能走其中的一条,这个时候,我们认准一条路并且总下去,这个时候,我们发现走到了死胡同,这个时候我们就需要则返回那个岔路口,这个从现在所在位置返回到刚刚做选择的岔路口就是一个回溯的过程,因此我们说这个回溯和深度搜索没有什么本质的区别,都是一条路走到黑再去选择另外的一条路,仅此而已。