代码随想录训练营Day20 | Leetcode 654、617、700、98
一、654 最大二叉树
题目链接:654 最大二叉树
核心:利用前序遍历递归构造二叉树(通常构造树都采用前序遍历)
第一,确定递归函数的参数和返回值:参数是存储元素的数组,返回值是构造后树的根节点;另外切割数组需额外定义新的数组,导致效率降低,因此使用下标直接在原数组上操作,那么参数还需要构造树的元素数组的区间;
第二,确定终止条件:区间为空即无节点元素,则返回nullptr;(注意:判断区间为空是因为单层递归逻辑中是可能存在空节点的)
第三,确定单层递归的逻辑:
首先找到数组中的最大值及其所处的下标(其实知道下标也知道最大值了),以该最大值构造根节点root,以此下标对数组分成左、右子树;
然后,利用左区间递归构造左子树;以及利用右区间递归构造右子树。
注意:区间要保证循环不变量,即所有区间都保持左闭右开或左闭右闭。
//递归函数,nums区间是[left,right),返回根节点root
TreeNode* traversal(vector<int>& nums,int left,int right)
{
//1.区间为空,即无节点,返回nullptr
if(left>=right)
return nullptr;
//2.寻找nums最大值所在索引,构造root
int maxIndex=left;
for(int i=left+1;i<right;++i)
{
if(nums[i]>nums[maxIndex])
maxIndex=i;
}
TreeNode* root=new TreeNode(nums[maxIndex]);
//3.分割左右区间,递归构造左右子树,左区间:[left,maxIndex),右区间:[maxIndex+1,right)
root->left=traversal(nums,left,maxIndex);
root->right=traversal(nums,maxIndex+1,right);
return root;
}
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
return traversal(nums,0,nums.size()); //注意区间是左开右闭
}
二、617 合并二叉树
题目链接:617 合并二叉树
核心:对两棵树的同一位置进行相同操作
(1)迭代法,类似层序遍历:借助队列,先将两棵树的根节点都入队,从队列中取出2个节点元素后,两节点值累加到节点1上;然后在两棵树的当前节点的左孩子都不为空时,进行入队操作,以及右孩子都不为空时也进行入队操作;而在树1的左孩子或右孩子为空,但树2的左孩子或右孩子不为空时,直接将树2的左孩子或右孩子赋值给树1的左孩子或右孩子;最后返回树1。
(2)递归法:采用前序遍历(中左右),对中的处理是2个节点值累加到树1上,然后对左子树和右子树进行递归处理。
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
//递归法:前序遍历,中左右
if(!root1) return root2;
if(!root2) return root1;
root1->val +=root2->val; //【中】
root1->left=mergeTrees(root1->left,root2->left); //左
root1->right=mergeTrees(root1->right,root2->right); //右
return root1;
/*
//迭代法,类似层序遍历
if(!root1) return root2;
if(!root2) return root1;
queue<TreeNode*> que;
que.push(root1);
que.push(root2);
while(!que.empty())
{
//从队列取出两个树的节点
TreeNode* node1=que.front();
que.pop();
TreeNode* node2=que.front();
que.pop();
node1->val+=node2->val; //一定不为空,两个节点值相加,仍在root1这棵树
if(node1->left && node2->left)
{//node1和node2的左孩子都不为空时加入队列
que.push(node1->left);
que.push(node2->left);
}
if(node1->right && node2->right)
{//右孩子都不为空时加入队列
que.push(node1->right);
que.push(node2->right);
}
if(!node1->left && node2->left)
node1->left=node2->left; //node1左孩子为空,node2左孩子不为空,赋值给node1
if(!node1->right && node2->right)
node1->right=node2->right; //node1右孩子为空node2右孩子不为空,赋值给node1
}
return root1; //最后返回root1,虽然一直对node1处理,但node1是root1的根节点
*/
}
三、700 二叉搜索树中的搜索
题目链接:700 二叉搜索树中的搜索
核心:明确二叉搜索树是根节点的左孩子都小于根节点,且右孩子都大于根节点,因此中序遍历的元素一定是从小到大,且不存在重复的元素
(1)迭代法:只要当前节点不为空,则循环遍历,循环处理时需根据当前节点值与val的大小确定对左子树进行遍历还是对右子树进行遍历;
(2)递归法:
首先,递归函数的参数是根节点和待搜索值val,返回值是与val相等的节点;
其次,终止条件是:若root为空,或者root值与val相等(已找到此节点),都需要返回root;
最后,单层递归处理时,需根据当前节点值与val的大小确定搜索左子树还是右子树。
TreeNode* searchBST(TreeNode* root, int val) {
//递归法:
if(!root || root->val == val)
return root; //要么为空要么找到val节点,都返回此时root
TreeNode* res=nullptr; //返回的节点
if(root->val > val)
res=searchBST(root->left,val); //左子树搜索,如果找到了就返回res
else if(root->val < val)
res=searchBST(root->right,val); //右子树搜索
return res;
/*
//迭代法:
while(root)
{//只要当前节点不为空就需要继续搜索
if(root->val > val)
root=root->left;
else if(root->val <val)
root=root->right;
else
return root; //root->val == val,找到了,返回此时的root
}
return nullptr; //遍历所有节点都没有找到val
*/
}
四、98 验证二叉搜索树
题目链接:98 验证二叉搜索树
核心:明确二叉搜索树的中序遍历数组一定是从小到大的有序数组,且不存在重复元素。
(1)利用中序遍历得到其有数组,判断该数组所有元素是否都是按照从小到大排列,一旦出现前一个元素大于等于当前元素值,则说明不是二叉搜索树;
(2)同样利用中序遍历,但无需构造数组,直接在遍历每个节点时与前一个节点相比较,若前一个节点值大于等于当前节点值,则返回false;否则说明该节点是符合条件的,需要更新前一个节点(更新到当前节点),继续判断下一个节点。
第二种思想可以通过递归法和迭代法分别实现,递归法的终止条件是当前节点为空时返回true,单层递归逻辑是先左在处理中最后是右,处理中时将当前节点与前一个节点进行比较;迭代法需借助栈来实现(目前理解不够深入)。
vector<int> res;
void traversal(TreeNode* node)
{//中序遍历:左中右
if(!node)
return;
traversal(node->left);
res.push_back(node->val); //得到中序遍历的数组
traversal(node->right);
}
bool isValidBST(TreeNode* root) {
//中序遍历数组必须是从小到大,且不存在重复元素
traversal(root);
for(int i=1;i<res.size();++i)
{
if(res[i]<=res[i-1])
return false; //一旦发现前一个元素之大于等于当前元素,说明不是二叉搜索树
}
return true;
}
bool isValidBST(TreeNode* root) {
//迭代法:中序遍历,左中右,借助栈
stack<TreeNode*> stk;
TreeNode* cur=root; //记录当前节点
TreeNode* pre=nullptr; //记录前一个节点
while(cur || !stk.empty())
{
if(cur)
{//当前节点不为空,需入栈,并向左遍历
stk.push(cur);
cur=cur->left; //左
}
else
{//cur为空,说明左边遍历结束,此时stk栈顶元素是最左边的元素
cur=stk.top(); //【中】
stk.pop();
if(pre && cur->val <= pre->val)
return false;
pre=cur; //更新前一个节点
cur=cur->right; //右【不能遗漏这一步!】
}
}
return true; //遍历结束都没有发现前一个节点值大于等于当前节点值的节点
/*
//递归法:中序遍历,左中右
if(!root)
return true;
bool left=isValidBST(root->left);
if(pre && pre->val >= root->val)
return false; //一旦前一个节点值大于等于当前节点值,则返回false
pre=root; //更新前一个节点
bool right=isValidBST(root->right);
return left && right; //只有左、右子树都符合二叉搜索树
*/
}