实验4:树、二叉树与查找

集美大学课程实验报告-实验4:树、二叉树与查找

项目名称 内容
课程名称 数据结构
班级 网安2411
指导教师 郑如滨
学生姓名 林宇钦
学号 202421336008
实验项目名称 树、二叉树与查找
上机实践日期 2025.4.20
上机实践时间 2学时

一、目的(本次实验所涉及并要求掌握的知识点)

  • 掌握创建二叉树与树及二叉树上的基本操作
  • 熟练掌握熟练掌握树的递归结构及在其上的递归算法
  • 掌握二叉树的层次遍历
  • 掌握BST树上的搜索、创建与删除
  • 掌握哈希表的应用

二、实验内容与设计思想

题目1:先序序列创建二叉树

题目
编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以二叉链表存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,代表一棵空树。然后再对二叉树进行中序遍历,输出遍历结果。
函数相关伪代码

// 定义二叉树节点结构
Struct Node {
    char data;
    Node left;
    Node right;
}

// 中序遍历二叉树
Function InorderTraversal(root):
    If root 为空:
        返回
    递归调用 InorderTraversal(root.left)
    输出 root.data
    递归调用 InorderTraversal(root.right)

// 根据先序遍历字符串构建二叉树
Function ConstructTree(str, index):
    If str[index] 是结束符 或者 str[index] 是 '#' :
        index 加 1
        返回 空节点
    分配新节点 root
    root.data = str[index]
    index 加 1
    root.left = ConstructTree(str, index)
    root.right = ConstructTree(str, index)
    返回 root

// 主函数
Function Main():
    初始化 index 为 0
    定义字符串 str
    While 成功读取字符串 str:
        index 重置为 0
        root = ConstructTree(str, index)
        调用 InorderTraversal(root)
        输出换行符

函数代码

#include <bits/stdc++.h>
using namespace std;

// 定义二叉树节点结构体
struct T{
    char a; // 节点存储的字符数据
    struct T *lt; // 指向左子节点的指针
    struct T *rt; // 指向右子节点的指针
};

// 中序遍历二叉树的函数
void inorder(T*root)
{
    // 如果当前节点为空,直接返回
    if(root==NULL) return;
    // 递归遍历左子树
    inorder(root->lt);
    // 输出当前节点存储的字符
    printf("%c ",root->a);
    // 递归遍历右子树
    inorder(root->rt);
}

// 根据先序遍历字符串构建二叉树的函数
T* ctree(char *str, int *i)
{
    // 如果当前字符是字符串结束符或者是代表空节点的 '#'
    if(str[*i]=='\0' || str[*i]=='#')
    {
        // 索引加 1
        (*i)++;
        // 返回空指针,表示该位置为空节点
        return NULL;
    }
    // 为新节点分配内存
    T* root = (T*)malloc(sizeof(T));
    // 将当前字符赋值给新节点的数据域
    root->a = str[(*i)++];
    // 递归构建左子树
    root->lt = ctree(str, i);
    // 递归构建右子树
    root->rt = ctree(str, i);
    // 返回构建好的当前节点
    return root;
}

int main()
{
    // 初始化索引为 0
    int i = 0;
    // 定义存储先序遍历字符串的字符数组
    char str[110];
    // 循环读取输入的字符串,直到文件结束
    while(scanf("%s", str) != EOF)
    {
        // 每次读取新字符串前,将索引重置为 0
        i = 0;
        // 调用 ctree 函数,根据当前字符串构建二叉树
        T* root = ctree(str, &i);
        // 对构建好的二叉树进行中序遍历并输出结果
        inorder(root);
        // 输出换行符
        printf("\n");
    }
    return 0;
}

时间复杂度
对于单个输入的字符串,构建二叉树和中序遍历的时间复杂度为O(n),这里的 n 是字符串的长度。若有k个输入的字符串,总的时间复杂度为O(k*n)。
空间复杂度
最坏情况:当二叉树退化为链表时,整个程序的空间复杂度由递归调用栈的深度决定,为O(n),这里的n是字符串的长度(也是二叉树的节点数)。平均情况:对于平衡二叉树,空间复杂度为O(log n),因为递归调用栈的深度与树的高度成正比。

题目2:先序输出叶结点

题目
本题要求按照先序遍历的顺序输出给定二叉树的叶结点。
函数相关伪代码

// 定义二叉树节点结构
Struct Node {
    data  // 节点存储的数据
    left  // 指向左子节点的指针
    right // 指向右子节点的指针
}

// 先序遍历打印叶子节点的函数
Function PreorderPrintLeaves(root):
    If root 不为空:
        If root 的左子节点为空 并且 root 的右子节点为空:
            输出 root.data
        递归调用 PreorderPrintLeaves(root.left)
        递归调用 PreorderPrintLeaves(root.right)

函数代码

void PreorderPrintLeaves( BinTree BT ){
    if(BT)
    {
        if(!BT->Left&&!BT->Right){
            printf(" %c",BT->Data);
        }
        PreorderPrintLeaves(BT->Left);
        PreorderPrintLeaves(BT->Right);
    }
}

时间复杂度
此函数的主要功能是先序遍历二叉树并打印叶子节点。先序遍历的过程是:先访问根节点,接着递归遍历左子树,最后递归遍历右子树。在遍历过程中,每个节点都会被访问一次,并且对每个节点执行的操作(判断是否为叶子节点以及可能的打印操作)都是常数时间的操作。函数 PreorderPrintLeaves 的时间复杂度为O(n),其中 n 是二叉树的节点数量。
空间复杂度
最坏情况:当二叉树退化为链表时(即每个节点只有一个子节点),二叉树的高度h等于节点数n。此时,递归调用会一直深入到链表的末尾,递归调用栈的深度达到最大,为 n。每一层递归调用都会在栈上占用一定的空间,由于有 n 层递归,所以最坏情况下的空间复杂度为O(n)。平均情况:对于一棵平衡二叉树,树的高度h与节点数n之间满足h=O(logn)的关系。这是因为平衡二叉树的节点分布相对均匀,每一层的节点数大致呈指数增长。在这种情况下,递归调用栈的最大深度就是树的高度,所以平均情况下的空间复杂度为O(log n)。

题目3:求二叉树高度

题目
本题要求给定二叉树的高度。
函数相关伪代码

// 定义二叉树节点结构
Struct Node {
    data  // 节点存储的数据
    left  // 指向左子节点的指针
    right // 指向右子节点的指针
}

// 计算二叉树高度的函数
Function GetHeight(root):
    If root 为空:
        返回 0
    leftHeight = 递归调用 GetHeight(root.left)
    rightHeight = 递归调用 GetHeight(root.right)
    If leftHeight > rightHeight:
        返回 leftHeight + 1
    否则:
        返回 rightHeight + 1

函数代码

// 函数用于计算二叉树的高度
int GetHeight( BinTree BT ) {
    // 如果当前二叉树节点为空,说明是空树,高度为 0,直接返回 0
    if (BT == NULL) {
        return 0;
    }
    // 递归调用 GetHeight 函数计算左子树的高度
    int leftHeight = GetHeight(BT->Left);
    // 递归调用 GetHeight 函数计算右子树的高度
    int rightHeight = GetHeight(BT->Right);
    // 比较左子树和右子树的高度
    if (leftHeight > rightHeight) {
        // 如果左子树高度大于右子树高度,那么当前树的高度为左子树高度加 1(加上当前节点这一层)
        return leftHeight + 1;
    } else {
        // 如果右子树高度大于等于左子树高度,那么当前树的高度为右子树高度加 1(加上当前节点这一层)
        return rightHeight + 1;
    }
}

时间复杂度
由于函数需要遍历二叉树的所有 n 个节点,且对每个节点的操作时间是常数级别的,记为O(1)所以总的时间复杂度就是对这 n 个节点的操作时间之和。GetHeight 函数的时间复杂度为O(n)。

空间复杂度
最坏情况:二叉树退化为链表,空间复杂度为O(n)。平均情况:二叉树平衡,空间复杂度为O(log n)。

题目4:二叉树层次遍历(广度优先)

题目
层次遍历树中所有节点。
输入一行字符串表示二叉树的顺序存储结构,比如字符串“#ABCD#EF#G##H##I”,#代表空节点。第一个#不使用
函数相关伪代码

// 定义全局变量
全局变量 index 初始化为 2
全局变量 in 初始化为 0
全局变量 i 初始化为 0

// 定义二叉树节点结构
Struct TreeNode {
    val  // 节点存储的字符值
    left  // 指向左子节点的指针
    right  // 指向右子节点的指针
    构造函数 TreeNode(x):
        val = x
        left = 空指针
        right = 空指针
}

// 定义全局队列
全局队列 q 用于存储 TreeNode 指针

// 根据顺序存储字符串构建二叉树的函数
Function buildTree(s):
    If s 的长度为 1 或者 s 的第二个字符是 '#' :
        返回 空指针
    创建新节点 root,其值为 s 的第二个字符
    将 root 加入队列 q
    While index 小于 s 的长度:
        current = 队列 q 的队首元素
        If index 小于 s 的长度 并且 s[index] 不是 '#' :
            创建新节点作为 current 的左子节点,其值为 s[index]
            将新节点加入队列 q
            in 加 1
        index 加 1
        If index 小于 s 的长度 并且 s[index] 不是 '#' :
            创建新节点作为 current 的右子节点,其值为 s[index]
            将新节点加入队列 q
            in 加 1
        index 加 1
    返回 root

// 主函数
Function Main():
    定义字符串 input
    读取输入到 input
    root = 调用 buildTree(input)
    If root 为空:
        输出 "NULL"
        返回 0
    While 队列 q 不为空:
        current = 队列 q 的队首元素
        从队列 q 中移除队首元素
        输出 current 的值
        i 加 1
        If i 小于等于 in:
            输出一个空格
    返回 0

函数代码

#include <iostream>
#include <string>
#include <queue>
using namespace std;

// 全局变量 index 用于记录构建树时字符串的索引,初始化为 2
int index = 2;
// 全局变量 in 用于记录树中非空节点的数量,初始化为 0
int in = 0;
// 全局变量 i 用于控制输出时节点间空格的打印,初始化为 0
int i = 0;

// 定义二叉树节点的结构体
struct TreeNode {
    // 节点存储的字符值
    char val;
    // 指向左子节点的指针
    TreeNode* left;
    // 指向右子节点的指针
    TreeNode* right;
    // 构造函数,用于初始化节点
    // 参数 x 为节点要存储的字符值
    TreeNode(char x) : val(x), left(nullptr), right(nullptr) {}
};

// 全局队列 q,用于在构建树时按层序处理节点
queue<TreeNode*> q;

// 根据顺序存储的字符串构建二叉树的函数
TreeNode* buildTree(const string& s) {
    // 如果字符串长度为 1 或者第二个字符为 '#',表示没有有效的树节点,返回空指针
    if (s.size() == 1 || s[1] == '#') {
        return NULL;
    }
    // 创建根节点,其值为字符串的第二个字符
    TreeNode* root = new TreeNode(s[1]);
    // 将根节点加入队列,为后续构建左右子树做准备
    q.push(root);
    // 当 index 小于字符串长度时,继续构建树
    while (index < s.size()) {
        // 取出队列的队首节点作为当前要处理的节点
        TreeNode* current = q.front();
        // 如果当前 index 对应的字符不是 '#',表示存在左子节点
        if (index < s.size() && s[index] != '#') {
            // 创建左子节点,其值为当前 index 对应的字符
            current->left = new TreeNode(s[index]);
            // 将左子节点加入队列,后续处理其左右子节点
            q.push(current->left);
            // 非空节点数量加 1
            in++;
        }
        // index 向后移动一位
        index++;
        // 如果新的 index 对应的字符不是 '#',表示存在右子节点
        if (index < s.size() && s[index] != '#') {
            // 创建右子节点,其值为新 index 对应的字符
            current->right = new TreeNode(s[index]);
            // 将右子节点加入队列,后续处理其左右子节点
            q.push(current->right);
            // 非空节点数量加 1
            in++;
        }
        // index 再向后移动一位
        index++;
        // 队首节点处理完毕,将其从队列中移除
        q.pop();
    }
    // 返回构建好的二叉树的根节点
    return root;
}

int main() {
    // 定义一个字符串变量 input,用于存储用户输入的顺序存储字符串
    string input;
    // 从标准输入读取字符串到 input
    cin >> input;
    // 调用 buildTree 函数,根据输入的字符串构建二叉树,并得到根节点
    TreeNode* root = buildTree(input);
    // 如果根节点为空,说明没有构建出有效的树,输出 "NULL" 并结束程序
    if (!root) {
        cout << "NULL";
        return 0;
    }
    // 当队列不为空时,循环输出队列中的节点值
    while (!q.empty()) {
        // 取出队列的队首节点
        TreeNode* current = q.front();
        // 将队首节点从队列中移除
        q.pop();
        // 输出当前节点的值
        cout << current->val;
        // 输出节点计数加 1
        i++;
        // 如果还没输出完所有非空节点,输出一个空格作为分隔
        if (i <= in) {
            cout << " ";
        }
    }
    return 0;
}

时间复杂度
队列中的每个节点都会被访问一次,并且每次访问的操作(如出队、输出节点值)都是常数时间的。队列中的节点数量最多为n。由于buildTree函数和main函数中的输出部分的时间复杂度都是O(n),所以整个程序的时间复杂度为O(n)。
空间复杂度
二叉树节点的数量取决于输入字符串中有效节点(非 #)的数量,最多为 n 个。每个节点都需要分配一定的内存空间,因此存储二叉树节点所需的空间复杂度为O(n)。

题目5: 创建二叉排序树并遍历

函数相关伪代码

# 定义二叉树节点结构
Struct TreeNode:
    value: 节点存储的值
    left: 指向左子节点的指针
    right: 指向右子节点的指针

# 插入节点到二叉排序树的函数
Function Insert(root, value):
    If root 为空:
        创建新节点 newNode,值为 value
        返回 newNode
    If value < root 的值:
        root 的左子节点 = Insert(root 的左子节点, value)
    Else:
        root 的右子节点 = Insert(root 的右子节点, value)
    返回 root

# 中序遍历二叉树的函数
Function InorderTraversal(root, resultList):
    If root 不为空:
        InorderTraversal(root 的左子节点, resultList)
        将 root 的值添加到 resultList
        InorderTraversal(root 的右子节点, resultList)

# 主函数
Function Main():
    # 给定的值列表
    values = [50, 30, 80, 20, 40, 90, 10, 25, 35, 85, 23, 88]
    # 初始化根节点为空
    root = 空指针
    # 遍历值列表,依次插入节点构建二叉排序树
    For each value in values:
        root = Insert(root, value)
    # 用于存储中序遍历结果的列表
    resultList = 空列表
    # 进行中序遍历
    InorderTraversal(root, resultList)
    # 输出中序遍历结果
    输出 "中序遍历结果: "
    For each value in resultList:
        输出 value 和一个空格
    输出换行符

函数代码

#include <iostream>
#include <vector>
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 inorderTraversal(TreeNode* root, vector<int>& result) {
    if (root != nullptr) {
        inorderTraversal(root->left, result);
        result.push_back(root->val);
        inorderTraversal(root->right, result);
    }
}

int main() {
    vector<int> values = {50, 30, 80, 20, 40, 90, 10, 25, 35, 85, 23, 88};
    TreeNode* root = nullptr;
    for (int val : values) {
        root = insert(root, val);
    }
    vector<int> inorderResult;
    inorderTraversal(root, inorderResult);
    cout << "中序遍历结果: ";
    for (int val : inorderResult) {
        cout << val << " ";
    }
    cout << endl;

    return 0;
}    

时间复杂度
平均情况:对于随机插入的节点,二叉排序树大致是平衡的,树的高度近似为O(log n),其中 n 是树中节点的数量。因此,插入一个节点的平均时间复杂度为 O(log n)。要插入 n 个节点,总的时间复杂度就是 n 次插入操作的时间复杂度之和,即 O(nlog n))。最坏情况:如果插入的节点是有序的(例如升序或降序排列),二叉排序树会退化为链表,树的高度变为 n。此时插入一个节点的时间复杂度为O(n),插入n个节点的总时间复杂度就是O(n^2)。
空间复杂度
平均情况:树的高度近似为O(log n),因此递归调用栈的空间复杂度为O(log n)。最坏情况:树退化为链表,树的高度为n递归调用栈的空间复杂度为O(n)。

题目6:二叉搜索树的操作集

题目
BinTree Insert( BinTree BST, ElementType X );
BinTree Delete( BinTree BST, ElementType X );
Position Find( BinTree BST, ElementType X );
Position FindMin( BinTree BST );
Position FindMax( BinTree BST );
函数相关伪代码

# 定义二叉搜索树节点结构
Struct BinTree {
    Data  # 节点存储的数据
    Left  # 指向左子节点的指针
    Right # 指向右子节点的指针
}

# 插入节点到二叉搜索树的函数
Function Insert(BST, X):
    If BST 为空:
        创建新节点 cur
        cur 的左子节点为空
        cur 的右子节点为空
        cur 的数据为 X
        返回 cur
    Else If X 大于 BST 的数据:
        BST 的右子树 = 递归调用 Insert(BST 的右子树, X)
    Else If X 小于 BST 的数据:
        BST 的左子树 = 递归调用 Insert(BST 的左子树, X)
    返回 BST

# 查找二叉搜索树中最大值节点的函数
Function FindMax(BST):
    If BST 为空:
        返回 BST
    Else:
        While BST 的右子节点不为空:
            BST = BST 的右子节点
        返回 BST

# 查找二叉搜索树中最小值节点的函数
Function FindMin(BST):
    If BST 为空:
        返回 BST
    Else:
        While BST 的左子节点不为空:
            BST = BST 的左子节点
        返回 BST

# 在二叉搜索树中查找值为 X 的节点的函数
Function Find(BST, X):
    If BST 为空:
        返回 空指针
    Else If BST 的数据小于 X:
        返回 递归调用 Find(BST 的右子树, X)
    Else If BST 的数据大于 X:
        返回 递归调用 Find(BST 的左子树, X)
    Else If BST 的数据等于 X:
        返回 BST
    返回 BST

# 从二叉搜索树中删除值为 X 的节点的函数
Function Delete(BST, X):
    If BST 为空:
        输出 "Not Found"
        返回 空指针
    If BST 的数据等于 X:
        If BST 的左子节点为空 且 BST 的右子节点为空:
            返回 空指针
        Else If BST 的左子节点为空 且 BST 的右子节点不为空:
            BST = BST 的右子节点
        Else If BST 的左子节点不为空 且 BST 的右子节点为空:
            BST = BST 的左子节点
        Else If BST 的左子节点不为空 且 BST 的右子节点不为空:
            BST 的数据 = 调用 FindMin(BST 的右子树) 的数据
            BST 的右子树 = 递归调用 Delete(BST 的右子树, BST 的数据)
    Else If X 小于 BST 的数据:
        BST 的左子树 = 递归调用 Delete(BST 的左子树, X)
    Else If X 大于 BST 的数据:
        BST 的右子树 = 递归调用 Delete(BST 的右子树, X)
    返回 BST

函数代码

BinTree Insert(BinTree BST,ElementType X)
{
    if(BST==NULL)
    {
        BinTree cur=(BinTree)malloc(sizeof(struct TNode));
        cur->Left=NULL;
        cur->Right=NULL;
        cur->Data=X;
        return cur;
    }
    else if(X>BST->Data)
    {
        BST->Right=Insert(BST->Right,X);
    }
    else if(X<BST->Data)
    {
        BST->Left=Insert(BST->Left,X);
    }
    return BST;
}
Position FindMax(BinTree BST)
{
    if(BST==NULL){return BST;}
    else{
        while(BST->Right)
        {
            BST=BST->Right;
        }
        return BST;
    }
}
Position FindMin(BinTree BST)
{
    if(BST==NULL){return BST;}
    else{
        while(BST->Left)
        {
            BST=BST->Left;
        }
        return BST;
    }
}
Position Find(BinTree BST,ElementType X)
{
    if(BST==NULL){return NULL;}
    else if(BST->Data<X){
        return Find(BST->Right,X);
    }
    else if(BST->Data>X){
        return Find(BST->Left,X);
    }
     else if(BST->Data==X){
        return BST;
    }return BST;
}
BinTree Delete( BinTree BST, ElementType X ){ 
    if(BST==NULL)
    {
         printf("Not Found\n");
        return NULL;
    }
    if(BST->Data==X)
    {
        if(BST->Left==NULL&&BST->Right==NULL)
        {
            return NULL;
        }
        else if(BST->Left==NULL&&BST->Right!=NULL)
        {
            BST=BST->Right;
        }
        else if(BST->Left!=NULL&&BST->Right==NULL)
        {
           BST=BST->Left;
        }
        else if(BST->Left!=NULL&&BST->Right!=NULL)
        {
           BST->Data=FindMin(BST->Right)->Data;
            BST->Right=Delete(BST->Right,BST->Data);
        
        }
    }
    else if(X<BST->Data)
    {
        BST->Left=Delete(BST->Left,X);
    }
    else if(X>BST->Data)
    {
        BST->Right=Delete(BST->Right,X);
    }
    return BST;
 
}

时间复杂度
Insert、FindMax、FindMin、Find 和 Delete 这几个函数在最坏情况下的时间复杂度均为O(n),在平均情况下的时间复杂度均为O(log n)。
空间复杂度
迭代实现的 FindMax 和 FindMin 函数空间复杂度为常数级,而递归实现的 Insert、Find 和 Delete 函数在最坏情况下空间复杂度为 (O(n)),平均情况下为 (O(\log n))

题目7:哈希表的应用:QQ账户查询

题目
1)若新申请帐户成功,则输出“New: OK”;
2)若新申请的号码已经存在,则输出“ERROR: Exist”;
3)若老帐户登陆成功,则输出“Login: OK”;
4)若老帐户QQ号码不存在,则输出“ERROR: Not Exist”;
5)若老帐户密码错误,则输出“ERROR: Wrong PW”。
函数相关伪代码

// 主函数
Function Main():
    // 定义一个键值对类型的映射(字典)mp,键和值都是字符串类型
    定义映射 mp,键类型为字符串,值类型为字符串
    // 定义三个字符串变量 s1, s2, s3 用于存储输入数据
    定义字符串 s1, s2, s3
    // 定义整数变量 n 用于存储输入的操作次数
    定义整数 n
    // 从标准输入读取 n 的值
    从标准输入读取 n
    // 循环 n 次,处理每一次的操作
    For i 从 1 到 n:
        // 从标准输入读取三个字符串,分别存储到 s1, s2, s3
        从标准输入读取 s1, s2, s3
        // 如果 s1 的值为 "N",表示进行新建操作
        If s1 等于 "N":
            // 在映射 mp 中查找键为 s2 的元素
            it = 在 mp 中查找 s2
            // 如果未找到对应的键
            If it 等于 mp 的结束迭代器:
                // 输出 "New: OK"
                输出 "New: OK"
                // 将键值对 (s2, s3) 插入到映射 mp 中
                mp[s2] = s3
            // 如果找到了对应的键
            Else:
                // 输出 "ERROR: Exist"
                输出 "ERROR: Exist"
        // 如果 s1 的值为 "L",表示进行登录操作
        Else If s1 等于 "L":
            // 在映射 mp 中查找键为 s2 的元素
            it = 在 mp 中查找 s2
            // 如果未找到对应的键
            If it 等于 mp 的结束迭代器:
                // 输出 "ERROR: Not Exist"
                输出 "ERROR: Not Exist"
            // 如果找到了对应的键
            Else:
                // 如果键 s2 对应的值与 s3 相等
                If it 对应的值 等于 s3:
                    // 输出 "Login: OK"
                    输出 "Login: OK"
                // 如果键 s2 对应的值与 s3 不相等
                Else:
                    // 输出 "ERROR: Wrong PW"
                    输出 "ERROR: Wrong PW"
    // 函数结束
    返回

函数代码

#include <bits/stdc++.h>
using namespace std;

int main()
{
    // 定义一个 map 容器 mp,用于存储用户名和对应的密码
    // 键为用户名(string 类型),值为密码(string 类型)
    map<string, string> mp;

    // 定义三个字符串变量 s1、s2 和 s3
    // s1 用于存储操作类型("N" 表示新建用户,"L" 表示用户登录)
    // s2 用于存储用户名
    // s3 用于存储密码
    string s1, s2, s3;

    // 定义一个整数变量 n,用于存储接下来要进行的操作次数
    int n;

    // 从标准输入读取操作次数 n
    cin >> n;

    // 循环 n 次,对每次输入的操作进行处理
    for (int i = 1; i <= n; i++)
    {
        // 从标准输入读取操作类型、用户名和密码,分别存储到 s1、s2 和 s3
        cin >> s1 >> s2 >> s3;

        // 当操作类型为新建用户(s1 为 "N")时
        if (s1 == "N")
        {
            // 在 map 容器 mp 中查找是否存在该用户名
            auto it = mp.find(s2);

            // 如果未找到该用户名,说明可以进行新建操作
            if (it == mp.end())
            {
                // 输出新建用户成功的提示信息
                cout << "New: OK" << endl;
                // 将新的用户名和密码添加到 map 容器 mp 中
                mp[s2] = s3;
            }
            // 如果找到了该用户名,说明用户名已存在
            else
            {
                // 输出用户名已存在的错误提示信息
                cout << "ERROR: Exist" << endl;
            }
        }

        // 当操作类型为用户登录(s1 为 "L")时
        if (s1 == "L")
        {
            // 在 map 容器 mp 中查找是否存在该用户名
            auto it = mp.find(s2);

            // 如果未找到该用户名,说明用户不存在
            if (it == mp.end())
            {
                // 输出用户不存在的错误提示信息
                cout << "ERROR: Not Exist" << endl;
            }
            // 如果找到了该用户名
            else
            {
                // 检查输入的密码是否与存储的密码一致
                if (it->second == s3)
                {
                    // 密码一致,输出登录成功的提示信息
                    cout << "Login: OK" << endl;
                }
                // 密码不一致
                else
                {
                    // 输出密码错误的提示信息
                    cout << "ERROR: Wrong PW" << endl;
                }
            }
        }
    }
    return 0;
}

时间复杂度
由于整个程序循环执行了 n 次,每次循环中的注册或登录操作的时间复杂度为 O(log m),其中 m 是当前 map 中元素的数量。在最坏情况下,m 可以达到 n(所有操作都是注册操作)。因此,整个程序的时间复杂度为O(nlog n)。
空间复杂度
这段代码的空间复杂度为O(nk),其中 n 是操作的总次数,k 是字符串(用户名和密码)的平均长度。


三、实验使用环境(本次实验所使用的平台和相关软件)

以下请根据实际情况编写

  • 操作系统:Windows 10
  • 编程语言:C++
  • 开发工具Visual Studio

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

题目1:先序序列创建二叉树

PTA提交截图

题目2:先序输出叶结点

PTA提交截图

题目3:求二叉树高度

PTA提交截图

题目4:二叉树层次遍历(广度优先)

PTA提交截图

题目5:创建二叉排序树并遍历

本机运行截图

题目6:BST(二叉排序树)的查找、插入与建树与删除

PTA提交截图

题目7:哈希表的应用:QQ账户查询

PTA提交截图


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

遇到的问题及解决方法:

  1. 问题:在BTS中,不知道如何删节点
  • 解决方法:用递归,分情况讨论
  1. 问题:不知道如何层序遍历
  • 解决方法:用queue存放
  1. 问题:不知道如何处理字符串
  • 解决方法:用index进行遍历。

实验体会和收获:

  • 掌握创建二叉树与树及二叉树上的基本操作
  • 熟练掌握熟练掌握树的递归结构及在其上的递归算法
  • 掌握二叉树的层次遍历
  • 掌握BST树上的搜索、创建与删除
  • 掌握哈希表的应用

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

  1. C++ Primer
posted @ 2025-04-24 20:44  林宇钦  阅读(62)  评论(0)    收藏  举报