大三寒假要结束了,继续备战秋招,年前刷了些双指针、数组、链表的简单LeetCode题,都没有做笔记,现在也忘得差不多了,计划写一份专栏记录刷题的过程,复盘算法中的细节,由易到难,先刷简单题,再斩中等题!
一转眼就到4月份了,投递了好多暑期实习,4月份笔面试就会陆续开启,距离秋招也越来越近,这段时间在刷LeetCode Hot 100,每天刷个两道mid题,同时作个笔记,作为复盘,也作为到时笔面试前的复习资料
目录
一、关键思路:递归
递归是一种算法或函数的设计技巧,通过在函数定义中调用自身来解决问题。在数据结构与算法中,递归通常用于解决可分解为多个相似子问题的问题。递归算法通常包含两部分:基本情况和递归情况。
基本情况是递归的出口,即在问题可以直接解决或不需要递归时的情况。
递归情况则是指通过将问题分解成更小的子问题,并将子问题递归地解决来解决原始问题的情况
二、递归法优点
递归算法通常具有简洁、优美和可读性强的优点,在编写递归时可以不用过于注重代码逻辑的具体细节,只要注意基本情况和递归情况的设计,以及递归深度的控制
三、例题训练
3.1 从前序与中序遍历序列构造二叉树
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
示例 1:
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7] 输出: [3,9,20,null,null,15,7] 示例 2:
输入: preorder = [-1], inorder = [-1] 输出: [-1]
来源:力扣(LeetCode) 链接:力扣
- 实现思路:
首先,使用一个哈希表存储中序遍历序列中每个节点的位置,便于在递归构造二叉树时快速找到根节点的位置。
其次,递归构造二叉树。递归函数的参数包括当前子树在前序遍历序列中的起始位置、子树在中序遍历序列中的起始位置和结束位置。在递归函数中,首先根据当前子树在前序遍历序列中的起始位置得到根节点的值,然后在中序遍历序列中找到根节点的位置,将中序遍历序列分为左右两部分,分别递归构造左右子树,最后将左右子树挂在根节点上并返回根节点。
- 核心思路:
根据前序遍历的特点找到根节点的值,再在哈希表中快速定位到根节点的下标,进而可以得出左子树的长度,接着可以计算出后序遍历和中序遍历中左右子树的长度,根据这些参数递归调用myBuildTree(),构造左右子树
- C++代码实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
private:
unordered_map<int, int> index;
public:
TreeNode* myBuildTree(const vector<int>& preorder, const vector<int>& inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
if (preorder_left > preorder_right) {
return nullptr;
}
// 前序遍历中的第一个节点就是根节点
int preorder_root_index=preorder_left;
// 在中序遍历中定位根节点
int inorder_root_index=index[preorder[preorder_root_index]];
// 先把根节点建立出来
TreeNode* root = new TreeNode((preorder[preorder_root_index]));
// 得到左子树中的节点数目
int size_left_subtree = inorder_root_index-inorder_left;
// 递归地构造左子树,并连接到根节点
// 先序遍历中「从 左边界+1 开始到 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
root->left=myBuildTree(preorder,inorder,preorder_left+1,preorder_left+size_left_subtree,inorder_left,inorder_root_index-1);
// 递归地构造右子树,并连接到根节点
// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root->right=myBuildTree(preorder,inorder,preorder_left+size_left_subtree+1,preorder_right,inorder_root_index+1,inorder_right);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n = preorder.size();
// 构造哈希映射,帮助我们快速定位根节点
for (int i = 0; i < n; ++i) {
index[inorder[i]] = i;
}
return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
}
};
3.2 从中序与后序遍历序列构造二叉树
给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
示例 1:
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3] 输出:[3,9,20,null,null,15,7] 示例 2:
输入:inorder = [-1], postorder = [-1] 输出:[-1]
来源:力扣(LeetCode) 链接:力扣 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
- 实现思路:
首先,使用一个哈希表存储中序遍历序列中每个节点的位置,便于在递归构造二叉树时快速找到根节点的位置。
其次,递归构造二叉树。递归函数的参数包括当前子树在后序遍历序列中的起始位置、子树在中序遍历序列中的起始位置和结束位置。在递归函数中,首先根据当前子树在后序遍历序列中的起始位置得到根节点的值,然后在中序遍历序列中找到根节点的位置,将中序遍历序列分为左右两部分,分别递归构造左右子树,最后将左右子树挂在根节点上并返回根节点。
- 核心思路:
根据后序遍历的特点找到根节点的值,再在哈希表中快速定位到根节点的下标,进而可以得出左子树的长度,接着可以计算出后序遍历和中序遍历中左右子树的长度,根据这些参数递归调用myBuildTree(),构造左右子树
- C++代码实现:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
unordered_map<int,int>index;
TreeNode* myBuildTree(const vector<int>&postorder,const vector<int>&inorder,int postorder_left,int postorder_right,int inorder_left,int inorder_right)
{
if(postorder_left>postorder_right)
return NULL;
// 后序遍历中的最后一个节点就是根节点
int postorder_root_index=postorder_right;
// 在中序遍历中定位根节点
int inorder_root_index=index[postorder[postorder_root_index]];
// 先把根节点建立出来
TreeNode* root =new TreeNode(postorder[postorder_root_index]);
// 得到左子树中的节点数目
int size_left_subtree = inorder_root_index-inorder_left;
// 递归地构造左子树,并连接到根节点
// 后序遍历中「从 左边界 开始到 (左边界+size_left_subtree-1)个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
root->left=myBuildTree(postorder,inorder,postorder_left,postorder_left+size_left_subtree-1,inorder_left,inorder_root_index-1);
// 递归地构造右子树,并连接到根节点
// 后序遍历中「从 (左边界+左子树节点数目) 开始到 根节点定位-1」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root->right=myBuildTree(postorder,inorder,postorder_left+size_left_subtree,postorder_root_index-1,inorder_root_index+1,inorder_right);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder)
{
int n = postorder.size();
for(int i=0;i<n;i++)
{
index[inorder[i]]=i;
}
return myBuildTree(postorder,inorder,0,n-1,0,n-1);
}
};