如果对一个数组每次循环都是改变位置,那么我第一次改变的位置会不会影响第二次改变?就是说我第二次循环的时候,第一次的那个改变还存不存在?真的很疑惑
这是我一开始写的版本:
//根据二叉树的前序遍历字符串,构建树,并输出中序遍历结果
#include <bits/stdc++.h>
using namespace std;
string s;
int len;
//定义二叉树的节点
struct node
{
char c;
node *left;
node *right;
};
void createTree(node *root)
{
if (len >= s.length())
return; //到最后一个字符了,结束返回(字符串最后3个字符一定是###)
char c = s[len++];
if (c == '#')
{
root = NULL;
return;
}
else
{
root = new node;
root->c = c;
//左右孩子初始化
root->left = NULL;
root->right = NULL;
//因为字符串s是线序遍历,所以这里根据先序遍历的顺序构造树
createTree(root->left);
createTree(root->right);
}
}
void midOrdTrave(node *node)
{
//中序遍历
if (node != NULL)
{
midOrdTrave(node->left);
cout << node->c << " ";
midOrdTrave(node->right);
}
return;
}
int main()
{
while (cin >> s)
{
node *binaryTreeRoot=NULL;
len = 0;
createTree(binaryTreeRoot);
midOrdTrave(binaryTreeRoot);
cout << endl;
}
return 0;
}
编译时报了Segment fault,提交时报了runtime error错误
不难知道是指针访问了不该访问的内存空间了。
后来发现,是因为main函数中,binaryTreeRoot在经历了createTree之后,仍然是NULL,这是为什么呢?
看过上面的内容,就明白了,错在createTree函数的传参。createTree函数的形参root在new node的时候便指向了一块新的内存地址了,而此时,实参binaryTreeRoot是没有任何变化的,仍然指向NULL。所以之后进行中序遍历,肯定会报错。
这个时候,我们在传参时,因为我们需要操作的是传入的实参指针,而不是形参指针,就需要把binaryTreeRoot的这个指针的地址传进去(和经典的swap例子一样的),才能起到一个真正的、数据双向绑定的效果,即:
int main()
{
while (cin >> s)
{
node *binaryTreeRoot = NULL;
len = 0;
createTree(&binaryTreeRoot);
midOrdTrave(binaryTreeRoot);
cout << endl;
}
return 0;
}
这时候,createTree接收到的参数是地址的地址,也就相当于一个二重指针,所以在定义形参时,需要做一定修改,即
//建树
void createTree(node **root)
在该函数内使用时,**root为被指向的node,*root为指向节点的指针,root为指针的地址。
我们在修改节点内容时,便应该使用(*root->c=new_char)这样的方式来更新。但直接使用会报编译错误,所以我们使用一个tmp指针来过度。
//创建一个新节点
node *tmp = new node;
tmp->c = c;
tmp->left = NULL;
tmp->right = NULL;
*root = tmp;
//因为字符串s是线序遍历,所以这里根据先序遍历的顺序构造树
createTree(&(tmp->left));
createTree(&(tmp->right));
后两行递归也是一样的道理。
那么完整的更新后的代码如下(也可以看题解,一样的):
//根据二叉树的前序遍历字符串,构建树,并输出中序遍历结果
#include <bits/stdc++.h>
using namespace std;
string s;
int len; //每次遍历到的字符串中字符的index
//定义二叉树的节点
struct node
{
char c;
node *left;
node *right;
};
//建树
void createTree(node **root)
{
if (len >= s.length())
return; //到最后一个字符了,结束返回(字符串最后3个字符一定是###)
char c = s[len++];
if (c == '#')
{
*root = NULL;
return;
}
else
{
//创建一个新节点
node *tmp = new node;
tmp->c = c;
tmp->left = NULL;
tmp->right = NULL;
*root = tmp;
//因为字符串s是线序遍历,所以这里根据先序遍历的顺序构造树
createTree(&(tmp->left));
createTree(&(tmp->right));
}
}
void midOrdTrave(node *node)
{
//中序遍历
if (node != NULL)
{
midOrdTrave(node->left);
cout << node->c << " ";
midOrdTrave(node->right);
}
return;
}
int main()
{
while (cin >> s)
{
node *binaryTreeRoot = NULL;
len = 0;
createTree(&binaryTreeRoot);
midOrdTrave(binaryTreeRoot);
cout << endl;
}
return 0;
}
每次循环改变数组位置会影响之后的循环结果,因为每次循环都是基于当前数组的操作,而不是原始数组的操作。例如,第一次循环改变数组的位置会影响第二次循环的结果。为了避免这种情况,可以考虑使用复制原始数组的方式来操作每个循环。以下是示例代码:
// 原始数组
int arr[] = {1, 2, 3, 4, 5};
// 复制数组
int copiedArr[sizeof(arr)/sizeof(int)];
memcpy(copiedArr, arr, sizeof(arr));
// 操作复制后的数组
for(int i = 0; i < sizeof(arr)/sizeof(int); i++) {
int temp = copiedArr[i];
copiedArr[i] = copiedArr[sizeof(arr)/sizeof(int)-1];
copiedArr[sizeof(arr)/sizeof(int)-1] = temp;
}
// 输出复制后的数组和原始数组
for(int i = 0; i < sizeof(arr)/sizeof(int); i++) {
cout<<copiedArr[i]<<" ";
}
cout<<endl;
for(int i = 0; i < sizeof(arr)/sizeof(int); i++) {
cout<<arr[i]<<" ";
}
cout<<endl;
当然存在,否则改了个寂寞