实验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提交截图

五、实验小结(实验中遇到的问题及解决过程、实验体会和收获)
遇到的问题及解决方法:
- 问题:在BTS中,不知道如何删节点
- 解决方法:用递归,分情况讨论
- 问题:不知道如何层序遍历
- 解决方法:用queue存放
- 问题:不知道如何处理字符串
- 解决方法:用index进行遍历。
实验体会和收获:
- 掌握创建二叉树与树及二叉树上的基本操作
- 熟练掌握熟练掌握树的递归结构及在其上的递归算法
- 掌握二叉树的层次遍历
- 掌握BST树上的搜索、创建与删除
- 掌握哈希表的应用

浙公网安备 33010602011771号