实验4:树与二叉树、查找
写在报告前:老师、学长好,由于我完成本次实验是在每次学习公告发布后,根据实验大纲顺序分别完成的,故报告编写顺序与课堂派“实验报告”作业中要求的实验内容顺序有出入,为方便阅读做如下说明,谢谢耐心阅读!
任务一、二:先序序列创建二叉树、层级遍历二叉树:"PTA编程:先序序列创建二叉树"。&"PTA编程:二叉树层次遍历(广度优先)
任务三:树的递归结构(求二叉树高度): PTA函数:先序输出叶结点。&PTA函数:求二叉树高度。
任务四:二叉排序树:创建二叉排序树并遍历。&BST(二叉排序树)的查找、插入与建树与删除
任务五:QQ账户的申请与登录:哈希表的应用:QQ账户查询或航空公司VIP客户查询。
graph TD;
a[1. PTA编程:先序序列创建二叉树。]-->c[任务一、二:先序序列创建二叉树、层级遍历二叉树];
b["4. PTA编程:二叉树层次遍历(广度优先)"]-->c;
d[2. PTA函数:先序输出叶结点。]-->e[任务三:树的递归结构(求二叉树高度)];
f[3. PTA函数:求二叉树高度。]-->e;
z[5. 创建二叉排序树并遍历。]-->y[任务四:二叉排序树];
hj[6. BST(二叉排序树)的查找、插入与建树与删除]-->y;
ch[7. 哈希表的应用:QQ账户查询或航空公司VIP客户查询。]-->td[**任务五:QQ账户的申请与登录**];
集美大学课程实验报告-实验4:树与二叉树、查找
| 项目名称 | 内容 |
|---|---|
| 课程名称 | 数据结构 |
| 班级 | 网安2411 |
| 指导教师 | 郑如滨 |
| 学生姓名 | 于鸿硕 |
| 学号 | 202421336018 |
| 实验项目名称 | 实验4:树与二叉树、查找 |
| 上机实践日期 | |
| 上机实践时间 | 2学时 |
一、目的(本次实验所涉及并要求掌握的知识点)
-
树与二叉树的基本操作 使用树的左右子树特性进行递归 利用二叉排序树进行查找 运用查找功能实现简单查找场景
二、实验内容与设计思想
#include <iostream>
#include<string>
#include<map>
using namespace std;
return 0;
typedef int ElemType;
typedef struct BiTNode{
ElemType data;
struct BiTNode *lchild, *rchild;
BiTNode(ElemType d) : data(d), lchild(nullptr), rchild(nullptr) {}
}BiTNode,*BiTree;
任务一、二:先序序列创建二叉树、层级遍历二叉树
//伪代码:
//普通创建
BIT root = new BITnode(1);
Bit node2=.....
root的lchild=node2;
root的rchild=node3;
...
//先序串创建
if(length>size||preorder[i]=='#')
{
i++;
return null;
}
BIT root=new BIT();
root-l=嵌套函数;
root-r=嵌套函数;
return root;
//代码:
#include<iostream>
using namespace std;
typedef int ElemType;
typedef struct BiTNode{
ElemType data;
struct BiTNode *lchild, *rchild;
BiTNode(ElemType d) : data(d), lchild(nullptr), rchild(nullptr) {}
}BiTNode,*BiTree;
//创建二叉树(任务一)
BiTree createBiTree()
{
BiTree root = new BiTNode(1);
BiTree node2 =new BiTNode(2);
BiTree node3 = new BiTNode(3);
BiTree node4 = new BiTNode(4);
BiTree node5 = new BiTNode(5);
BiTree node6 = new BiTNode(6);
root->lchild = node2;
root->rchild = node3;
node2->rchild = node4;
node3->lchild = node5;
node3->rchild = node6;
return root;
}
//先序遍历二叉树
void preOrderTraversal(BiTree root)
{
if (root)
{
cout << root->data << " ";
preOrderTraversal(root->lchild);
preOrderTraversal(root->rchild);
}
}
//中序遍历
void inOrderTraversal(BiTree root)
{
if (root)
{
inOrderTraversal(root->lchild);
cout << root->data << " ";
inOrderTraversal(root->rchild);
}
}
//后序遍历
void postOrderTraversal(BiTree root)
{
if (root)
{
postOrderTraversal(root->lchild);
postOrderTraversal(root->rchild);
cout << root->data << " ";
}
}
//主函数
int main()
{
BiTree myTree = createBiTree();
cout << "先序遍历:";
preOrderTraversal(myTree);
cout << endl;
cout << "中序遍历:";
inOrderTraversal(myTree);
cout << endl;
cout << "后序遍历:";
postOrderTraversal(myTree);
cout << endl;
return 0;
}
//需把结构体的元素类型修改为char
// 辅助函数,从先序串创建二叉树
BiTree createTreeFromPreorder(string& preorder, int& index) {
if (index >= preorder.size() || preorder[index] == '#') {
index++;
return nullptr;
}
BiTree root = new BiTNode(preorder[index++]);
root->lchild = createTreeFromPreorder(preorder, index);
root->rchild = createTreeFromPreorder(preorder, index);
return root;
}
任务三:树的递归结构(求二叉树高度)
//先序输出叶节点伪代码:
if(Bt)
if(!btleft&&!btright)
cout<<bt-data;
嵌套递归left&right;
//先序输出叶节点代码:
void PreorderPrintLeaves( BinTree BT ){
if(BT){
if(!BT->Left && !BT->Right){
printf(" %c",BT->Data);
}
PreorderPrintLeaves(BT->Left);
PreorderPrintLeaves(BT->Right);
}
}
//求二叉树高度伪代码:
int m,n;
if(BT=null)
return 0;
else
{
m=GetHight(left);
n=GetHight(right);
if(m>n)return(m+1);
else return(n+1)
}
//求二叉树高度代码:
int GetHeight( BinTree BT)
{
int m,n;
if(BT==NULL)
return 0;
else
{
m=GetHeight(BT->Left);
n=GetHeight(BT->Right);
if (m>n)
return(m+1);
else
return(n+1);
}
}
任务四:二叉排序树
//伪代码:
//主要区别与其他设计二叉树代码的设计为插入、查找和删除部分,故此处其他伪代码略去
TreeNode* insert()//插入
{
if 空结点
创建一个新结点并赋值;
if 值小于结点值
递归至左子树;
else 值大于结点值
递归至右子树;
return 根节点;
}
TreeNode* search()//查找
{
if 空结点或结点值等价
return 结点;
if 值小于结点值
递归至左子树;
else
递归至右子树;
}
TreeNode* remove()//删除
{
if 空结点
return 结点;
if 值小于结点值
递归至左子树;
else if 值大于结点值
递归至右子树;
else 值相等
{
if 左子树空
暂存至右子树;删除右子树;
else if 右子树空
暂存至左子树;删除左子树;
}
找到最小结点,替换删除结点,保持二叉排序树稳定性;
}
//代码:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
// 定义二叉排序树节点结构
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
// 插入节点到二叉排序树
TreeNode* insert(TreeNode* root, int val) {
if (root == nullptr) {
return new TreeNode(val);
}
if (val < root->val) {
root->left = insert(root->left, val);
}
else {
root->right = insert(root->right, val);
}
return root;
}
// 中序遍历二叉排序树
void inorder(TreeNode* root) {
if (root != nullptr) {
inorder(root->left);
cout << root->val << " ";
inorder(root->right);
}
}
// 查找节点
TreeNode* search(TreeNode* root, int val) {
if (root == nullptr || root->val == val) {
return root;
}
if (val < root->val) {
return search(root->left, val);
}
return search(root->right, val);
}
// 查找最小节点
TreeNode* findMin(TreeNode* root) {
while (root->left != nullptr) {
root = root->left;
}
return root;
}
// 删除节点
TreeNode* remove(TreeNode* root, int val) {
if (root == nullptr) {
return root;
}
if (val < root->val) {
root->left = remove(root->left, val);
} else if (val > root->val) {
root->right = remove(root->right, val);
} else {
if (root->left == nullptr) {
TreeNode* temp = root->right;
delete root;
return temp;
} else if (root->right == nullptr) {
TreeNode* temp = root->left;
delete root;
return temp;
}
TreeNode* temp = findMin(root->right);
root->val = temp->val;
root->right = remove(root->right, temp->val);
}
return root;
}
// 释放二叉排序树内存
void freeTree(TreeNode* root) {
if (root != nullptr) {
freeTree(root->left);
freeTree(root->right);
delete root;
}
}
int main() {
string input = "50 30 80 20 40 90 10 25 35 85 23 88 #";
istringstream iss(input);
TreeNode* root = nullptr;
string token;
while (iss >> token) {
if (token == "#") {
break;
}
int num = stoi(token);
root = insert(root, num);
}
// 进行中序遍历
inorder(root);
cout << endl;
// 释放内存
freeTree(root);
return 0;
}
任务五:QQ账户的申请与登录
//伪代码:
int main()
{
定义一个map类型,键和值类型都为string的变量;
for()
{
char 操作指令;string 账号和密码;
cin<<char<<string1<<string2;
if(char==N)
{
if(map中查找存在)cout<<"error:exist";
else qq号键、密码值;cout<<"ok";
}
else(char==L)
{
if(map中查找不存在)cout<<"error:no exist";
else
{
if(键值匹配)cout<<"OK";
else cout<<"error pw"
}
}
}
return 0;
}
//代码:
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main()
{
int n;
cin>>n;
map<string,string>accounts;
for(int i=0;i<n;i++)
{
char command;
string qqNumber,password;
cin>>command>>qqNumber>>password;
if(command=='N')
{
if(accounts.find(qqNumber)!=accounts.end())
{
cout<<"ERROR: Exist"<<endl;
}else
{
accounts[qqNumber]=password;
cout<<"New: OK"<<endl;
}
}
else if(command == 'L')
{
if(accounts.find(qqNumber)==accounts.end())
{
cout<<"ERROR: Not Exist"<<endl;
}
else{
if(accounts[qqNumber]==password)
{
cout<<"Login: OK"<<endl;
}
else cout<<"ERROR: Wrong PW"<<endl;
}
}
}
return 0;
}
三、实验使用环境(本次实验所使用的平台和相关软件)
以下请根据实际情况编写
- 操作系统:Windows 11
- 编程语言:C++
- 开发工具:visual studio2022 &pintia
- 编译器:g++
四、实验步骤和调试过程(实验步骤、测试数据设计、测试结果分析)
任务一、二:创建二叉树、遍历二叉树
本机运行截图:


PINTIA运行截图:


任务三:树的递归结构(先序输出叶结点、求二叉树高度)
PINTIA运行截图:


任务四:创建二叉排序树并中序遍历
本机运行截图:

任务五:QQ账户的申请与登录
PTA运行截图:

五、实验小结(实验中遇到的问题及解决过程、实验体会和收获)
遇到的问题及解决方法:
实验中遇到的最大问题应该是理解树中隐藏的递归性质,这也是编写树结构最重要的一点。在实验过程中经常会转不过弯无法理解怎么递归下去,后通过丢给ai解释理解。
另一个问题是二叉排序树本质上是一个特殊的二叉树,在对二叉排序树进行删除操作后,很容易导致排序树的稳定性出问题,从而失去而二叉排序树本身特质,且该问题不容易被发现。我在测试删除结点函数对照时 发现原来的树并没有排序,后通过询问ai解决方案找到“找最小结点替换”的方法解决该问题
实验体会和收获:
在实验中,我发现树的操作高度依赖递归实现,相较于栈和队列区别在于可查找到并直接修改子节点而非顶或底、头或尾,自己理解了树的递归逻辑,有了一定操作树的能力,具备了基本的树的语法。也理解了二叉树的左右子树特质的时间、空间复杂度优势。
浙公网安备 33010602011771号