实验4-树、二叉树与查找
集美大学课程实验报告-实验4:二叉树与查找
| 项目名称 | 内容 |
|---|---|
| 课程名称 | 数据结构 |
| 实验项目名称 | 二叉树与查找 |
| 上机实践日期 | 4.30 |
| 上机实践时间 | 2学时 |
一、目的(本次实验所涉及并要求掌握的知识点)
以下内容请根据实际情况编写
- 掌握的二叉树与查找 基本概念和应用。
- 学习二叉树与查找 的实现与优化。
- 理解二叉树与查找 在实际问题中的应用。
二、实验内容与设计思想
以下请根据实际情况编写
题目1:先序序列创建二叉树
函数相关伪代码
伪代码
// 节点结构
结构体 Node {
字符 data
Node left
Node right
}
// 建树函数
函数 建树(字符串 s, 下标 i):
如果 s[i] == '#' 或 i越界:
i加1
返回 空
创建新节点 node
node.data = s[i]
i加1
node.left = 建树(s, i)
node.right = 建树(s, i)
返回 node
// 中序遍历
函数 中序(node):
如果 node == 空:
返回
中序(node.left)
输出 node.data + " "
中序(node.right)
// 主程序
主函数:
循环 读取字符串 s:
如果 读完了:
退出循环
i = 0
root = 建树(s, i)
中序(root)
输出换行
函数代码
#include <iostream>
#include <string>
using namespace std;
// 二叉树节点结构
typedef struct Node {
char data;
struct Node* left;
struct Node* right;
} Node, *Tree;
// 根据先序遍历字符串构建二叉树
Tree buildTree(string& preorder, int& index) {
if (index >= preorder.length()) {
return nullptr;
}
char ch = preorder[index++];
if (ch == '#') {
return nullptr; // 空节点
}
Node* node = new Node();
node->data = ch;
node->left = buildTree(preorder, index);
node->right = buildTree(preorder, index);
return node;
}
// 中序遍历
void inorderTraversal(Tree root) {
if (root == nullptr) {
return;
}
// 左子树
inorderTraversal(root->left);
// 根节点
cout << root->data << " ";
// 右子树
inorderTraversal(root->right);
}
// 释放二叉树内存
void deleteTree(Tree root) {
if (root == nullptr) {
return;
}
deleteTree(root->left);
deleteTree(root->right);
delete root;
}
int main() {
string preorder;
while (cin >> preorder) {
int index = 0;
Tree root = buildTree(preorder, index);
inorderTraversal(root);
cout << endl;
deleteTree(root);
}
return 0;
}
时间复杂度
O(n)
空间复杂度
O(n)
题目2:先序输出叶结点
函数相关伪代码
先序输出叶结点(节点):
如果 节点 == 空:
返回
如果 节点的左孩子 == 空 且 节点的右孩子 == 空:
输出 " " + 节点的值
先序输出叶结点(节点的左孩子)
先序输出叶结点(节点的右孩子)
函数代码
相关主要代码
void PreorderPrintLeaves(BinTree BT) {
if (BT == NULL) {
return;
}
// 判断是否为叶结点
if (BT->Left == NULL && BT->Right == NULL) {
printf(" %c", BT->Data);
}
// 先序遍历左子树
PreorderPrintLeaves(BT->Left);
// 先序遍历右子树
PreorderPrintLeaves(BT->Right);
}
时间复杂度
O(n)
空间复杂度
最坏O(n)最好O(logn)
题目3:求二叉树高度
函数相关伪代码
伪代码函数 GetHeight(节点):
如果 节点 == 空:
返回 0
左高 = GetHeight(节点.左孩子)
右高 = GetHeight(节点.右孩子)
返回 max(左高, 右高) + 1
函数代码
int GetHeight(BinTree BT) {
if (BT == NULL) {
return 0;
}
int leftHeight = GetHeight(BT->Left);
int rightHeight = GetHeight(BT->Right);
return (leftHeight > rightHeight ? leftHeight : rightHeight) + 1;
}
时间复杂度
O(n)
空间复杂度
最坏O(n)最好O(logn)
题目4:二叉树层次遍历(广度优先)
函数相关伪代码
伪代码函数 建树(字符串 arr, 下标 index):
如果 index 越界 或 arr[index] == '#':
返回 空
创建节点 node 值为 arr[index]
node.left = 建树(arr, 2*index)
node.right = 建树(arr, 2*index+1)
返回 node
函数 层次遍历(root):
如果 root 为空:
输出 "NULL"
返回
创建队列 q
q.push(root)
当 q 不为空:
cur = q.front(), q.pop()
输出 cur.data(如果不是第一个,前面加空格)
如果 cur.left 存在: q.push(cur.left)
如果 cur.right 存在: q.push(cur.right)
主函数:
读入字符串 input
root = 建树(input, 1)
层次遍历(root)
函数代码
相关主要代码#include <iostream>
#include <string>
#include <queue>
using namespace std;
typedef struct Node {
char data;
Node* left;
Node* right;
Node(char d) : data(d), left(nullptr), right(nullptr) {}
} Node;
// 根据顺序存储字符串构建二叉树(下标从1开始)
Node* buildTree(const string& arr, int index) {
if (index >= arr.length() || arr[index] == '#') {
return nullptr;
}
Node* root = new Node(arr[index]);
root->left = buildTree(arr, 2 * index);
root->right = buildTree(arr, 2 * index + 1);
return root;
}
// 层次遍历
void levelOrder(Node* root) {
if (root == nullptr) {
cout << "NULL";
return;
}
queue<Node*> q;
q.push(root);
bool first = true;
while (!q.empty()) {
Node* cur = q.front();
q.pop();
if (!first) {
cout << " ";
}
cout << cur->data;
first = false;
if (cur->left) q.push(cur->left);
if (cur->right) q.push(cur->right);
}
}
int main() {
string input;
cin >> input;
// 输入的字符串第一个字符是 '#' 占位,从下标1开始构建树
Node* root = buildTree(input, 1);
levelOrder(root);
return 0;
}
时间复杂度
O(n)
空间复杂度
O(n)
题目5:创建二叉排序树并遍历
函数相关伪代码
伪代码结构体 Node {
int data
Node left
Node right
}
函数 插入(root, val):
如果 root == 空:
root = 新节点(val)
返回
如果 val < root.data:
插入(root.left, val)
否则如果 val > root.data:
插入(root.right, val)
函数 中序(root):
如果 root == 空:
返回
中序(root.left)
输出 root.data + " "
中序(root.right)
主函数:
root = 空
循环 读入 val:
如果 val == '#':
跳出循环
插入(root, val)
中序(root)
函数代码
相关主要代码
#include <iostream>
using namespace std;
// 二叉树节点结构
typedef struct Node {
int data;
struct Node* left;
struct Node* right;
} Node, *BSTree;
// 创建新节点
Node* createNode(int val) {
Node* newNode = new Node();
newNode->data = val;
newNode->left = nullptr;
newNode->right = nullptr;
return newNode;
}
// 插入节点到二叉排序树
void insert(BSTree& root, int val) {
if (root == nullptr) {
root = createNode(val);
return;
}
if (val < root->data) {
insert(root->left, val);
} else if (val > root->data) {
insert(root->right, val);
}
// 等于的情况,根据题目要求可以不插入(二叉排序树一般不允许重复)
}
// 中序遍历(左 → 根 → 右)
void inorderTraversal(BSTree root) {
if (root == nullptr) {
return;
}
inorderTraversal(root->left);
cout << root->data << " ";
inorderTraversal(root->right);
}
int main() {
BSTree root = nullptr;
int val;
// 读入数据,直到遇到 #
while (cin >> val) {
if (val == -1) { // 如果题目用 -1 表示结束,根据实际情况调整
break;
}
insert(root, val);
}
// 中序遍历输出
inorderTraversal(root);
cout << endl;
return 0;
}
题目7:航空公司VIP客户查询
函数相关伪代码
伪代码// 哈希函数
h = id每个字符的加权累加 % 200003
// 查找
idx = h(id)
遍历 hashTable[idx] 链表:
如果 id 匹配: 返回里程
返回 -1
// 插入
idx = h(id)
遍历链表:
如果 id 匹配: 里程 += miles; 返回
头插法创建新节点
// 主流程
读 N, K
循环 N 次: 读 id, miles; 如果 miles<K 则 miles=K; 插入(id, miles)
读 M
循环 M 次: 读 id; 查找并输出结果或 "No Info"
函数代码
相关主要代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define HASH_SIZE 200003 // 一个大质数,减少冲突
// 哈希链节点
typedef struct Node {
char id[19]; // 身份证号(18位 + '\0')
int mileage; // 累积里程
struct Node* next; // 指向下一个节点
} Node;
// 哈希表
Node* hashTable[HASH_SIZE];
// 哈希函数:简单累加取模
unsigned int hash(const char* id) {
unsigned int h = 0;
for (int i = 0; id[i]; i++) {
h = (h * 31 + id[i]) % HASH_SIZE;
}
return h;
}
// 查找:返回里程,-1 表示未找到
int find(const char* id) {
unsigned int idx = hash(id);
Node* p = hashTable[idx];
while (p) {
if (strcmp(p->id, id) == 0) {
return p->mileage;
}
p = p->next;
}
return -1;
}
// 插入或更新
void insert(const char* id, int miles) {
unsigned int idx = hash(id);
Node* p = hashTable[idx];
// 查找是否已存在
while (p) {
if (strcmp(p->id, id) == 0) {
p->mileage += miles;
return;
}
p = p->next;
}
// 不存在,创建新节点(头插法)
Node* newNode = (Node*)malloc(sizeof(Node));
strcpy(newNode->id, id);
newNode->mileage = miles;
newNode->next = hashTable[idx];
hashTable[idx] = newNode;
}
int main() {
int N, K;
scanf("%d %d", &N, &K);
// 初始化哈希表
for (int i = 0; i < HASH_SIZE; i++) {
hashTable[i] = NULL;
}
// 读入 N 条飞行记录
for (int i = 0; i < N; i++) {
char id[19];
int miles;
scanf("%s %d", id, &miles);
// 低于 K 公里的按 K 计算
if (miles < K) {
miles = K;
}
insert(id, miles);
}
// 读入 M 条查询
int M;
scanf("%d", &M);
for (int i = 0; i < M; i++) {
char queryId[19];
scanf("%s", queryId);
int result = find(queryId);
if (result == -1) {
printf("No Info\n");
}
else {
printf("%d\n", result);
}
}
// 释放内存(非必须,但良好习惯)
for (int i = 0; i < HASH_SIZE; i++) {
Node* p = hashTable[i];
while (p) {
Node* tmp = p;
p = p->next;
free(tmp);
}
}
return 0;
}
平均时间复杂度 O(N + M),空间复杂度 O(N),其中 N 为不同身份证号数量,M 为查询次数
三、实验使用环境(本次实验所使用的平台和相关软件)
以下请根据实际情况编写
- 操作系统:Windows 11
- 编程语言:C++
- 开发工具:Visual Studio 2026
四、实验步骤和调试过程(实验步骤、测试数据设计、测试结果分析)
以下请根据实际情况编写
题目1:先序序列创建二叉树
PTA提交截图

题目2:先序输出叶节点
PTA提交截图
题目3:求树的高度
PTA提交截图

题目4:二叉树层次遍历(广度优先)
PTA提交截图

题目5:创建二叉排序树并遍历
本机运行截图

题目7:航空公司VIP客户查询
PTA运行截图

五、实验小结(实验中遇到的问题及解决过程、实验体会和收获)
以下请根据实际情况编写
遇到的问题及解决方法:
-
问题:哈希函数导致大量冲突,查询速度慢。
- 解决方法:采用大质数 200003 作为哈希表大小,并使用加权哈希(h = h * 31 + id[i]),使身份证号分布更均匀,减少冲突。
-
问题:处理低于 K 公里的里程时逻辑容易遗漏。
- 解决方法:在读入记录后立即判断 if (miles < K) miles = K;,确保里程值正确后再插入哈希表。
实验体会和收获:
- 哈希表应用:
深入理解了链地址法哈希表的实现原理
学会了设计哈希函数(加权累加取模大质数)
掌握了哈希表的插入、查找、删除操作。
六、附件(参考文献和相关资料)
以下请根据实际情况编写
- C++ Primer
- deepseek

浙公网安备 33010602011771号