在树的数据结构里面,少不了递归。
哈喽哈喽,大家好,我是你们的金樽清酒。最近呀,我在研究研究算法这个数据结构,今天研究了一天,写了几道简单的算法题,昨天呢,我是研究了一下链表。我总结出一个结论,那就是树的题目呀就是一直递呀递呀归。那有人可能会问了,你昨天不是研究链表去了嘛,那链表呢?诶,别急,今天的主角是树,其实链表也有一定结论,那就是指呀指呀针。哎呀,不多说了,我们这篇文章的主角是树。下篇一定写一篇链表。
认识递归
递归是编程中一种强大的技术,特别适合处理问题的自相似性质,例如树的遍历、动态规划等。在计算机科学中,递归函数通过在其函数体内调用自身来解决问题。这种方法允许问题分解成更小的、相同类型的子问题,直到最小的问题可以直接解决。
举个例子,考虑计算阶乘的递归函数:
javascriptCopy Code
function factorial(n) {
if (n === 0) {
return 1;
}
return n * factorial(n - 1);
}
在这个函数中,如果 n 等于 0,则返回1,否则返回 n 乘以 factorial(n - 1)。这种方式使得我们可以递归地计算阶乘,直到达到基本情况 n === 0。
递归虽然强大,但也需要小心使用,因为不正确的递归实现可能导致无限循环或者堆栈溢出。在编写递归函数时,需要确保:
- 定义清晰的基本情况,即递归终止条件。
- 确保每次递归调用都能朝着基本情况逼近。
- 处理递归过程中的中间状态,确保问题可以正确分解和合并。
递归是怎么来的呢?其实就是函数的执行是有个函数栈的。栈都知道吧,先进后出。我们在执行这个函数的时候,也调用了自身。由于找不到结果,会一层层的函数执行,这就是递。然后总有一个终止条件有结果,可是通过那个值来求最初要的值,这就是归。啊啊啊啊啊啊,就是蛮抽象的,脑子有时候转不过来。
为什么说树离不开递归
我们这里主要针对二叉树哦。我们的二叉树不是简单的线性结构。他是分左右的。但是呢,你看他每个节点是不是都是相似的呢,有左指针有右指针有值。而看我们的递归就是解决这种自相似的问题。既然每个节点的结构都差不多,那我们就可以用一个方法来处理每一个节点,靠递归解决问题。有小伙伴说了,就这就这,你就说树离不开递归。别急别急,小编今天递归一天了,将一些题目给你们奉上。
关于树的一些简单的leetcode题
- 树的前中后序遍历,递它
//递归实现三序遍历。
function prevOrder(root) {//前序遍历
if (!root) return null;
let arr = []
arr.push(root.val)
if (root.left) {
arr = arr.concat(prevOrder(root.left))
}
if (root.right) {
arr = arr.concat(prevOrder(root.right))
}
return arr
}
function midOrder(root) {//中序遍历
if (!root) return null
let arr = []
if (root.left) {
arr = arr.concat(midOrder(root.left))
}
arr.push(root.val)
if (root.right) {
arr = arr.concat(midOrder(root.right))
}
return arr
}
function postOrder(root) {
if (!root) return null
let arr = []
if (root.left) {
arr = arr.concat(postOrder(root.left))
}
if (root.right) {
arr = arr.concat(postOrder(root.right))
}
arr.push(root.val)
return arr
}
你看看,我们要遍历它,就是将某个节点的值存入数组。然后某个节点又要执行这个操作。子节点又要执行这个操作,话不多说递它,我就能遍历这个树,访问每个节点,还能把事情解决了,岂不快哉。但是说实话递归确实抽象,递归不是一种算法,它是一种机制,合理利用机制就能解决很多的问题。妈妈我不想递归啦。
- 反转二叉树
什么是反转二叉树?那就是将树的每一个节点的左,右对调。诶,你是不是又想到了,每一个节点都要执行同样的操作,那还等啥,递它,其实树的递归更像遍历这棵树。
function reverseTree(root) {//利用递归的方式,将每一个节点互换,就可以达到对称的效果。
if (root == null) {
return null;
}
let temp = root.left;
root.left = root.right;
root.right = temp;
reverseTree(root.left);
reverseTree(root.right);
return root;
}
- 求二叉树的最大深度
var calculateDepth = function (root) {//这种方法好像采用了递归的思想
//把递归看成一个个的调用栈,最后找到突破口。
if (root == null) {
return 0;
}
let left = calculateDepth(root.left);
let right = calculateDepth(root.right);
return Math.max(left + 1, right + 1);
};
怎么写这道题,很简单,递归呗。每个节点访问左值和右值,然后每加1。root===null就是终止条件了。 那我估计你也知道怎么求二叉树的最小深度了,可不是直接把max改为min啊,有些特殊情况过不了。因为什么呢?求最小,子节点如果右边没有,左边有,那么直接改成min那深度就是子节点算右边没有的那个深度,而真正的深度是左边有子节点的那个深度。听懂掌声。
- 求二叉树的最小深度
var MinDepth = function (root) {//这种方法好像采用了递归的思想
//把递归看成一个个的调用栈,最后找到突破口。
if (root == null) {
return 0;
}
// 如果是叶子节点,则返回深度 1
if (root.left == null && root.right == null) {
return 1;
}
// 如果左子树为空,只计算右子树的深度
if (root.left == null) {
return MinDepth(root.right) + 1;
}
// 如果右子树为空,只计算左子树的深度
if (root.right == null) {
return MinDepth(root.left) + 1;
}
// 否则,计算左右子树深度的较小值
return Math.min(MinDepth(root.left), MinDepth(root.right)) + 1;
};
- 平衡二叉树
这玩意是递中递。首先先解释一下什么叫平衡二叉树。就是每一个节点,它的子节点的深度相差不超过1.注意是每一个节点,不单单是根节点。有的小伙伴会说很简单,我遍历一下左边,再遍历一下右边,看看它们的深度差呗。诶,没错是计算深度差,但是是每一个节点的深度差。而我们那个的求最大深度和最小深度其实只针对根节点。那根节点都能求,其他节点求不了,都是同样的方法嘛,那就递呗,本来求深度就是递归,还要每一个节点,那就递中递呗。
var isBalanced = function (root) {
let minDepth = function (root) {
if (root == null) {
return 0;
}
if (root.left == null && root.right == null) {
return 1;
}
if (root.left == null) {
return minDepth(root.right) + 1;
}
if (root.right == null) {
return minDepth(root.left) + 1;
}
return Math.min(minDepth(root.left), minDepth(root.right)) + 1;
};
// 辅助函数:计算最大深度
let maxDepth = function (root) {
if (root == null) {
return 0;
}
let left = maxDepth(root.left);
let right = maxDepth(root.right);
return Math.max(left, right) + 1;
};
// 检查平衡性
let checkBalance = function (root) {
if (root == null) {
return true;
}
let leftDepth = maxDepth(root.left);
let rightDepth = maxDepth(root.right);
if (Math.abs(leftDepth - rightDepth) > 1) {
return false;
}
return checkBalance(root.left) && checkBalance(root.right);
};
return checkBalance(root);
};
结合上面的辅助函数,写出判断函数,然后递它,对每一个节点进行判断。咦,果然树就离不开递归。递归也经常是比较抽象的。我们这么递了一天。妈妈我不想再递递递递递归了。