实验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++

四、实验步骤和调试过程(实验步骤、测试数据设计、测试结果分析)

任务一、二:创建二叉树、遍历二叉树

本机运行截图:

img

img

PINTIA运行截图:

img

img

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

PINTIA运行截图:

img

img

任务四:创建二叉排序树并中序遍历

本机运行截图:

img

任务五:QQ账户的申请与登录

PTA运行截图:

img


五、实验小结(实验中遇到的问题及解决过程、实验体会和收获)

遇到的问题及解决方法:

实验中遇到的最大问题应该是理解树中隐藏的递归性质,这也是编写树结构最重要的一点。在实验过程中经常会转不过弯无法理解怎么递归下去,后通过丢给ai解释理解。

另一个问题是二叉排序树本质上是一个特殊的二叉树,在对二叉排序树进行删除操作后,很容易导致排序树的稳定性出问题,从而失去而二叉排序树本身特质,且该问题不容易被发现。我在测试删除结点函数对照时 发现原来的树并没有排序,后通过询问ai解决方案找到“找最小结点替换”的方法解决该问题

实验体会和收获:

在实验中,我发现树的操作高度依赖递归实现,相较于栈和队列区别在于可查找到并直接修改子节点而非顶或底、头或尾,自己理解了树的递归逻辑,有了一定操作树的能力,具备了基本的树的语法。也理解了二叉树的左右子树特质的时间、空间复杂度优势。


六、附件(参考文献和相关资料)

posted @ 2025-04-24 15:33  KinthYu  阅读(50)  评论(0)    收藏  举报