457 lines
13 KiB
TeX
457 lines
13 KiB
TeX
|
\documentclass[全部作业]{subfiles}
|
|||
|
\input{mysubpreamble}
|
|||
|
|
|||
|
\begin{document}
|
|||
|
\setcounter{chapter}{10}
|
|||
|
\chapter{第十一次作业}
|
|||
|
|
|||
|
\setlist[2]{label=\textbf{(\arabic{enumii})},listparindent=\parindent}
|
|||
|
|
|||
|
\begin{enumerate}
|
|||
|
\questionandanswer[]{
|
|||
|
(计算题)
|
|||
|
根据以下给出三个基本表Student、Course、SC,其中Student(学生表)的字段为学号、姓名、性别、年龄、所属院系;
|
|||
|
|
|||
|
Course(课程表)的字段按顺序为课程编号、课程名、先行课程、课程学分;
|
|||
|
|
|||
|
SC(选课表)的字段按顺序为学号、课程号、成绩。
|
|||
|
|
|||
|
各表的记录如表1.1、表1.2、表1.3所示:
|
|||
|
|
|||
|
\noindent\includegraphics[width=1\linewidth]{imgs/2024-12-21-16-59-15.png}
|
|||
|
|
|||
|
首先,使用C语言或Python语言模拟数据结构和函数:
|
|||
|
}{}
|
|||
|
\begin{enumerate}
|
|||
|
\questionandanswer[]{
|
|||
|
假设我们为SC表中的Sno(学号)字段创建了顺序索引,要求使用顺序查找方法从表中查找某个学生(如学号为 95001)的所有选课记录。
|
|||
|
|
|||
|
|
|||
|
|
|||
|
编写一个函数,该函数接受一个学号(Sno),并输出该学号对应的所有选课记录。
|
|||
|
|
|||
|
要求:没有使用哈希表或其他高级索引,直接通过顺序扫描来实现查询。
|
|||
|
}{}
|
|||
|
{\kaishu
|
|||
|
\begin{minted}{python}
|
|||
|
def question_1_1(sno):
|
|||
|
return [(sno, cno, grade) for (sno, cno, grade) in sc]
|
|||
|
\end{minted}
|
|||
|
}
|
|||
|
\questionandanswer[]{
|
|||
|
假设我们为Student表中的Sno(学号)字段创建了一个 B+ 树索引。请编写一个简单的 B+ 树实现,并使用它来查找某个学号对应的学生信息。
|
|||
|
|
|||
|
|
|||
|
|
|||
|
定义一个 B+ 树类,支持插入(Insert)和查找(Search)操作。每个节点最多有 3 个键,最少有 2 个键(阶数为 3)。
|
|||
|
|
|||
|
实现查找学号为 95002 的学生信息。
|
|||
|
}{}
|
|||
|
{\kaishu
|
|||
|
由于B+树非常复杂,所以直接使用大模型生成代码了。(题目中说“使用C语言或Python语言”,实现B+树这种底层的数据结构这里就用C了,而题目中又说“定义一个B+树类”,C语言中没有类的概念,这里就只能模拟一个类了。)
|
|||
|
|
|||
|
文件 bplus_tree.h
|
|||
|
\begin{minted}{C}
|
|||
|
#ifndef BPLUS_TREE_H
|
|||
|
#define BPLUS_TREE_H
|
|||
|
|
|||
|
#include <stdio.h>
|
|||
|
#include <stdlib.h>
|
|||
|
|
|||
|
#define ORDER 3
|
|||
|
|
|||
|
typedef struct Node {
|
|||
|
int num_keys;
|
|||
|
int keys[ORDER - 1];
|
|||
|
struct Node *children[ORDER];
|
|||
|
struct Node *parent;
|
|||
|
void *values[ORDER - 1]; // Assuming values are pointers to some data
|
|||
|
struct Node *next; // Pointer to next leaf node
|
|||
|
} Node;
|
|||
|
|
|||
|
typedef struct {
|
|||
|
Node *root;
|
|||
|
Node *first_leaf;
|
|||
|
} BPlusTree;
|
|||
|
|
|||
|
// Function declarations
|
|||
|
BPlusTree* create_bplus_tree();
|
|||
|
Node* create_node(int is_leaf);
|
|||
|
void insert(BPlusTree *tree, int key, void *value);
|
|||
|
Node* search(BPlusTree *tree, int key);
|
|||
|
void split_node(Node *node);
|
|||
|
void insert_into_parent(Node *left, int key, Node *right);
|
|||
|
void print_tree(Node *root);
|
|||
|
|
|||
|
#endif
|
|||
|
\end{minted}
|
|||
|
|
|||
|
\vspace{5em}
|
|||
|
文件 bplus_tree.c
|
|||
|
\begin{minted}{C}
|
|||
|
#include "bplus_tree.h"
|
|||
|
|
|||
|
BPlusTree* create_bplus_tree() {
|
|||
|
BPlusTree *tree = (BPlusTree*)malloc(sizeof(BPlusTree));
|
|||
|
tree->root = NULL;
|
|||
|
tree->first_leaf = NULL;
|
|||
|
return tree;
|
|||
|
}
|
|||
|
|
|||
|
Node* create_node(int is_leaf) {
|
|||
|
Node *node = (Node*)malloc(sizeof(Node));
|
|||
|
node->num_keys = 0;
|
|||
|
for (int i = 0; i < ORDER; i++) {
|
|||
|
node->children[i] = NULL;
|
|||
|
}
|
|||
|
node->parent = NULL;
|
|||
|
node->next = NULL;
|
|||
|
if (is_leaf) {
|
|||
|
node->next = NULL;
|
|||
|
}
|
|||
|
return node;
|
|||
|
}
|
|||
|
|
|||
|
void insert(BPlusTree *tree, int key, void *value) {
|
|||
|
if (tree->root == NULL) {
|
|||
|
tree->root = create_node(1);
|
|||
|
tree->root->keys[0] = key;
|
|||
|
tree->root->values[0] = value;
|
|||
|
tree->root->num_keys = 1;
|
|||
|
tree->first_leaf = tree->root;
|
|||
|
} else {
|
|||
|
Node *leaf = find_leaf(tree->root, key);
|
|||
|
if (leaf->num_keys < ORDER - 1) {
|
|||
|
int i = leaf->num_keys - 1;
|
|||
|
while (i >= 0 && leaf->keys[i] > key) {
|
|||
|
leaf->keys[i + 1] = leaf->keys[i];
|
|||
|
leaf->values[i + 1] = leaf->values[i];
|
|||
|
i--;
|
|||
|
}
|
|||
|
leaf->keys[i + 1] = key;
|
|||
|
leaf->values[i + 1] = value;
|
|||
|
leaf->num_keys++;
|
|||
|
} else {
|
|||
|
Node *new_leaf = create_node(1);
|
|||
|
new_leaf->keys[0] = key;
|
|||
|
new_leaf->values[0] = value;
|
|||
|
new_leaf->num_keys = 1;
|
|||
|
|
|||
|
int virtual_keys[ORDER];
|
|||
|
void *virtual_values[ORDER];
|
|||
|
Node *virtual_children[ORDER];
|
|||
|
|
|||
|
int i = 0;
|
|||
|
while (i < ORDER - 1) {
|
|||
|
virtual_keys[i] = leaf->keys[i];
|
|||
|
virtual_values[i] = leaf->values[i];
|
|||
|
i++;
|
|||
|
}
|
|||
|
virtual_keys[i] = key;
|
|||
|
virtual_values[i] = value;
|
|||
|
|
|||
|
int split_point = ORDER / 2;
|
|||
|
|
|||
|
leaf->num_keys = 0;
|
|||
|
i = 0;
|
|||
|
while (i < split_point) {
|
|||
|
leaf->keys[i] = virtual_keys[i];
|
|||
|
leaf->values[i] = virtual_values[i];
|
|||
|
i++;
|
|||
|
leaf->num_keys++;
|
|||
|
}
|
|||
|
|
|||
|
i = split_point;
|
|||
|
int j = 0;
|
|||
|
while (i < ORDER) {
|
|||
|
new_leaf->keys[j] = virtual_keys[i];
|
|||
|
new_leaf->values[j] = virtual_values[i];
|
|||
|
i++;
|
|||
|
j++;
|
|||
|
new_leaf->num_keys++;
|
|||
|
}
|
|||
|
|
|||
|
new_leaf->next = leaf->next;
|
|||
|
leaf->next = new_leaf;
|
|||
|
|
|||
|
for (i = leaf->num_keys; i < ORDER - 1; i++) {
|
|||
|
leaf->keys[i] = 0;
|
|||
|
leaf->values[i] = NULL;
|
|||
|
}
|
|||
|
for (i = new_leaf->num_keys; i < ORDER - 1; i++) {
|
|||
|
new_leaf->keys[i] = 0;
|
|||
|
new_leaf->values[i] = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if (leaf->parent == NULL) {
|
|||
|
Node *new_root = create_node(0);
|
|||
|
new_root->keys[0] = new_leaf->keys[0];
|
|||
|
new_root->children[0] = leaf;
|
|||
|
new_root->children[1] = new_leaf;
|
|||
|
new_root->num_keys++;
|
|||
|
leaf->parent = new_root;
|
|||
|
new_leaf->parent = new_root;
|
|||
|
tree->root = new_root;
|
|||
|
} else {
|
|||
|
insert_into_parent(leaf, new_leaf->keys[0], new_leaf);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Node* find_leaf(Node *root, int key) {
|
|||
|
if (root == NULL) {
|
|||
|
return root;
|
|||
|
}
|
|||
|
int i = 0;
|
|||
|
while (i < root->num_keys) {
|
|||
|
if (key < root->keys[i]) {
|
|||
|
break;
|
|||
|
}
|
|||
|
i++;
|
|||
|
}
|
|||
|
if (root->children[0] != NULL) {
|
|||
|
return find_leaf(root->children[i], key);
|
|||
|
} else {
|
|||
|
return root;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Node* search(BPlusTree *tree, int key) {
|
|||
|
Node *leaf = find_leaf(tree->root, key);
|
|||
|
if (leaf == NULL) {
|
|||
|
return leaf;
|
|||
|
}
|
|||
|
int i = 0;
|
|||
|
while (i < leaf->num_keys) {
|
|||
|
if (leaf->keys[i] == key) {
|
|||
|
return leaf;
|
|||
|
}
|
|||
|
i++;
|
|||
|
}
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
void split_node(Node *old_node) {
|
|||
|
Node *new_node = create_node(old_node->children[0] != NULL ? 0 : 1);
|
|||
|
int mid = ORDER / 2;
|
|||
|
old_node->num_keys = mid;
|
|||
|
|
|||
|
int i = mid;
|
|||
|
int j = 0;
|
|||
|
while (i < ORDER - 1) {
|
|||
|
new_node->keys[j] = old_node->keys[i];
|
|||
|
new_node->values[j] = old_node->values[i];
|
|||
|
if (old_node->children[0] != NULL) {
|
|||
|
new_node->children[j] = old_node->children[i];
|
|||
|
new_node->children[j]->parent = new_node;
|
|||
|
}
|
|||
|
i++;
|
|||
|
j++;
|
|||
|
new_node->num_keys++;
|
|||
|
}
|
|||
|
if (old_node->children[0] != NULL) {
|
|||
|
new_node->children[j] = old_node->children[i];
|
|||
|
new_node->children[j]->parent = new_node;
|
|||
|
}
|
|||
|
|
|||
|
if (old_node->next != NULL) {
|
|||
|
new_node->next = old_node->next;
|
|||
|
}
|
|||
|
old_node->next = new_node;
|
|||
|
|
|||
|
if (old_node->parent == NULL) {
|
|||
|
Node *new_root = create_node(0);
|
|||
|
new_root->keys[0] = new_node->keys[0];
|
|||
|
new_root->children[0] = old_node;
|
|||
|
new_root->children[1] = new_node;
|
|||
|
new_root->num_keys++;
|
|||
|
old_node->parent = new_root;
|
|||
|
new_node->parent = new_root;
|
|||
|
tree->root = new_root;
|
|||
|
} else {
|
|||
|
insert_into_parent(old_node, new_node->keys[0], new_node);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void insert_into_parent(Node *left, int key, Node *right) {
|
|||
|
Node *parent = left->parent;
|
|||
|
int i = parent->num_keys - 1;
|
|||
|
while (i >= 0 && parent->keys[i] > key) {
|
|||
|
parent->keys[i + 1] = parent->keys[i];
|
|||
|
parent->children[i + 2] = parent->children[i + 1];
|
|||
|
i--;
|
|||
|
}
|
|||
|
parent->keys[i + 1] = key;
|
|||
|
parent->children[i + 2] = right;
|
|||
|
parent->num_keys++;
|
|||
|
|
|||
|
if (parent->num_keys == ORDER) {
|
|||
|
split_node(parent);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void print_tree(Node *root) {
|
|||
|
if (root == NULL) {
|
|||
|
return;
|
|||
|
}
|
|||
|
if (root->children[0] == NULL) {
|
|||
|
printf("Leaf Node: ");
|
|||
|
for (int i = 0; i < root->num_keys; i++) {
|
|||
|
printf("%d ", root->keys[i]);
|
|||
|
}
|
|||
|
printf("\n");
|
|||
|
} else {
|
|||
|
for (int i = 0; i <= root->num_keys; i++) {
|
|||
|
print_tree(root->children[i]);
|
|||
|
}
|
|||
|
printf("Internal Node: ");
|
|||
|
for (int i = 0; i < root->num_keys; i++) {
|
|||
|
printf("%d ", root->keys[i]);
|
|||
|
}
|
|||
|
printf("\n");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
int main() {
|
|||
|
BPlusTree *tree = create_bplus_tree();
|
|||
|
|
|||
|
insert(tree, 5, NULL);
|
|||
|
insert(tree, 15, NULL);
|
|||
|
insert(tree, 25, NULL);
|
|||
|
insert(tree, 35, NULL);
|
|||
|
insert(tree, 45, NULL);
|
|||
|
insert(tree, 55, NULL);
|
|||
|
insert(tree, 65, NULL);
|
|||
|
|
|||
|
print_tree(tree->root);
|
|||
|
|
|||
|
int search_key = 35;
|
|||
|
Node *result = search(tree, search_key);
|
|||
|
if (result != NULL) {
|
|||
|
printf("Key %d found in the tree.\n", search_key);
|
|||
|
} else {
|
|||
|
printf("Key %d not found in the tree.\n", search_key);
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
\end{minted}
|
|||
|
|
|||
|
查找学号为 95002 的学生信息就应该使用\mint{C}|find_leaf(tree, 95002)|
|
|||
|
}
|
|||
|
\questionandanswer[]{
|
|||
|
假设我们为SC表中的Cno(课程编号)字段创建了散列索引,要求使用散列索引来查找某个课程(如课程编号为 2)的所有选课记录。
|
|||
|
|
|||
|
|
|||
|
|
|||
|
编写一个函数,该函数接受课程编号(Cno),并输出该课程编号对应的所有选课记录。
|
|||
|
|
|||
|
要求:使用散列表的方式将课程编号(Cno)映射到槽位,并在槽位中存储选课记录。
|
|||
|
}{}
|
|||
|
{\kaishu
|
|||
|
\begin{minted}{python}
|
|||
|
def question_1_3(cno):
|
|||
|
# 这里的sc应该是一个字典(使用的是散列索引)
|
|||
|
return sc[cno]
|
|||
|
\end{minted}
|
|||
|
}
|
|||
|
|
|||
|
\end{enumerate}
|
|||
|
\questionandanswer[]{
|
|||
|
(计算题)
|
|||
|
|
|||
|
\noindent\includegraphics[width=1\linewidth]{imgs/2024-12-21-17-01-20.png}
|
|||
|
|
|||
|
使用SQL语言实现:
|
|||
|
}{}
|
|||
|
\begin{enumerate}
|
|||
|
\questionandanswer[]{
|
|||
|
在Employee表的EmpID列上创建聚集索引,并查询员工编号为3的员工信息。
|
|||
|
}{}
|
|||
|
{\kaishu
|
|||
|
\begin{minted}{SQL}
|
|||
|
create index index_id on Employee(EmpID);
|
|||
|
select * from Employee where EmpID = 3;
|
|||
|
\end{minted}
|
|||
|
}
|
|||
|
\questionandanswer[]{
|
|||
|
创建一个索引,加速员工薪资在5000到9000之间的员工信息的查询。
|
|||
|
}{}
|
|||
|
{\kaishu
|
|||
|
\begin{minted}{SQL}
|
|||
|
create index index_salary on Employee(Salary);
|
|||
|
select * from Employee where Salary between 5000 and 9000;
|
|||
|
\end{minted}
|
|||
|
}
|
|||
|
\questionandanswer[]{
|
|||
|
创建一个索引,加速部门为IT的所有员工信息的查询。
|
|||
|
}{}
|
|||
|
{\kaishu
|
|||
|
\begin{minted}{SQL}
|
|||
|
create index index_dept on Employee(Dept);
|
|||
|
select * from Employee where Dept = 'IT';
|
|||
|
\end{minted}
|
|||
|
}
|
|||
|
\questionandanswer[]{
|
|||
|
在Employee表上创建多列索引,查询部门为HR且职位为人事经理的员工信息。
|
|||
|
}{}
|
|||
|
{\kaishu
|
|||
|
\begin{minted}{SQL}
|
|||
|
create index index_dept_position on Employee(Dept, Position);
|
|||
|
select * from Employee where Dept = 'HR' and Position = '人事经理';
|
|||
|
\end{minted}
|
|||
|
}
|
|||
|
\end{enumerate}
|
|||
|
|
|||
|
\item \choice[2]{(判断题) 一个表只能建一个索引和一个聚簇。()
|
|||
|
A
|
|||
|
对
|
|||
|
|
|||
|
B
|
|||
|
错}{2}
|
|||
|
|
|||
|
\item \choice[2]{ (判断题) 聚集索引与普通索引相比,数据的插入、更新和删除效率更高。()
|
|||
|
A
|
|||
|
对
|
|||
|
|
|||
|
B
|
|||
|
错}{2}
|
|||
|
|
|||
|
\item \choice[2]{(判断题) 索引有助于提高数据检索的速度,因此建立索引的数量越多越好。()
|
|||
|
A
|
|||
|
对
|
|||
|
|
|||
|
B
|
|||
|
错}{2}
|
|||
|
|
|||
|
\item \choice[2]{(判断题) 对一个基本表需要建立多个聚集索引。()
|
|||
|
A
|
|||
|
对
|
|||
|
|
|||
|
B
|
|||
|
错}{2}
|
|||
|
|
|||
|
\item \choice[2]{(判断题) 对一个基本表建立索引不会改变基本表中的数据。()
|
|||
|
A
|
|||
|
对
|
|||
|
|
|||
|
B
|
|||
|
错}{1}
|
|||
|
|
|||
|
\questionandanswer[]{
|
|||
|
(简答题) B+索引与散列索引的区别。
|
|||
|
}{
|
|||
|
B+索引会在插入、删除记录时自动调整索引结构,使得查询任何一个值的时间复杂度相同。而散列索引只是在插入记录时计算散列值,选择一个合适的桶,在桶中还是需要顺序搜索,因此桶的数量会影响性能。
|
|||
|
}
|
|||
|
|
|||
|
\questionandanswer[]{
|
|||
|
(简答题) 请简述稠密索引与稀疏索引的区别,并说明在什么情况下使用稠密索引和稀疏索引比较合适?
|
|||
|
}{
|
|||
|
稠密索引是文件中的每个搜索码值都有一个索引记录,稀疏索引是只为搜索码的某些值建立索引记录。稠密索引定位一条记录的速度比较快,稀疏索引插入和删除时所需的空间及维护开销较小。
|
|||
|
|
|||
|
在查找操作较多时,适合使用稠密索引。在插入和删除的操作较多时,适合使用稀疏索引。
|
|||
|
}
|
|||
|
\end{enumerate}
|
|||
|
|
|||
|
\end{document}
|