CE14.【C++ Cont】练习题组15(二叉树专题)

目录

1.P1305 新二叉树

题目

分析

代码

提交结果

2.P4913 【深基16.例3】二叉树深度 

题目

分析

代码

提交结果

3.★[NOIP 2001 普及组] 求先序排列 

题目

分析

统一做法

算法

示意图

递归函数的模版

代码

提交结果

4. P1827 [USACO3.4] 美国血统 American Heritage

题目

分析

代码

提交结果


1.P1305 新二叉树

题目

新二叉树 - 洛谷

分析

把字母当成对应的ASCII码编号的节点处理,例如a节点,'a'的ASCII码为97,其编号为97号,向往常一样使用前序遍历

代码

#include <iostream>
using namespace std;
const int N = 1000;
char _left[N], _right[N], root, node;
int n;
void preorder(char root)
{
	if (root == '*')
		return;
	cout << root;
	preorder(_left[root]);
	preorder(_right[root]);
}

int main()
{
	cin >> n;
	cin >> root; 
    cin>>_left[root] >> _right[root];//读根节点和其左右子树 
	n--;
	while (n--)
	{
		cin >> node;
        cin >> _left[node] >> _right[node];
	}
	preorder(root);
	return 0;
}

注:不建议写成cin >> root >>_left[root] >> _right[root];和cin >> node >> _left[node] >> _right[node];在Dev C++和VS上测试都有问题,分开读

提交结果

2.P4913 【深基16.例3】二叉树深度 

题目

【深基16.例3】二叉树深度 - 洛谷

分析

二叉树的深度求解方法参见109.【C语言】数据结构之求二叉树的高度文章

公式:二叉树的高度 == 1 + max(左子树的高度, 右子树的高度)

代码

#include <iostream>
#include <algorithm>//max函数 
using namespace std;
const int N=1e6+10;
int _left[N],_right[N],n;
int i=1;//根节点编号为1
int treeheight(int root)
{
	if (root==0)//停止递归的条件 
		return 0;
	
	//root!=0
	int _left_height=treeheight(_left[root]);//左子树高度 
	int _right_height=treeheight(_right[root]);//右子树高度 
	return max(_left_height,_right_height)+1;//返回左右子树高度较大的那个,且根节点高度为1也要加上 
}

int main()
{
	cin>>n;
    while(n--)
	{
		cin>>_left[i]>>_right[i];
		i++;
	}
	cout<<treeheight(1);
    return 0;
}

提交结果

3.★[NOIP 2001 普及组] 求先序排列 

题目

[NOIP 2001 普及组] 求先序排列 - 洛谷

分析

本题和下面这些题是同类题型,且做法固定

给出一棵二叉树的先序中序排列。求出它的后序排列(本文第4题)

注:给出一棵二叉树的先序与后序排列无法还原二叉树,必须知道中序排列!!!

统一做法

★重复此步骤:先确定根节点,再按根节点划分左右子树

以测试用例BADC BDCA为例分析

先确定根节点:

因为中序排列为BADC,后序排列为BDCA,那么根节点一定为A(后序排列最后访问的一定是根!!),打印A

再按根节点划分左右子树:

因为中序排列按左子树->根-->右子树遍历,所以划分为B | A | DC,显然 B为左子树部分(打印B),DC为右子树部分,则后序排列为B | DC | A

处理左子树B:

先确定根节点:B,再按根节点划分左右子树:为空,不用划分

处理右子树DC:先确定根节点:由于中序排列和后序排列都是同一个顺序DC,那么C为根节点(后序排列最后访问的一定是根!!),打印C,再按根节点划分左右子树:D | C | ,则D为C的左子树,打印D,C的右子树不存在.

则打印的前序排列为ABCD

算法

先确定根节点:利用循环来找,设在中序排列找到的下标为pos(可以调用find函数或者写循环来手动查找)

再按根节点划分左右子树:中序排列划分:[left1,pos-1],pos,[pos+1,right1],左区间长度为pos-1-left1+1==pos-left,右区间长度为right1-pos-1+1==right1-pos,则可以推出后序排列的划分[left2,left2+(pos-left1)-1],[left2+(pos-left1),right2-1],right2

再分别对左子树递归: [left1,pos-1]和[left2,left2+(pos-left1)-1],传四个参数

右子树递归:[pos+1,right1]和[left2+(pos-left1),right2-1],传四个参数

示意图

?处下标的算法:绿色部分长度相同,因此(pos-1)-(left1)==(?)-left2

注:left1,right1,left2,right2不一定就在边界处,这里仅示意

递归函数的模版

void print_preorder(int left1,int right1,int left2,int right2)
{
	//先写递归结束条件
	if (......) 
		return; 
	//再确定根节点
	cout<<......;
	int pos=......;
	
	//最后按根节点划分左右子树,对左右子树递归
	//中序排列划分:[left1,pos-1],pos,[pos+1,right1]
	//后序排列的划分:[left2,left2+(pos-left1)-1],[left2+(pos-left1),right2-1],right2
	//左子树:[left1,pos-1]和[left2,left2+(pos-left1)-1]
    //右子树:[pos+1,right1]和[left2+(pos-left1),right2-1]

	//递归处理左子树
	print_preorder(?,?,?,?);
	
	//递归处理右子树
	print_preorder(?,?,?,?);
}

代码

利用递归算法

#include <iostream>
#include <string>
using namespace std;
string inorder_str,postorder_str;

void print_preorder(int left1,int right1,int left2,int right2)
{
	//递归结束条件
	if (right1<left1||right2<left2) 
		return; 
	//先确定根节点
	cout<<postorder_str[right2];
	int pos=inorder_str.find(postorder_str[right2]);
	
	//再按根节点划分左右子树
	//中序排列划分:[left1,pos-1],pos,[pos+1,right1]成立前提:left1<=pos-1<=right1 
	//后序排列的划分:[left2,left2+(pos-left1)-1],[left2+(pos-left1),right2-1],right2成立前提:left2<=left2+(pos-left1)-1<right2-1
	
	//递归处理左子树
	print_preorder(left1,pos-1,left2,left2+(pos-left1)-1);
	
	//递归处理右子树
	print_preorder(pos+1,right1,left2+(pos-left1),right2-1);
}

int main()
{
	cin>>inorder_str>>postorder_str;
	print_preorder(0,inorder_str.size()-1,0,postorder_str.size()-1);
    return 0;
}

注意前序遍历的顺序: (打印)根-->(递归)左-->(递归)右

提交结果

4. P1827 [USACO3.4] 美国血统 American Heritage

题目

[USACO3.4] 美国血统 American Heritage - 洛谷

分析

给中序遍历和前序遍历,打印后序遍历,和第3题思路相同,不再赘述

代码

#include <iostream>
#include <string>
using namespace std;
string inorder_str,preorder_str;

void print_postorder(int left1,int right1,int left2,int right2)
{
	//结束条件
	if (left1>right1||left2>right2) 
		return;
	
	//找根,划分左右子树 
	int pos=inorder_str.find(preorder_str[left2]);
	
	//递归左子树 
	print_postorder(left1,pos-1,left2+1,right2+pos-right1);
	
	//递归右子树
	print_postorder(pos+1,right1,right2+pos-right1+1,right2);
	
	//打印根节点 
	cout<<preorder_str[left2];
}

int main()
{
	cin>>inorder_str>>preorder_str;
	print_postorder(0,inorder_str.size()-1,0,preorder_str.size()-1);
    return 0;
}

注意后序遍历的顺序:(递归)左-->(递归)右-->(打印)根

提交结果

这是一个非常常见的 C++ 开发问题,尤其是在使用 IDE(如 Visual Studio、CLion、VSCode 等)时,**“转到定义”(Go to Definition)功能失效**,可能的原因有很多。我们来详细分析并提供解决方案。 --- ## ❓ 问题描述: 你在使用 IDE 时点击“转到定义”(通常是 `F12` 或右键菜单),但提示: - “无法导航到定义” - “未找到定义” - “没有可用的定义” --- ## 🧠 常见原因与解决方案: ### ✅ 1. **未正确包含头文件** 如果你在 `.cpp` 文件中定义了一个函数,但在 `.h` 文件中没有声明它,IDE 就无法找到定义。 #### 示例: ```cpp // main.cpp #include <iostream> using namespace std; void sayHello() { cout << "Hello" << endl; } int main() { sayHello(); // 此时没有函数声明,IDE 无法跳转 return 0; } ``` #### ✅ 解决方法: 在头文件中声明函数: ```cpp // sayhello.h #pragma once void sayHello(); ``` ```cpp // sayhello.cpp #include "sayhello.h" #include <iostream> using namespace std; void sayHello() { cout << "Hello" << endl; } ``` ```cpp // main.cpp #include "sayhello.h" int main() { sayHello(); // 现在可以跳转 return 0; } ``` --- ### ✅ 2. **函数未定义** 如果你只是声明了一个函数但从未实现它,IDE 也无法跳转。 #### 示例: ```cpp class MyClass { public: void doSomething(); // 只有声明 }; int main() { MyClass obj; obj.doSomething(); // 无法跳转 } ``` #### ✅ 解决方法: 定义函数: ```cpp void MyClass::doSomething() { // 实现 } ``` --- ### ✅ 3. **IDE 未正确索引代码(缓存问题)** 有时候 IDE 的索引或缓存未更新,导致跳转失败。 #### ✅ 解决方法: - **Visual Studio**: - 清理解决方案并重新生成 - 删除 `.sdf` 文件和 `.VC.db` 文件(位于项目目录) - 使用 `Ctrl + ,` 搜索“Rebuild”重建索引 - **VSCode + C/C++ 插件**: - 检查 `.vscode/c_cpp_properties.json` 是否配置正确 - 使用 `C/C++: Rebuild Intellisense` 命令 - 确保 `Include Path` 包含所有头文件路径 - **CLion**: - 重新加载 CMake 项目(Reload CMake) - 清理并重新构建项目 --- ### ✅ 4. **跨文件定义未正确链接** 如果你的函数定义在另一个 `.cpp` 文件中,但未被当前文件 `#include`,IDE 也无法跳转。 #### 示例: ```cpp // file1.cpp #include <iostream> void sayHello() { std::cout << "Hello" << std::endl; } ``` ```cpp // main.cpp void sayHello(); // 声明 int main() { sayHello(); // 虽然能编译,但 IDE 无法跳转 } ``` #### ✅ 解决方法: 将声明写入 `.h` 文件并包含它: ```cpp // sayhello.h #pragma once void sayHello(); ``` ```cpp // main.cpp #include "sayhello.h" int main() { sayHello(); // 现在可以跳转 } ``` --- ### ✅ 5. **宏定义或模板导致跳转失败** 某些复杂的宏或模板代码,IDE 可能无法解析。 #### 示例: ```cpp #define CALL(func) func() void myFunc() {} int main() { CALL(myFunc); // IDE 无法识别 } ``` #### ✅ 解决方法: - 尽量避免宏替换函数调用 - 使用模板时确保类型明确,IDE 有时无法推导 --- ## ✅ 总结:解决“无法转到定义”的步骤 | 步骤 | 操作 | |------|------| | 1 | 确保函数在头文件中声明 | | 2 | 确保函数在源文件中定义 | | 3 | 确保头文件被正确包含 | | 4 | 清理并重建项目,刷新 IDE 缓存 | | 5 | 避免使用宏替换函数调用 | | 6 | 检查 `.vscode/c_cpp_properties.json`(VSCode)等配置是否正确 | --- ##
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhangcoder

赠人玫瑰手有余香,感谢支持~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值