刷654.最大二叉树的时候遇到的报错:
引发错误的代码:
class Solution {
public:
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
//终止条件:数组内没有没遍历到的元素了,也就是切出来的左区间和右区间都是NULL
//也可以写成,nums数组大小==1,也就是到了最后一个叶子节点
if(nums.size()==1){
return new TreeNode(nums[0]); //如果只有一个元素,就返回这个节点作为根节点
}
//本题目的要求(提示)里面写了nums数组大小>=1,所以不用考虑本来就是空的情况
//找最大值及其下标
int maxValue=INT_MIN;
int index;
int maxIndex;
for(index=0;index<nums.size();index++){
if(nums[index]>maxValue){
maxValue = nums[index];//最大值
maxIndex = index;//最大值的下标
}
}
//找到最大值之后,根节点数值确定
TreeNode* root = new TreeNode(maxValue);
//nums.erase(nums[maxIndex]);
//分割数组
//左数组,左闭右开
vector<int>left(nums.begin(),nums.begin()+maxIndex);
//右数组,左闭右开
vector<int>right(nums.begin()+maxIndex+1,nums.end());
//分割数组后进行左右子树的递归
root->left = constructMaximumBinaryTree(left);
root->right = constructMaximumBinaryTree(right);
return root;
}
};
这个错误通常是由于对空的或者无效的迭代器进行操作引发的,这种操作包括尝试对空迭代器进行递增或者递减等。
此错误表示程序正在尝试在空指针上应用非零偏移,这在 C++ 中是未定义行为。这个错误可能发生在尝试从一个空的 vector 中访问元素时。具体来说,在上面代码中,当 nums 是空数组或者 maxIndex 为0时,下面的创建子数组的操作可能会引发问题:
//左数组,左闭右开
vector<int>left(nums.begin(),nums.begin()+maxIndex);
当 maxIndex 为0,即nums.begin() + maxIndex指向nums.begin(),那么以上代码将尝试在一个空数组上进行操作,引发错误。当起始点和结束点一样时,得到的 vector 为空即不包含任何元素,这样下一层递归就会出问题了。
类似的,当 maxIndex 为 nums.size() - 1 时,以下代码也会产生一个空数组:
//右数组,左闭右开
vector<int>right(nums.begin()+maxIndex+1,nums.end());
需要修改代码来正确处理这些边缘情况。我们可以在递归调用 constructMaximumBinaryTree 之前,检查子数组是否为空。修改后的代码如下:
class Solution {
public:
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
//终止条件:数组内没有没遍历到的元素了,也就是切出来的左区间和右区间都是NULL
//也可以写成,nums数组大小==1,也就是到了最后一个叶子节点
if(nums.size()==1){
return new TreeNode(nums[0]); //如果只有一个元素,就返回这个节点作为根节点
}
//本题目的要求(提示)里面写了nums数组大小>=1,所以不用考虑本来就是空的情况
//找最大值及其下标
int maxValue=INT_MIN;
int index;
int maxIndex;
for(index=0;index<nums.size();index++){
if(nums[index]>maxValue){
maxValue = nums[index];//最大值
maxIndex = index;//最大值的下标
}
}
//找到最大值之后,根节点数值确定
TreeNode* root = new TreeNode(maxValue);
//nums.erase(nums[maxIndex]);
//分割数组
//左数组,左闭右开
vector<int>left;
if(maxIndex>0){
left = vector<int>(nums.begin(),nums.begin()+maxIndex);
//分割数组后进行左右子树的递归
//这种写法需要把left的部分也加到if里面,否则还是操作空数组!
root->left = constructMaximumBinaryTree(left);
}
//右数组,左闭右开
vector<int>right;
if(maxIndex<(nums.size()-1)){
right = vector<int>(nums.begin()+maxIndex+1,nums.end());
root->right = constructMaximumBinaryTree(right);
}
return root;
}
};
这样修改后,当子数组为空时,就不会对其进行递归操作,避免了在空数组上进行未定义操作。
注意
本题的报错并不是因为vector数组vectorA(a,b)构造的时候是否允许传入的两个参数相等,也就是a=b。
在 C++ 的 std::vector 中,允许通过提供两个相等的迭代器来构造一个空的向量。在这种情况下,std::vector 的构造函数会创建一个不含任何元素的向量。这是完全合法且没有问题的。
问题发生在我们尝试在空向量上进行一些操作的时候,例如在错误代码中做的,将空向量left传递给了 constructMaximumBinaryTree 函数,而这个函数并没有正确处理空向量的情况,导致了运行时错误。
在处理向量或者其他容器类型的时候,一个好的做法是总是先检查容器是否为空,然后再进行任何操作。这样可以避免很多可能的问题和错误。
关于终止条件的问题
这道题力扣上有一条说明是nums不为空,至少含有一个元素。
因此,我们使用 if(nums.size()==1) 作为终止条件。由于输入数组 nums 的长度至少为 1,所以不需要考虑 nums 为空的情况。
但实际上,constructMaximumBinaryTree 是一个递归函数,在递归过程中,我们从 nums 中切出一部分数组作为新的输入,所以即使开始的时候 nums 是非空的,递归过程中也可能出现空数组。
例如,假设 nums = [1, 2]。第一次调用 constructMaximumBinaryTree(nums) 时,会找到 2 是最大的元素,然后将 nums 分成 left = [1] 和 right = []。接下来对 left 和 right 进行递归调用,constructMaximumBinaryTree(left) 的结果是没有问题的,但 constructMaximumBinaryTree(right) 就会导致错误,因为 right 是空的。
因此,建议仍然将 if(nums.empty()) 作为递归终止条件,这样可以避免操作空数组,而且以if(nums.empty)作为终止条件的话,也不需要加上任何空数组的判定。因为空数组在第一步终止条件就会返回。