From 7123a79aa65919c4a50722f9799e60e37d806143 Mon Sep 17 00:00:00 2001 From: Luthics Date: Tue, 19 Dec 2023 03:23:35 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=AE=9E=E9=AA=8C=E6=8A=A5?= =?UTF-8?q?=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 1/dp.in | 11 +- 7/s3.c | 14 + report.md | 3484 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 3505 insertions(+), 4 deletions(-) create mode 100644 report.md diff --git a/1/dp.in b/1/dp.in index 0b82d78..c74cf85 100644 --- a/1/dp.in +++ b/1/dp.in @@ -1,4 +1,7 @@ -70 3 -71 100 -69 1 -1 2 \ No newline at end of file +10 6 +1 1 +8 4 +4 2 +3 2 +5 3 +2 2 \ No newline at end of file diff --git a/7/s3.c b/7/s3.c index 45cc63b..f2b4445 100644 --- a/7/s3.c +++ b/7/s3.c @@ -259,6 +259,18 @@ void pt(Tree *root) { printf("\n"); } +// 打印二叉树 +void printTree(Tree *root, int level) { + if (root == NULL) + return; + printTree(root->r, level + 1); + for (int i = 0; i < level; i++) + printf(" "); + printf("%d\n", root->val); + printTree(root->l, level + 1); +} + + int main() { freopen("data.in", "r", stdin); scanf("%d", &n); @@ -276,6 +288,8 @@ int main() { scanf("%d", &x); root = deleteNode(root, x); pt(root); + printTree(root, 0); + printf("----------------\n"); } return 0; diff --git a/report.md b/report.md new file mode 100644 index 0000000..eeea8f9 --- /dev/null +++ b/report.md @@ -0,0 +1,3484 @@ +#! https://zhuanlan.zhihu.com/p/672958409 +# 数据结构实验报告 +## Author: Luthics + +# 实验一 背包问题求解 +## 问题描述 +假设有一个能装入总体积为`T`的背包和`n`件体积分别为`w1,w2,…wn`的物品,能否从`n`件物品中挑选若干件恰好装背包,即使`w1+w2+…+wm=T`,要求找出所有满足上述条件的解。 +例如:当`T=10`,各件物品的体积`{1,8,4,3,5,2}`时,可找到下列`4`组解: +- `(1,4,3,2)` +- `(1,4,5)` +- `(8,2)` +- `(3,5,2)` + +## 实现提示 +可利用回溯法的设计思想来解决背包问题。首先,将物品排成一列,然后,顺序选取物品装入背包,若已选取`i`件物品后未满,则继续选取第`i+1`件,若该件物品“太大”不能装入,则弃之,继续选取下一件,直至背包装满为。 + +如果在剩余的物品中找不到合适的物品以填满背包,则说明“刚刚”装入的物品“不合适”,应将它取出“弃之一”,继续再从“它之后”的物品中选取,如此重复,直到求得满足条件的解,或者无解。 + +由于回溯求解的规则是“后进先出”,自然要用到“栈”。 +进一步考虑:如果每件物品都有体积和价值,背包又有大小限制,求解背包中存放物品总价值最大的问题解---最优解或近似最优解。 + +## 思路 +一个能装入总体积为`T`的背包和`n`件体积分别为`w1,w2,…wn`的物品,能否从`n`件物品中挑选若干件恰好装背包,即使`w1+w2+…+wm=T` + +相当于取一个向量 $n$ = $(x_1, x_2, ..., x_n)$,其中 $x_i \in \{0, 1\}$,使得 $\sum_{i=1}^n x_i w_i = T$,求所有满足条件的 $n$。 + +使用暴力 `dfs` 搜索,搜索每一件物品的选择情况,符合条件时输出。 + +使用 `sum+w[k]<=T` 进行剪枝,提高运行速度 + +总情况数为 $2^n$,时间复杂度为 $O(2^n)$。 + +## 代码及解释 + +```c++ +#include +#include +#define MAX_N 100 + +int T; +int n; +int w[MAX_N]; +int x[MAX_N]; +int solution_count = 0; + +// dfs函数,t为当前搜索的层数,sum为当前已经选择的物品的体积和,k为当前搜索的物品编号 +void dfs(int t, int sum, int k) { + if (sum == T) { + solution_count++; + if (solution_count > 50) + return; + // 输出解 + int *re = malloc(sizeof(int) * k); + int j = 0; + for (int i = 0; i < k; i++) { + if (x[i]) { + re[j++] = w[i]; + } + } + for (int i = 0; i < j; i++) { + printf("%d ", re[i]); + } + printf("\n"); + } else if (sum < T && k < n) { + // 剪枝,提高运行速度 + if (sum + w[k] <= T) { + // 选第k件物品 + x[k] = 1; + dfs(t + 1, sum + w[k], k + 1); + } + // 不选第k件物品 + x[k] = 0; + dfs(t + 1, sum, k + 1); + } +} + +int main() { + freopen("data.in", "r", stdin); + // freopen("data.out", "w", stdout); + scanf("%d", &T); + scanf("%d", &n); + for (int i = 0; i < n; i++) { + scanf("%d", &w[i]); + } + // 搜索 + dfs(0, 0, 0); + printf("Total Solution Count: %d\n", solution_count); + return 0; +} + +``` + +## 输入样例 +``` +10 +6 +1 8 4 3 5 2 +``` + +## 输出样例 +``` +1 4 3 2 +1 4 5 +8 2 +3 5 2 +Total Solution Count: 4 +``` + +## 拓展方法 +将题目拓展为背包问题,使用 `dp` 算法解决 + +### 代码 +```c++ +#include +#include + +int T, M, t[105], w[105]; +int dp[1005][1005]; + +int max(int a, int b) { return a > b ? a : b; } + +int main() { + freopen("dp.in", "r", stdin); + scanf("%d%d", &T, &M); + for (int i = 1; i <= M; i++) { + scanf("%d%d", &t[i], &w[i]); + } + dp[0][0] = 0; + // dp[i][j]表示前i件物品,体积为j时的最大价值 + for (int i = 1; i <= M; i++) { + for (int j = 1; j <= T; j++) { + // 如果第i件物品的体积大于j,那么就不能选第i件物品 + if (j < t[i]) + dp[i][j] = dp[i - 1][j]; + else + // 否则就是选或者不选第i件物品的最大值 + dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - t[i]] + w[i]); + } + } + printf("%d\n", dp[M][T]); + // for(int i=1;i<=M;i++){ + // for(int j=1;j<=T;j++){ + // printf("%d ",dp[i][j]); + // } + // printf("\n"); + // } + + return 0; +} +``` + +### 输入样例 +``` +10 6 +1 1 +8 4 +4 2 +3 2 +5 3 +2 2 +``` + +### 输出样例 +``` +7 +``` + +# 实验二 农夫过河问题的求解 +## 问题描述 + 一个农夫带着一只狼、一只羊和一棵白菜,身处河的南岸。他要把这些东西 +全部运到北岸。他面前只有一条小船,船只能容下他和一件物品,另外只有农夫 +才能撑船。如果农夫在场,则狼不能吃羊,羊不能吃白菜,否则狼会吃羊,羊会 +吃白菜,所以农夫不能留下羊和白菜自己离开,也不能留下狼和羊自己离开,而 +狼不吃白菜。请求出农夫将所有的东西运过河的方案。 + +## 实现提示 +求解这个问题的简单方法是一步一步进行试探,每一步搜索所有可能的选择 +,对前一步合适的选择后再考虑下一步的各种方案。要模拟农夫过河问题,首先 +需要对问题中的每个角色的位置进行描述。可用4位二进制数顺序分别表示农夫、 +狼、白菜和羊的位置。用0表在南岸,1表示在北岸。例如,整数5 (0101)表示农 +夫和白菜在南岸,而狼和羊在北岸。 +现在问题变成:从初始的状态二进制0000(全部在河的南岸)出发,寻找一种 +全部由安全状态构成的状态序列,它以二进制1111(全部到达河的北岸)为最终目 +标。总状态共16种(0000到1111),(或者看成16个顶点的有向图)可采用广度优先或深度优先的搜索策略---得到从0000到1111的安全路径。 +以广度优先为例:整数队列---逐层存放下一步可能的安全状态;Visited[16]数组标记该状态是否已访问过,若访问过,则记录前驱状态值---安全路径。 +最终的过河方案应用汉字显示出每一步的两岸状态。 + +## 思路 +使用 `dfs` 搜索,搜索每一步的选择情况,符合条件时输出。 + +## 代码及解释 +```c++ +#include +#include + +int state = 0; +int v[16]; +char labels[4][16] = {"农夫", "狼", "羊", "菜"}; +int re[16]; +int xx[16]; +int k = 0; + +void pt(int command, int state) { + int *f = malloc(sizeof(int) * 4); + for (int i = 0; i < 4; i++) { + // 计算每一位的值 + f[i] = (state >> i) & 1; + } + // 按照命令输出 + if (command == 0) { + printf("农夫自己过河\n"); + } else if (command == 1) { + printf("带 狼 过河\n"); + } else if (command == 2) { + printf("带 羊 过河\n"); + } else if (command == 3) { + printf("带 菜 过河\n"); + } + printf("A > "); + for (int i = 0; i < 4; i++) { + if (f[i] == 1) { + printf("%s ", labels[i]); + } + } + printf("\n"); + printf("B > "); + for (int i = 0; i < 4; i++) { + if (f[i] == 0) { + printf("%s ", labels[i]); + } + } + printf("\n---------------------\n"); +} + +int validate_state(int state) { + int *f = malloc(sizeof(int) * 4); + for (int i = 0; i < 4; i++) { + f[i] = (state >> i) & 1; + // printf("%d ", f[i]); + } + // printf("\n"); + if (!f[1] ^ f[2] && f[0] ^ f[1]) { + return 0; + } else if (!f[2] ^ f[3] && f[0] ^ f[2]) { + return 0; + } else { + return 1; + } +} + +int trans(int x, int undo) { + // 0 表示农夫自己过河 + // 1 表示带狼过河 + // 2 表示带羊过河 + // 3 表示带菜过河 + int tmp_state = state; + if (x == 1) { + tmp_state = state ^ 3; + } else if (x == 2) { + tmp_state = state ^ 5; + } else if (x == 3) { + tmp_state = state ^ 9; + } else { + tmp_state = state ^ 1; + } + // pt(state); + // printf("%d > ", x); + // pt(tmp_state); + if (undo) { + v[tmp_state] = 0; + state = tmp_state; + re[--k] = 0; + xx[k] = 0; + return 1; + } + if (validate_state(tmp_state) == 0) { + return 0; + } + if (v[tmp_state] == 0) { + v[tmp_state] = 1; + state = tmp_state; + xx[k] = x; + re[k++] = state; + return 1; + } + return 0; +} + +void dfs() { + // printf(">> %d\n", state); + if (state == 15) { + for (int i = 0; i < k; i++) { + pt(xx[i], re[i]); + } + printf("\n"); + return; + } + if (trans(0, 0)) { + dfs(); + trans(0, 1); + } + if (trans(1, 0)) { + dfs(); + trans(1, 1); + } + if (trans(2, 0)) { + dfs(); + trans(2, 1); + } + if (trans(3, 0)) { + dfs(); + trans(3, 1); + } +} + +int main() { + // freopen("data.in", "r", stdin); + // freopen("data.out", "w", stdout); + v[0] = 1; + dfs(); + return 0; +} +``` + +## 输入样例 +``` +无 +``` + +## 输出样例 +``` +带 羊 过河 +A > 农夫 羊 +B > 狼 菜 +--------------------- +农夫自己过河 +A > 羊 +B > 农夫 狼 菜 +--------------------- +带 狼 过河 +A > 农夫 狼 羊 +B > 菜 +--------------------- +带 羊 过河 +A > 狼 +B > 农夫 羊 菜 +--------------------- +带 菜 过河 +A > 农夫 狼 菜 +B > 羊 +--------------------- +农夫自己过河 +A > 狼 菜 +B > 农夫 羊 +--------------------- +带 羊 过河 +A > 农夫 狼 羊 菜 +B > +--------------------- +``` + +# 实验三 简易电子表格的设计 +## 问题描述 +设计一个支持基本计算统计功能和其它一些表格管理/处理功能的软件,使用 +户可在该软件的支持下,用交互方式进行表格建立、数据输入、数据编辑及其它 +一些表格操作。即类似于简易Execel表格处理软件。 + +## 基本要求 +1. 建立表格:建立空白表格,同时在屏幕上显示,使其处于可输入数据的状态。用户可指定新建表格的行列数。 +2. 输入数据与编辑数据:通过键盘将数据输入到显示在屏幕上的电子表格上。 +3. 基本统计计算:可选择按行或列合计、求平均、求最大最小。 +4. 排序:使任意指定的行或列中的数据按大小(升或降)排列,对字符型数据, +还可选择大小写敏感。 +5. 表格保存:使电子表格以磁盘文件的方式存储在磁盘上,并可随时读入, +供继续处理。 +6. 公式支持*:单元格内可输入公式(表达式),使对应单元格的最终内容为公 +式的计算结果。公式最基本的形式是算术计算公式,可按名引用其它单元格。 +*该功能可选做。 + +## 思路 +参考代码中的 `main.c`,使用 `TableInfo` 结构体存储表格信息,使用 `key.h` 存储键盘按键的宏定义,使用 `conio.h` 实现光标移动,使用 `stdlib.h` 实现 `malloc` 函数,使用 `string.h` 实现 `strcpy` 函数,使用 `stdio.h` 实现 `printf` 函数,使用 `math.h` 实现 `atoi` 函数。 + + +## 代码及解释 +`key.h` +```c++ +#define KEY_LEFT_ARROW 0x4B // 左箭头 +#define KEY_UP_ARROW 0x48 // 上箭头 +#define KEY_RIGHT_ARROW 0x4D // 右箭头 +#define KEY_DOWN_ARROW 0x50 // 下箭头 +#define KEY_ENTER 0x0D // 回车 +#define KEY_ESC 0x1B // ESC +``` + +`main.c` +```c++ +#include "key.h" +#include +#include +#include +#include + +// 一些常量 +#define MAX_COLUMN 20 +#define MAX_ROW 20 +#define MAX_COLUMN_WIDTH 10 +#define MAX_DATA_LENGTH 100 +#define MAX_TICK 200 +#define MAX_MESSAGE_LENGTH 100 +#define FUNCTION_NUM 8 + +// tick 已废弃 +int tick = 0; +// 当前光标所在位置 +int current_x = 0; +int current_y = 0; +// 当前是否关闭 +int alive = 1; +// 选择模式是否启用 +int select_mode = 0; +// 从哪里进入选择模式 +int select_entry = 0; +// 当前的输入模式 +int input_mode = 0; + +// 底部的功能列表 +char functions[FUNCTION_NUM][100] = {"Save", "Load", "Sum", "Average", + "Max", "Min", "Sort", "Exit"}; + +// 当前的消息 +char *message = "Hi~"; + +// 表格信息 +typedef struct { + int current_width; + int current_height; + int column_width[MAX_COLUMN]; + char data[MAX_ROW][MAX_COLUMN][MAX_DATA_LENGTH]; +} TableInfo; + +// 常用函数 +int max(int a, int b) { return a > b ? a : b; } + +int min(int a, int b) { return a < b ? a : b; } + +// 计算一个 int 的长度 +int int_length(int num) { + if (num == 0) + return 1; + int length = 0; + while (num) { + num /= 10; + length++; + } + return length; +} + +// 判断一个字符串是否全是数字 +int is_string_all_number(char *str) { + int length = strlen(str); + for (int i = 0; i < length; i++) { + if (str[i] < '0' || str[i] > '9') { + return 0; + } + } + return 1; +} + +// 判断一行或一列是否全是数字 +int is_index_all_number(TableInfo *table, int type, int index) { + if (type == 0) { + // 行 + index = index - 1; + for (int i = 0; i < table->current_width; i++) { + if (strlen(table->data[index][i]) > 0) { + if (!is_string_all_number(table->data[index][i])) { + return 0; + } + } + } + } else { + // 列 + index = index - 'A'; + for (int i = 0; i < table->current_height; i++) { + if (strlen(table->data[i][index]) > 0) { + if (!is_string_all_number(table->data[i][index])) { + return 0; + } + } + } + } + return 1; +} + +// 交换两列 +void changeTwoColumn(TableInfo *table, int column1, int column2) { + // 验证两列是否合法且不等 + if (column1 == column2 || column1 < 0 || column2 < 0 || + column1 >= table->current_width || column2 >= table->current_width) { + return; + } + char tmp[MAX_DATA_LENGTH]; + for (int i = 0; i < table->current_height; i++) { + strcpy(tmp, table->data[i][column1]); + strcpy(table->data[i][column1], table->data[i][column2]); + strcpy(table->data[i][column2], tmp); + } +} + +// 交换两行 +void changeTwoRow(TableInfo *table, int row1, int row2) { + // 验证两行是否合法且不等 + if (row1 == row2 || row1 < 0 || row2 < 0 || row1 >= table->current_height || + row2 >= table->current_height) { + return; + } + char tmp[MAX_DATA_LENGTH]; + for (int i = 0; i < table->current_width; i++) { + strcpy(tmp, table->data[row1][i]); + strcpy(table->data[row1][i], table->data[row2][i]); + strcpy(table->data[row2][i], tmp); + } +} + +// 自定义支持大小写敏感的字符串比较函数 +int str_cmp(char *str1, char *str2, int case_sensitive) { + if (case_sensitive) { + return strcmp(str1, str2); + } else { + char *tmp_str1 = (char *)malloc(sizeof(char) * strlen(str1)); + char *tmp_str2 = (char *)malloc(sizeof(char) * strlen(str2)); + strcpy(tmp_str1, str1); + strcpy(tmp_str2, str2); + for (int i = 0; i < strlen(tmp_str1); i++) { + if (tmp_str1[i] >= 'A' && tmp_str1[i] <= 'Z') { + tmp_str1[i] += 32; + } + } + for (int i = 0; i < strlen(tmp_str2); i++) { + if (tmp_str2[i] >= 'A' && tmp_str2[i] <= 'Z') { + tmp_str2[i] += 32; + } + } + int result = strcmp(tmp_str1, tmp_str2); + free(tmp_str1); + free(tmp_str2); + return result; + } +} + +// 判断一个 index 是否合法 +int check_index_valid(TableInfo *table, char *index) { + // 合法的 index 应该是 A1,A23 这样的格式 + int length = strlen(index); + if (length == 0) { + return 0; + } + + if (index[0] >= 'A' && index[0] <= 'A' + table->current_width - 1) { + // 列号合法 + int row = 0; + for (int i = 1; i < length; i++) { + if (index[i] < '0' || index[i] > '9') { + return 0; + } + row = row * 10 + index[i] - '0'; + } + if (row >= 1 && row <= table->current_height) { + return 1; + } + } + return 0; +} + +// 传入实际的 index +int check_valid_formatter(TableInfo *table, int x, int y) { + // 合法的公示应该以 = 开头,并且只包含 + - 和 + char *data = malloc(sizeof(char) * MAX_DATA_LENGTH); + strcpy(data, table->data[y][x]); + if (data[0] != '=') { + return 0; + } + int length = strlen(data); + char *tmp_data = malloc(sizeof(char) * MAX_DATA_LENGTH); + for (int i = 1; i < length; i++) { + if (data[i] == ' ') + continue; + // 获取 =,-,+,\n 之间的字符串 + int j = i; + while (j < length && data[j] != '=' && data[j] != '-' && + data[j] != '+' && data[j] != '\n') { + tmp_data[j - i] = data[j]; + j++; + } + tmp_data[j - i] = '\0'; + // 判断是否是合法的 index + if (tmp_data[0] >= 'A' && tmp_data[0] <= 'Z') { + if (!check_index_valid(table, tmp_data)) + return 0; + } else { + for (int k = 0; k < j - i; k++) { + if (tmp_data[k] < '0' || tmp_data[k] > '9') { + return 0; + } + } + } + } + return 1; +} + +// 获取是行还是列 +int get_index_type(int index) { return index >> 7; } + +// 获取具体的值 +int get_index_value(int index) { return index & 0b01111111; } + +// 进入选择模式,选择某行或某列 +int start_select_index(int entry) { + message = "Select a row or column"; + current_x = 0; + current_y = 1; + select_mode = 1; + select_entry = entry; +} + +// 退出选择模式 +void end_select_index(TableInfo *table) { + // 一个 8 位数字 + // 最高位: 0 代表行 1 代表列 + // 低 7 位: 代表行号或列号对应字符的 ASCII 码 + int select_value = 0; + if (current_x == 0) { + // x = 0 表示选择行 + select_value = 0b00000000 | current_y; + } else { + // x > 0 表示选择列 + select_value = 0b10000000 | (current_x + 'A' - 1); + } + int type = get_index_type(select_value); + int value = get_index_value(select_value); + + int ascii_sort = 0; + // 0 代表不区分大小写,1 代表区分大小写 + int case_sensitive = 0; + + // 判断是否和数字操作有关 + if (select_entry == 2 || select_entry == 3 || select_entry == 4 || + select_entry == 5 || select_entry == 6) { + int valid = is_index_all_number(table, type, value); + if (!valid) { + if (select_entry == 6) { + ascii_sort = 1; + message = + "This row or column is not all number, use ASCII to " + "sort!\n Do you want to use Case Sensitive? ([Y]/n): "; + // 等待完善:选择大小写敏感 + // char tmp; + // input_mode = 1; + // scanf("%c", &tmp); + // input_mode = 0; + // if (tmp == 'n' || tmp == 'N') { + // case_sensitive = 0; + // } else { + // case_sensitive = 1; + // } + } else { + message = "This row or column is not all number!"; + return; + } + } + } + + if (select_entry == 2) { + // sum + int sum = 0; + if (type == 0) { + // 行 + value = value - 1; + for (int i = 0; i < table->current_width; i++) { + if (strlen(table->data[value][i]) > 0) { + sum += atoi(table->data[value][i]); + } + } + } else { + // 列 + value = value - 'A'; + for (int i = 0; i < table->current_height; i++) { + if (strlen(table->data[i][value]) > 0) { + sum += atoi(table->data[i][value]); + } + } + } + char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH); + sprintf(tmp_message, "Sum: %d", sum); + message = tmp_message; + } else if (select_entry == 3) { + // average + int sum = 0; + int count = 0; + if (type == 0) { + // 行 + value = value - 1; + for (int i = 0; i < table->current_width; i++) { + if (strlen(table->data[value][i]) > 0) { + sum += atoi(table->data[value][i]); + count++; + } + } + } else { + // 列 + value = value - 'A'; + for (int i = 0; i < table->current_height; i++) { + if (strlen(table->data[i][value]) > 0) { + sum += atoi(table->data[i][value]); + count++; + } + } + } + char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH); + sprintf(tmp_message, "Average: %f", (double)sum / count); + message = tmp_message; + } else if (select_entry == 4) { + // max + int max_ = -0x7fffffff; + if (type == 0) { + // 行 + value = value - 1; + for (int i = 0; i < table->current_width; i++) { + if (strlen(table->data[value][i]) > 0) { + max_ = atoi(table->data[value][i]) > max_ + ? atoi(table->data[value][i]) + : max_; + } + } + } else { + // 列 + value = value - 'A'; + for (int i = 0; i < table->current_height; i++) { + if (strlen(table->data[i][value]) > 0) { + max_ = atoi(table->data[i][value]) > max_ + ? atoi(table->data[i][value]) + : max_; + } + } + } + char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH); + sprintf(tmp_message, "Max: %d", max_); + message = tmp_message; + } else if (select_entry == 5) { + // min + int min_ = 0x7fffffff; + if (type == 0) { + // 行 + value = value - 1; + for (int i = 0; i < table->current_width; i++) { + if (strlen(table->data[value][i]) > 0) { + min_ = atoi(table->data[value][i]) < min_ + ? atoi(table->data[value][i]) + : min_; + } + } + } else { + // 列 + value = value - 'A'; + for (int i = 0; i < table->current_height; i++) { + if (strlen(table->data[i][value]) > 0) { + min_ = atoi(table->data[i][value]) < min_ + ? atoi(table->data[i][value]) + : min_; + } + } + } + char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH); + sprintf(tmp_message, "Min: %d", min_); + message = tmp_message; + } else if (select_entry == 6) { + // sort + if (type == 0) { + // 行 + value = value - 1; + for (int i = 0; i < table->current_width; i++) { + for (int j = i + 1; j < table->current_width; j++) { + if (!ascii_sort) { + if (atoi(table->data[value][i]) > + atoi(table->data[value][j])) { + changeTwoColumn(table, i, j); + } + message = "Sorted by number!"; + } else { + if (str_cmp(table->data[value][i], + table->data[value][j], + case_sensitive) > 0) { + changeTwoColumn(table, i, j); + } + message = "Sorted by ASCII!"; + } + } + } + } else { + // 列 + value = value - 'A'; + for (int i = 0; i < table->current_height; i++) { + for (int j = i + 1; j < table->current_height; j++) { + if (!ascii_sort) { + if (atoi(table->data[i][value]) > + atoi(table->data[j][value])) { + changeTwoRow(table, i, j); + } + message = "Sorted by number!"; + } else { + if (str_cmp(table->data[i][value], + table->data[j][value], + case_sensitive) > 0) { + changeTwoRow(table, i, j); + } + message = "Sorted by ASCII!"; + } + } + } + } + } else { + // 选择行或列 + if (type == 0) { + // 行 + value = value - 1; + char *tmp_message = + (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH); + sprintf(tmp_message, "Selected row: %d", value + 1); + message = tmp_message; + } else { + // 列 + value = value - 'A'; + char *tmp_message = + (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH); + sprintf(tmp_message, "Selected column: %c", value + 'A'); + message = tmp_message; + } + } + + // char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH); + // sprintf(tmp_message, "Selected: %d,%c", get_index_type(select_value), + // get_index_value(select_value)); + // message = tmp_message; + + select_mode = 0; + current_y = -1; + current_x = select_entry; +} + +// 将 int 转换为字符串 +char *int_to_string(int num) { + char *str = (char *)malloc(sizeof(char) * 100); + sprintf(str, "%d", num); + return str; +} + +// 初始化表格 +void init_table(int width, int height, int column_width, TableInfo *table) { + table->current_width = width; + table->current_height = height; + for (int i = 0; i < width; i++) { + table->column_width[i] = column_width; + } + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + table->data[i][j][0] = '\0'; + } + } +} + +// 计算公式 +int cal_formula(TableInfo *table, int x, int y) { + char *data = malloc(sizeof(char) * MAX_DATA_LENGTH); + strcpy(data, table->data[y][x]); + int length = strlen(data); + char *tmp_data = malloc(sizeof(char) * MAX_DATA_LENGTH); + int result = 0; + for (int i = 1; i < length; i++) { + if (data[i] == ' ') + continue; + // 标注 - + int minus = 0; + // 获取 =,-,+,\n 之间的字符串 + int j = i; + while (j < length && data[j] != '=' && data[j] != '-' && + data[j] != '+' && data[j] != '\n') { + tmp_data[j - i] = data[j]; + j++; + } + tmp_data[j - i] = '\0'; + // 判断是否是 - + if (data[i - 1] == '-') { + minus = 1; + } + // 判断是否是合法的 index + if (tmp_data[0] >= 'A' && tmp_data[0] <= 'Z') { + if (!check_index_valid(table, tmp_data)) + return -1; + // 0 表示 列,剩下的是行 + int col = tmp_data[0] - 'A'; + int row = 0; + for (int k = 1; k < strlen(tmp_data); k++) { + row = row * 10 + tmp_data[k] - '0'; + } + // 判断 table->data[row - 1][col] 是否是数字 + if (strlen(table->data[row - 1][col]) == 0) { + return -1; + } + if (!is_string_all_number(table->data[row - 1][col])) { + return -1; + } + if (minus) { + result -= atoi(table->data[row - 1][col]); + } else { + result += atoi(table->data[row - 1][col]); + } + } else { + for (int k = 0; k < j - i; k++) { + if (tmp_data[k] < '0' || tmp_data[k] > '9') { + return -1; + } + } + if (minus) { + result -= atoi(tmp_data); + } else { + result += atoi(tmp_data); + } + } + i += j - i; + } + return result; +} + +// 输出表格 +void printTable(TableInfo *table) { + system("cls"); + printf("Table v0.2 by Luthics\n"); + int max_l = int_length(table->current_height) + 1; + // render table + for (int i = -1; i < table->current_height; i++) { + for (int j = -1; j < table->current_width; j++) { + printf("+"); + if (j == -1) { + for (int k = 0; k < max_l; k++) { + printf("-"); + } + continue; + } + for (int k = 0; k < table->column_width[j]; k++) { + printf("-"); + } + } + printf("+\n"); + if (i == -1) { + // 输出 A-Z 的列名 + for (int j = -1; j < table->current_width; j++) { + if (j == -1) { + printf("|"); + for (int k = 0; k < max_l; k++) { + printf(" "); + } + continue; + } + printf("|"); + for (int k = 0; k < table->column_width[j] - 1; k++) { + printf(" "); + } + if (select_mode) { + if (current_x - 1 == j && current_y == 0) { + // RED + printf("\033[31m"); + // BOLD + printf("\033[1m"); + } + } + printf("%c", 'A' + j); + if (select_mode) { + if (current_x - 1 == j && current_y == 0) { + // RESET + printf("\033[0m"); + } + } + } + printf("|\n"); + continue; + } + for (int j = -1; j < table->current_width; j++) { + if (j == -1) { + printf("|"); + int data_length = int_length(i + 1); + for (int k = 0; k < max_l - data_length; k++) { + printf(" "); + } + if (select_mode) { + if (current_x == 0 && current_y == i + 1) { + // RED + printf("\033[31m"); + // BOLD + printf("\033[1m"); + } + } + printf("%d", i + 1); + if (select_mode) { + if (current_x == 0 && current_y == i + 1) { + // RESET + printf("\033[0m"); + } + } + continue; + } + printf("|"); + int data_length = + table->data[i][j] == NULL ? 0 : strlen(table->data[i][j]); + + if (data_length == 0) { + if (i == current_y && j == current_x && !select_mode) { + printf("_"); + } else { + printf(" "); + } + for (int k = 0; k < table->column_width[j] - 1; k++) { + printf(" "); + } + } else { + int formatter = 0; + if (table->data[i][j][0] == '=') { + formatter = 1; + } + int add_ = 0; + if (select_mode) { + if (current_x == j + 1 || current_y == i + 1) { + // RED + printf("\033[31m"); + // BOLD + printf("\033[1m"); + } + } else if (i == current_y && j == current_x) { + // RED + printf("\033[31m"); + // BOLD + printf("\033[1m"); + } else if (formatter) { + // BLUE + printf("\033[34m"); + // BOLD + printf("\033[1m"); + } + int formatter_success = 0; + if (formatter && !select_mode && + !(i == current_y && j == current_x)) { + // todo 先计算后渲染,修复列宽不合适的bug + int data = cal_formula(table, j, i); + if (data == -1) { + formatter_success = 0; + } else { + char *tmp_data = + (char *)malloc(sizeof(char) * MAX_DATA_LENGTH); + sprintf(tmp_data, "%d", data); + data_length = strlen(tmp_data); + printf("%s", tmp_data); + table->column_width[j] = + max(table->column_width[j], strlen(tmp_data)); + formatter_success = 1; + } + } + if (!formatter_success) { + for (int k = 0; k < data_length; k++) { + printf("%c", table->data[i][j][k]); + } + } + if (select_mode) { + if (current_x == j + 1 || current_y == i + 1) { + // RESET + printf("\033[0m"); + } + } else if (i == current_y && j == current_x) { + // RESET + printf("\033[0m"); + // 如果还有空间,就补上下划线 + if (table->column_width[j] - data_length > 0) { + printf("_"); + add_ = 1; + } + } else if (formatter) { + // Reset + printf("\033[0m"); + } + if (formatter) { + // 补上空格 + for (int k = 0; k < table->column_width[j] - data_length; + k++) { + printf(" "); + } + } else { + for (int k = 0; + k < table->column_width[j] - data_length - add_; k++) { + printf(" "); + } + } + } + } + printf("|\n"); + } + for (int j = -1; j < table->current_width; j++) { + if (j == -1) { + printf("+"); + for (int k = 0; k < max_l; k++) { + printf("-"); + } + continue; + } + printf("+"); + for (int k = 0; k < table->column_width[j]; k++) { + printf("-"); + } + } + printf("+\n"); + + // render functions + for (int i = 0; i < FUNCTION_NUM; i++) { + if (current_y == -1 && i == current_x) { + // RED + printf("\033[31m"); + // BOLD + printf("\033[1m"); + } + printf("%s", functions[i]); + if (current_y == -1 && i == current_x) { + // RESET + printf("\033[0m"); + } + if (i != FUNCTION_NUM - 1) { + printf(" "); + } + } + printf("\n"); + printf("%s\n", message); +} + +// 判断数据文件是否存在 +int is_data_file_exist() { + FILE *fp = fopen("table.dat", "r"); + if (fp == NULL) { + return 0; + } else { + fclose(fp); + return 1; + } +} + +// 从文件中读取表格信息 +TableInfo load_table_from_file(char *filename) { + TableInfo table; + FILE *fp = fopen(filename, "r"); + fread(&table, sizeof(TableInfo), 1, fp); + fclose(fp); + return table; +} + +// 将表格信息写入文件 +int save_table_to_file(char *filename, TableInfo table) { + FILE *fp = fopen(filename, "w"); + fwrite(&table, sizeof(TableInfo), 1, fp); + fclose(fp); + return 0; +} + +// 设置单元格的值 +void setCell(TableInfo *table, int row, int column, char *data) { + if (row >= table->current_height || column >= table->current_width || + row < 0 || column < 0) { + return; + } + strcpy(table->data[row][column], data); + if (strlen(data) > table->column_width[column]) { + table->column_width[column] = strlen(data); + } +} + +// 获取单元格的值 +int get_max_width_in_a_column(TableInfo *table, int column) { + int max_width = 0; + for (int i = 0; i < table->current_height; i++) { + int data_length = + table->data[i][column] == NULL ? 0 : strlen(table->data[i][column]); + if (data_length > max_width) { + max_width = data_length; + } + } + return max(max_width, table->column_width[column]); +} + +// 向单元格中添加字符 +void add_char_to_cell(TableInfo *table, int row, int column, char c) { + if (row >= table->current_height || column >= table->current_width || + row < 0 || column < 0) { + return; + } + int data_length = strlen(table->data[row][column]); + if (data_length < MAX_DATA_LENGTH - 1) { + table->data[row][column][data_length] = c; + table->data[row][column][data_length + 1] = '\0'; + table->column_width[column] = get_max_width_in_a_column(table, column); + } +} + +// 从单元格中删除字符 +void del_char_from_cell(TableInfo *table, int row, int column) { + if (row >= table->current_height || column >= table->current_width || + row < 0 || column < 0) { + return; + } + int data_length = strlen(table->data[row][column]); + if (data_length > 0) { + table->data[row][column][data_length - 1] = '\0'; + table->column_width[column] = get_max_width_in_a_column(table, column); + } +} + +// 返回 1 代表响应成功 +// 返回 0 代表响应失败 +int handleKeyPress(int key, TableInfo *table) { + int status = 0; + if (key == 224) { // 特殊键 + key = getch(); // 获取特殊键码 + if (key == KEY_LEFT_ARROW) { + if (select_mode) { + // 选择模式,0,0是左上角,0,1 0,2 0,3 代表每行行号 1,0 2,0 3,0 + // 代表每列列号 + if (current_y == 0) { + if (current_x > 1) + current_x--; + else + current_x = table->current_width; + } + if (current_x == 0) { + current_y = 0; + current_x = table->current_width; + } + status = 1; + } else { + if (current_x > 0) { + current_x--; + } else if (current_x == 0) { + if (current_y == -1) { + current_x = FUNCTION_NUM - 1; + } else { + current_x = table->current_width - 1; + } + } + status = 1; + } + } + if (key == KEY_RIGHT_ARROW) { + if (select_mode) { + if (current_y == 0) { + if (current_x < table->current_width) + current_x++; + else + current_x = 1; + } + if (current_x == 0) { + current_y = 0; + current_x = 1; + } + status = 1; + } else { + if (current_y == -1) { + current_x = (current_x + 1) % FUNCTION_NUM; + } else { + current_x = (current_x + 1) % table->current_width; + } + status = 1; + } + } + if (key == KEY_UP_ARROW) { + if (select_mode) { + if (current_x == 0) { + if (current_y > 1) + current_y--; + else + current_y = table->current_height; + } + if (current_y == 0) { + current_x = 0; + current_y = table->current_height; + } + status = 1; + } else { + if (current_y > 0) { + current_y--; + } else if (current_y == 0) { + current_y = -1; + current_x = min(FUNCTION_NUM - 1, current_x); + } else if (current_y == -1) { + current_y = table->current_height - 1; + current_x = min(table->current_width - 1, current_x); + } + status = 1; + } + } + if (key == KEY_DOWN_ARROW) { + if (select_mode) { + if (current_x == 0) { + if (current_y < table->current_height) + current_y++; + else + current_y = 1; + } + if (current_y == 0) { + current_x = 0; + current_y = 1; + } + status = 1; + } else { + if (current_y < table->current_height - 1 && current_y >= 0) { + current_y++; + } else if (current_y == table->current_height - 1) { + // TO FUNCTIONS AREA + current_y = -1; + current_x = min(FUNCTION_NUM - 1, current_x); + } else if (current_y == -1) { + current_y = 0; + current_x = min(table->current_width - 1, current_x); + } + status = 1; + } + } + } else { + // ctrl + c + if (key == 3) { + alive = 0; + message = "Bye!"; + status = 1; + } + // ctrl + s + if (key == 19) { + save_table_to_file("table.dat", *table); + message = "Saved!"; + status = 1; + } + if (select_mode) { + // ENTER + if (key == KEY_ENTER) { + end_select_index(table); + status = 1; + } + // ESC + if (key == KEY_ESC) { + select_mode = 0; + current_y = -1; + current_x = select_entry; + message = "Canceled!"; + status = 1; + } + } else { + // 判断 key 是可打印字符 + if (key >= 32 && key <= 126) { + add_char_to_cell(table, current_y, current_x, key); + status = 1; + } + // 退格键 + if (key == 8) { + del_char_from_cell(table, current_y, current_x); + status = 1; + } + // ENTER + if (key == KEY_ENTER) { + if (current_y == -1) { + if (current_x == 0) { + save_table_to_file("table.dat", *table); + message = "Saved!"; + } + if (current_x == 1) { + *table = load_table_from_file("table.dat"); + message = "Loaded!"; + } + if (current_x == 2) { + start_select_index(2); + } + if (current_x == 3) { + start_select_index(3); + } + if (current_x == 4) { + start_select_index(4); + } + if (current_x == 5) { + start_select_index(5); + } + if (current_x == 6) { + start_select_index(6); + } + if (current_x == 7) { + alive = 0; + message = "Bye!"; + } + } else { + // 如果以 = 开头 + if (table->data[current_y][current_x][0] == '=') { + if (!check_valid_formatter(table, current_x, + current_y)) { + message = "Not a valid formatter!"; + } + } + } + } + } + } + + int debug = 0; + if (debug) { + char *tmp_message = (char *)malloc(sizeof(char) * MAX_MESSAGE_LENGTH); + sprintf(tmp_message, "x: %d, y: %d", current_x, current_y); + message = tmp_message; + } + return status; +} + +int main() { + TableInfo table; + int load_from_file = 0; + + // 先判断是否有数据文件 + if (is_data_file_exist()) { + char tmp; + // 询问是否从文件中读取 + printf("Load from file? ([Y]/n): "); + scanf("%c", &tmp); + if (tmp == 'n' || tmp == 'N') { + load_from_file = 0; + } else { + load_from_file = 1; + } + } + if (load_from_file) { + table = load_table_from_file("table.dat"); + } else { + // 创建新表格 + printf("Let's create a table!\n"); + int width = 0, height = 0; + int column_width = 0; + while (width <= 0 || width > MAX_COLUMN) { + printf("Input width (1-%d): ", MAX_COLUMN); + scanf("%d", &width); + } + while (height <= 0 || height > MAX_ROW) { + printf("Input height (1-%d): ", MAX_ROW); + scanf("%d", &height); + } + while (column_width <= 0 || column_width > MAX_COLUMN_WIDTH) { + printf("Input column width (1-%d): ", MAX_COLUMN_WIDTH); + scanf("%d", &column_width); + } + + init_table(width, height, column_width, &table); + } + // 把第一行第一列的单元格设置为 Hello + setCell(&table, 0, 0, "Hello"); + + // 主循环 + while (alive) { + printTable(&table); + // drawGUI(currentSelection); + + int keyPressed = getch(); // 获取键盘输入 + handleKeyPress(keyPressed, &table); + + // tick = (tick + 1) % MAX_TICK; + } + + // 询问是否保存 + int save_to_file = 0; + char tmp; + printf("Save to file? ([Y]/n): "); + scanf("%c", &tmp); + if (tmp == 'n' || tmp == 'N') { + save_to_file = 0; + } else { + save_to_file = 1; + } + if (save_to_file) { + save_table_to_file("table.dat", table); + } + return 0; +} +``` + +# 实验四 八皇后问题 +## 问题描述 +设在初始状态下在国际象棋的棋盘上没有任何棋子(这里的棋子指皇后棋子)。然后顺序在第1行,第2行……第8行上布放棋子。在每一行中共有8个可选择的位置,但在任一时刻棋盘的合法布局都必须满足3个限制条件(1)任意两个棋子不得放在同一行(2)任意两个棋子不得放在同一列上(3)任意棋子不得放在同一正斜线和反斜线上。 + +## 基本要求 +编写求解并输出此问题的一个合法布局的程序。 + +## 实现提示: +在第i行布放棋子时,从第1列到第8列逐列考察。当在第i行第j列布放棋子时,需要考察布放棋子后在行方向、列方向、正斜线和反斜线方向上的布局状态是否合法,若该棋子布放合法,再递归求解在第i+1行布放棋子;若该棋子布放不合法,移去这个棋子,恢复布放该棋子前的状态,然后再试探在第i行第j+1列布放棋子。 + +## 思路 +使用回溯法,从第一行开始,每一行都有 n 个选择,如果选择合法,就继续下一行,否则就回溯到上一行,重新选择。 + +## 代码 +参考 洛谷 P1219 实现的 `n` 皇后问题 +```c +#include +#include + +// 每行每列每个对角线是否被占用 +int ans, a[10005]; +int n; +int cx[100], zx[100], col[100]; + +void pt() { + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + if (a[i] == j) { + printf("Q"); + } else { + printf("."); + } + } + printf("\n"); + } + printf("\n"); + for (int i = 1; i <= n; i++) { + printf("%d ", a[i]); + } + printf("\n"); +} + +// 回溯法 +void dfs(int x) { + if (x > n) { + ans++; + if (ans <= 3) { + pt(); + // for (int i = 1; i <= n; i++) { + // printf("%d ", a[i]); + // } + // printf("\n"); + } + return; + } + for (int i = 1; i <= n; i++) { + int l = x + i, r = x - i + 15; + // 如果这一列、这一正对角线、这一反对角线都没有被占用 + if (cx[r] == 0 && zx[l] == 0 && col[i] == 0) { + a[x] = i; + cx[r] = 1; + zx[l] = 1; + col[i] = 1; + dfs(x + 1); + cx[r] = 0; + zx[l] = 0; + col[i] = 0; + } + } +} + +int main() { + scanf("%d", &n); + dfs(1); + printf("%d", ans); + return 0; +} + +// Ref: P1219 [USACO1.5] 八皇后 Checker Challenge +// https://www.luogu.com.cn/record/40132197 +``` + +## 输入 +``` +8 +``` + +## 输出 +``` +Q....... +....Q... +.......Q +.....Q.. +..Q..... +......Q. +.Q...... +...Q.... + +1 5 8 6 3 7 2 4 +Q....... +.....Q.. +.......Q +..Q..... +......Q. +...Q.... +.Q...... +....Q... + +1 6 8 3 7 4 2 5 +Q....... +......Q. +...Q.... +.....Q.. +.......Q +.Q...... +....Q... +..Q..... + +1 7 4 6 8 2 5 3 +92 +``` + +## 输入2 +``` +12 +``` + +## 输出2 +``` +Q........... +..Q......... +....Q....... +.......Q.... +.........Q.. +...........Q +.....Q...... +..........Q. +.Q.......... +......Q..... +........Q... +...Q........ + +1 3 5 8 10 12 6 11 2 7 9 4 +Q........... +..Q......... +....Q....... +.........Q.. +.......Q.... +..........Q. +.Q.......... +...........Q +.....Q...... +........Q... +......Q..... +...Q........ + +1 3 5 10 8 11 2 12 6 9 7 4 +Q........... +..Q......... +....Q....... +.........Q.. +.......Q.... +..........Q. +.Q.......... +...........Q +......Q..... +........Q... +...Q........ +.....Q...... + +1 3 5 10 8 11 2 12 7 9 4 6 +14200 +``` + +# 实验五 约瑟夫环问题仿真 +## 问题描述 +设编号为`1,2,…,n(n>0)`个人按顺时针方向围坐一圈,每人持有一个正整数密码。开始时任意给出一个报数上限`m`,从第一个人开始顺时针方向自1起顺序报数,报到`m`时停止报数,报`m`的人出列,将他的密码作为新的`m`值,从他在顺时针方向上的下一个人起重新自`1`报数;如此下去直到所有人全部出列为止。 + +## 基本要求 +设计一个程序模拟此过程,给出出列人的编号序列。 + +## 实现提示: +可考虑不带头结点的单链表结构。 + +## 测试数据: +`N=7`,七个人的密码依次为`3,1,7,2,4,8,4`. +初始报数上限值`m=20`。 + +## 思路 +使用循环链表,每次报数到 m 时,就删除这个节点,然后从下一个节点开始重新报数,直到报到这个节点对应的数字,再次循环。 + +## 代码 +```c +#include +#include + +int rs[105]; // 每个人的状态 +int n; // 人数 + +int xcz; // 剩余人数 +int m[105], m0; // 间隔 + +int zj(int a) { + // 寻找下一个活着的人 + a++; + if (a > n) + a -= n; + while (rs[a] == 0 && xcz > 0) { + a++; + if (a > n) + a -= n; + } + return a; +} + +int main() { + freopen("data.in", "r", stdin); + scanf("%d%d", &n, &m0); + for (int i = 1; i <= n; i++) + scanf("%d", &m[i]); + xcz = n; + for (int i = 1; i <= n; i++) + // 把每个人设置为活着 + rs[i] = 1; + while (m0 > n) { + m0 -= n; + } + for (int i = m0; xcz > 0;) { + if (rs[i] == 1) { + rs[i] = 0; + xcz--; + printf("%d ", i); + } + int ls = m[i]; + // printf("ls=%d\n", ls); + while (ls--) + i = zj(i); + } + return 0; +} + +// Ref: P1996 约瑟夫问题 +// 已修改为实验版本 +``` + +## 输入 +``` +7 20 +3 1 7 2 4 8 4 +``` + +## 输出 +``` +6 1 4 7 2 3 5 +``` + +# 实验六 教学计划编制问题 +## 问题描述 +大学的每个专业都要制定教学计划。假设任何专业都有固定的学习年 +限,每学年含两学期,每学期的时间长度和学分上限值均相等。每个专业 +开设的课程都是固定的,而且课程在开设时间的安排必须满足先修关系。 +每门课程有哪些先修课程是确定的,可以有任意多门,也可以没有。每门 +课恰好占一个学期。试在这样的前提下设计一个教学计划编制程序。 + +## 基本要求 +1. 输入参数包括:学期总数,一学期的学分上限,每门课的课程号 +(固定占3位的字母数字串)、学分和直接先修课的课程号。 +2. 允许用户指定下列两种编排策略之一:一是使学生在各学期中的 +学习负担尽量均匀;二是使课程尽可能地集中在前几个学期中。 +3. 若根据给定的条件问题无解,则报告适当的信息;否则,将教学 +计划输出到用户指定的文件中。计划的表格格式自行设计。 + +## 实现提示: +可设学期总数不超过12,课程总数小于100。如果输入的先修课程号不在该专业开设的课程序列中,则作为错误处理。 + +## 思路: +使用拓扑排序,先将课程按照先修课程的数量进行排序,先修课程数量越多的课程越靠前,然后从前往后,如果学期学习数量小于平均值,则将前面的课程往后移动。 + +## 代码: +```c++ +#include + +using namespace std; + +int se; // 学期总数 +int sx; // 学分上限 +int cl; // 课程数量 +int ren[12], fe[12], re[12][105]; + +struct Class { + string id; // 课程编号 + int sx; // 学分 + int xq; + int sel; // 是否已经选择 + int pren, nexn; // 先修课程数量 + int ppren; // use for topo + string pre[105]; // 先修课程 + string next[105]; // 后续课程 +} ss[105]; + +// 输出具体的课程安排 +void pt(int detail = 0) { + // print + for (int i = 0; i < cl; i++) { + cout << ss[i].id << " " << ss[i].sx << " " << ss[i].pren << " " + << ss[i].nexn << endl; + if (!detail) + continue; + if (ss[i].pren > 0) { + cout << "pre:" << endl; + for (int j = 0; j < ss[i].pren; j++) { + cout << ss[i].pre[j] << " "; + } + cout << endl; + } + if (ss[i].nexn > 0) { + cout << "next:" << endl; + for (int j = 0; j < ss[i].nexn; j++) { + cout << ss[i].next[j] << " "; + } + cout << endl; + } + cout << endl; + } +} + +// 寻找 id +int fd(string id) { + for (int i = 0; i < cl; i++) { + if (ss[i].id == id) { + return i; + } + } + return -1; +} + +// 去除字符串两边空格和换行符 +string ql(string s) { + int l = 0, r = s.length() - 1; + while (s[l] == ' ' || s[l] == '\n') + l++; + while (s[r] == ' ' || s[r] == '\n') + r--; + return s.substr(l, r - l + 1); +} + +// 寻找先修课程的最迟学期 +int xz(int i) { + // cout << ss[i].id << " " << ss[i].pren << endl; + if (ss[i].pren == 0) + return 0; + int maxn = 0; + for (int j = 0; j < ss[i].pren; j++) { + int fid = fd(ss[i].pre[j]); + if (ss[fid].xq > maxn) { + maxn = ss[fid].xq; + } + } + return maxn + 1; +} + +// 寻找后修课程的最早学期 +int xz2(int i) { + if (ss[i].nexn == 0) + return se; + int minn = se; + for (int j = 0; j < ss[i].nexn; j++) { + int fid = fd(ss[i].next[j]); + if (ss[fid].xq < minn) { + minn = ss[fid].xq; + } + } + return minn; +} +// 拓扑排序,将排好序的id 放入sorted +void topo() { + int sorted[105] = {0}; + int cnt = 0; + queue q; + for (int i = 0; i < cl; i++) { + if (ss[i].ppren == 0) { + q.push(&ss[i]); + } + } + while (!q.empty()) { + Class *tmp = q.front(); + sorted[cnt] = fd(tmp->id); + q.pop(); + cnt++; + for (int i = 0; i < tmp->nexn; i++) { + ss[fd(tmp->next[i])].ppren--; + if (ss[fd(tmp->next[i])].ppren == 0) { + q.push(&ss[fd(tmp->next[i])]); + } + } + } + if (cnt != cl) { + cout << "error" << endl; + exit(0); + } + Class newss[105]; + // 将 ss 顺序改为 sorted 顺序 + for (int i = 0; i < cl; i++) { + newss[i] = ss[sorted[i]]; + } + for (int i = 0; i < cl; i++) { + ss[i] = newss[i]; + } +} + +void avgf() { + // 计算平均每学期学习数量 + int avg = cl / se; + // cout << avg << endl; + // 按从后向前,如果学期学习数量小于平均值,则将前面的课程往后移动 + for (int i = se - 1; i >= 1; i--) { + if (ren[i] < avg) { + for (int j = i - 1; j >= 0; j--) { + // cout << j << " " << ren[j] << endl; + if (ren[j] <= 0) + continue; + int flag = 0; + for (int ii = 0; ii < ren[j] && ren[i] < avg; ii++) { + int c = re[j][ii]; + // cout << " " << ss[c].id << " " << xz2(2)<< endl; + // cout << c << endl; + if (xz2(c) > i) { + // 把 c 课程放到 i 学期 + re[i][ren[i]++] = c; + ss[c].xq = i; + // 把 c 课程从 j 学期删除 + for (int jj = ii; jj < ren[j] - 1; jj++) { + re[j][jj] = re[j][jj + 1]; + } + ren[j]--; + flag = 1; + } + // 如果 i 学期中的课程数量大于平均值,则跳出循环 + if (ren[i] >= avg) { + break; + } + if (flag == 1) { + ii = -1; + flag = 0; + } + } + } + } + } +} + +// 输出最终结果 +void pte() { + for (int i = 0; i < se; i++) { + if (ren[i] == 0) + continue; + cout << "学期 " << i + 1 << " 学分 " << fe[i] << " 课程数 " << ren[i] + << " 课程:"; + for (int j = 0; j < ren[i]; j++) { + cout << ss[re[i][j]].id << " "; + } + cout << endl; + } +} + +int main() { + freopen("in.txt", "r", stdin); + cin >> se >> sx; + if (se <= 0 || sx <= 0 || se > 6 || sx > 10) { + cout << "error" << endl; + exit(0); + } + cin >> cl; + if (cl <= 0 || cl > 12) { + cout << "error" << endl; + exit(0); + } + for (int i = 0; i < cl; i++) { + cin >> ss[i].id; + ss[i].id = ql(ss[i].id); + } + for (int i = 0; i < cl; i++) { + // 学分和前置课程 + cin >> ss[i].sx >> ss[i].pren; + ss[i].ppren = ss[i].pren; + for (int j = 0; j < ss[i].pren; j++) { + string tmp; + cin >> tmp; + tmp = ql(tmp); + if (tmp.size() == 0) + continue; + int fid = fd(tmp); + if (fid == -1) { + cout << "error" << endl; + exit(0); + } + ss[i].pre[j] = ss[fid].id; + ss[fid].next[ss[fid].nexn++] = ss[i].id; + } + } + // pt(1); + topo(); + // pt(1); + for (int i = 0; i < cl; i++) { + ss[i].xq = xz(i); + } + int ttc = 0; + for (int i = 0; i < se; i++) { + for (int j = 0; j < cl; j++) { + // 判断是否已选择 + if (ss[j].sel == 1) { + continue; + } + // 判断是否学分超过上限 + if (ss[j].sx + fe[i] > sx) { + continue; + } + // 判断是否先修课程已经学习 + int flag = 0; + for (int k = 0; k < ss[j].pren; k++) { + int fid = fd(ss[j].pre[k]); + if (ss[fid].sel == 0) { + flag = 1; + break; + } + } + if (flag == 1) { + continue; + } + // 选择该课程 + ss[j].sel = 1; + fe[i] += ss[j].sx; + re[i][ren[i]++] = j; + ttc++; + } + } + // 判断是否全部选中 + if (ttc != cl) { + cout << "error" << endl; + exit(0); + } + // for (int i = 0; i < cl; i++) { + // cout << ss[i].id << " " << ss[i].xq << endl; + // } + // pte(); + int kind; + cin >> kind; + if (kind != 1 && kind != 2) { + cout << "error" << endl; + exit(0); + } + // 输入分配方式,1是负担均匀,2是尽早学习 + if (kind == 1) { + avgf(); + } else if (kind == 2) { + // 本来就是今早分配的,无需更改 + } else { + cout << "error" << endl; + exit(0); + } + // 输出最终结果 + pte(); + return 0; +} +``` + +## 输入 +平均负载 +``` +6 10 +12 +C01 C02 C03 C04 C05 C06 C07 C08 C09 C10 C11 C12 +2 0 +3 1 C01 +4 2 C01 C02 +3 1 C01 +2 2 C03 C04 +3 1 C11 +4 2 C03 C05 +4 2 C03 C06 +7 0 +5 1 C09 +2 1 C09 +3 3 C01 C09 C10 +1 +``` + +## 输出 +``` +学期 1 学分 9 课程数 2 课程:C01 C09 +学期 2 学分 8 课程数 2 课程:C02 C11 +学期 3 学分 9 课程数 2 课程:C03 C04 +学期 4 学分 8 课程数 2 课程:C05 C10 +学期 5 学分 8 课程数 2 课程:C12 C06 +学期 6 学分 0 课程数 2 课程:C08 C07 +``` + +## 输入2 +尽早学完 +``` +6 10 +12 +C01 C02 C03 C04 C05 C06 C07 C08 C09 C10 C11 C12 +2 0 +3 1 C01 +4 2 C01 C02 +3 1 C01 +2 2 C03 C04 +3 1 C11 +4 2 C03 C05 +4 2 C03 C06 +7 0 +5 1 C09 +2 1 C09 +3 3 C01 C09 C10 +2 +``` + +## 输出2 +``` +学期 1 学分 9 课程数 2 课程:C01 C09 +学期 2 学分 8 课程数 3 课程:C02 C04 C11 +学期 3 学分 9 课程数 2 课程:C10 C03 +学期 4 学分 8 课程数 3 课程:C12 C06 C05 +学期 5 学分 8 课程数 2 课程:C08 C07 +``` + + +# 实验七 二叉排序树与平衡二叉树的实现 +## 问题描述 +分别采用二叉链表和顺序表作存储结构,实现对二叉排序树与平衡二叉树的操作。 + +## 基本要求 +1. 用二叉链表作存储结构实现二叉排序树。 + 1. 以回车符(‘\n’)为输入结束标志,输入数列L,生成一棵二叉排序树T; + 2. 对二叉排序树T作中序遍历,输出结果; + 3. 计算二叉排序树T查找成功的平均查找长度,输出结果; + 4. 输入元素x,查找二叉排序树T,若存在含x的结点,则删除该结点,并作中序遍历(执行操作2);否则,输出信息“无x”; + +2. 用顺序表(一维数组)作存储结构----静态链表 + 1. 以回车符(‘\n’)为输入结束标志,输入数列L,生成一棵二叉排序树T; + 2. 对二叉排序树T作中序遍历,输出结果; + 3. 计算二叉排序树T查找成功的平均查找长度,输出结果; + 4. 输入元素x,查找二叉排序树T,若存在含x的结点,则删除该结点,并作中序遍历(执行操作2);否则,输出信息“无x”; + +3. 用二叉链表作存储结构实平衡的二叉排序树。 + 1. 用数列L,生成平衡的二叉排序树BT:当插入新元素之后,发现当前的二叉排序树BT不是平衡的二叉排序树,则立即将它转换成新的平衡的二叉排序树BT; + 2. 计算平衡的二叉排序树BT的平均查找长度,输出结果。 + +## 代码 +### 二叉链表 +```c +#include +#include + +typedef struct Tree { + int val; + struct Tree *l, *r; + struct Tree *parent; +} Tree; + +int n; +int m; + +void makeTree(Tree *root, int val) { + if (val < root->val) { + if (root->l == NULL) { + root->l = (Tree *)malloc(sizeof(Tree)); + root->l->val = val; + root->l->l = NULL; + root->l->r = NULL; + root->l->parent = root; + } else { + makeTree(root->l, val); + } + } else { + if (root->r == NULL) { + root->r = (Tree *)malloc(sizeof(Tree)); + root->r->val = val; + root->r->l = NULL; + root->r->r = NULL; + root->r->parent = root; + } else { + makeTree(root->r, val); + } + } +} + +// 中序遍历 +void inOrder(Tree *root) { + if (root == NULL) + return; + inOrder(root->l); + printf("%d ", root->val); + inOrder(root->r); +} + +// 二叉排序树T查找成功的平均查找长度 +double ASL(Tree *root, int level) { + if (root == NULL) + return 0; + return level + ASL(root->l, level + 1) + ASL(root->r, level + 1); +} + +// 找到二叉树中值为val的节点并删除 +void delNode(Tree *root, int val) { + if (root == NULL) + return; + if (root->val == val) { + if (root->l == NULL && root->r == NULL) { + if (root->parent->l == root) { + root->parent->l = NULL; + } else { + root->parent->r = NULL; + } + free(root); + } else if (root->l == NULL) { + if (root->parent->l == root) { + root->parent->l = root->r; + } else { + root->parent->r = root->r; + } + free(root); + } else if (root->r == NULL) { + if (root->parent->l == root) { + root->parent->l = root->l; + } else { + root->parent->r = root->l; + } + free(root); + } else { + Tree *p = root->r; + while (p->l != NULL) { + p = p->l; + } + root->val = p->val; + delNode(p, p->val); + } + } else if (root->val > val) { + delNode(root->l, val); + } else { + delNode(root->r, val); + } +} + +void pt(Tree *root) { + printf("inOrder:\n"); + inOrder(root); + printf("\n"); + printf("ASL: %lf\n", ASL(root, 1) / n); + printf("\n"); +} + +int main() { + freopen("data.in", "r", stdin); + scanf("%d", &n); + Tree *root = (Tree *)malloc(sizeof(Tree)); + root->parent = NULL; + for (int i = 0; i < n; i++) { + int val; + scanf("%d", &val); + if (i == 0) { + root->val = val; + root->l = NULL; + root->r = NULL; + } else { + makeTree(root, val); + } + } + pt(root); + + scanf("%d", &m); + for (int i = 0; i < m; i++) { + int x; + scanf("%d", &x); + delNode(root, x); + pt(root); + } + return 0; +} +``` + +#### 输入 +10 +2 5 10 8 7 9 4 6 1 3 +3 +2 +5 +9 + +#### 输出 +``` +inOrder: +1 2 3 4 5 6 7 8 9 10 +ASL: 3.500000 + +inOrder: +1 3 4 5 6 7 8 9 10 +ASL: 3.100000 + +inOrder: +1 3 4 6 7 8 9 10 +ASL: 2.500000 + +inOrder: +1 3 4 6 7 8 10 +ASL: 2.000000 +``` + +### 顺序表 +```c +#include +#include + +int n; +int m; +// 二叉排序树,根节点为 1,左子树为 2 * i,右子树为 2 * i + 1 +int tree[1000]; + +void makeTree(int val) { + int r = 1; + while (1) { + if (val < tree[r]) { + if (tree[2 * r] == 0) { + // printf("r: %d, val: %d\n", 2 * r, val); + tree[2 * r] = val; + break; + } else { + r = 2 * r; + } + } else { + if (tree[2 * r + 1] == 0) { + // printf("r: %d, val: %d\n", 2 * r + 1, val); + tree[2 * r + 1] = val; + break; + } else { + r = 2 * r + 1; + } + } + } +} + +// 中序遍历 +void inOrder(int root) { + if (tree[2 * root] != 0) + inOrder(2 * root); + printf("%d ", tree[root]); + if (tree[2 * root + 1] != 0) + inOrder(2 * root + 1); +} + +// 二叉排序树T查找成功的平均查找长度 +double ASL(int root, int level) { + if (tree[root] == 0) + return 0; + return level + ASL(2 * root, level + 1) + ASL(2 * root + 1, level + 1); +} + +// 找到二叉树中值为val的节点并删除 +void delNode(int root, int x) { + if (tree[root] == 0) + return; + if (tree[root] == x) { + if (tree[2 * root] == 0 && tree[2 * root + 1] == 0) { + tree[root] = 0; + } else if (tree[2 * root] != 0 && tree[2 * root + 1] == 0) { + tree[root] = tree[2 * root]; + tree[2 * root] = 0; + } else if (tree[2 * root] == 0 && tree[2 * root + 1] != 0) { + tree[root] = tree[2 * root + 1]; + tree[2 * root + 1] = 0; + } else { + int r = 2 * root + 1; + while (tree[2 * r] != 0) { + r = 2 * r; + } + tree[root] = tree[r]; + tree[r] = 0; + } + } else if (tree[root] > x) { + delNode(2 * root, x); + } else { + delNode(2 * root + 1, x); + } +} + +void pt() { + printf("inOrder:\n"); + inOrder(1); + printf("\n"); + printf("ASL: %lf\n", ASL(1, 1) / n); + printf("\n"); +} + +int main() { + freopen("data.in", "r", stdin); + scanf("%d", &n); + for (int i = 1; i <= n; i++) { + int val; + scanf("%d", &val); + if (i == 1) { + tree[1] = val; + } else { + makeTree(val); + } + } + pt(); + + scanf("%d", &m); + for (int i = 0; i < m; i++) { + int x; + scanf("%d", &x); + delNode(1, x); + pt(); + } + return 0; +} +``` + +#### 输入 +10 +2 5 10 8 7 9 4 6 1 3 +3 +2 +5 +9 + +#### 输出 +``` +inOrder: +1 2 3 4 5 6 7 8 9 10 +ASL: 3.500000 + +inOrder: +1 3 4 5 6 7 8 9 10 +ASL: 3.100000 + +inOrder: +1 3 4 6 7 8 9 10 +ASL: 2.500000 + +inOrder: +1 3 4 6 7 8 10 +ASL: 2.000000 +``` + +### 平衡二叉树 +```c +#include +#include + +typedef struct Tree { + int val; + struct Tree *l, *r; + struct Tree *parent; + int height; + int balance; +} Tree; + +int n; +int m; + +// 获取节点高度 +int getHeight(Tree *root) { + if (root == NULL) + return 0; + return root->height; +} + +// 更新节点高度和平衡因子 +void updateHeight(Tree *root) { + if (root == NULL) + return; + int l = getHeight(root->l); + int r = getHeight(root->r); + root->height = (l > r ? l : r) + 1; + root->balance = l - r; +} + +// 左旋操作 +Tree *leftRotate(Tree *root) { + Tree *newRoot = root->r; + root->r = newRoot->l; + if (newRoot->l != NULL) + newRoot->l->parent = root; + newRoot->l = root; + newRoot->parent = root->parent; + root->parent = newRoot; + updateHeight(root); + updateHeight(newRoot); + return newRoot; +} + +// 右旋操作 +Tree *rightRotate(Tree *root) { + Tree *newRoot = root->l; + root->l = newRoot->r; + if (newRoot->r != NULL) + newRoot->r->parent = root; + newRoot->r = root; + newRoot->parent = root->parent; + root->parent = newRoot; + updateHeight(root); + updateHeight(newRoot); + return newRoot; +} + +// 左右旋操作 +Tree *leftRightRotate(Tree *root) { + root->l = leftRotate(root->l); + return rightRotate(root); +} + +// 右左旋操作 +Tree *rightLeftRotate(Tree *root) { + root->r = rightRotate(root->r); + return leftRotate(root); +} + +// 插入节点 +Tree *insertNode(Tree *root, int val) { + if (root == NULL) { + Tree *tmp = (Tree *)malloc(sizeof(Tree)); + tmp->val = val; + tmp->l = NULL; + tmp->r = NULL; + tmp->parent = NULL; + tmp->height = 1; + tmp->balance = 0; + return tmp; + } + + if (val < root->val) { + root->l = insertNode(root->l, val); + root->l->parent = root; + } else { + root->r = insertNode(root->r, val); + root->r->parent = root; + } + + updateHeight(root); + if (root->balance > 1) { + if (val < root->l->val) { + return rightRotate(root); + } else { + return leftRightRotate(root); + } + } else if (root->balance < -1) { + if (val > root->r->val) { + return leftRotate(root); + } else { + return rightLeftRotate(root); + } + } + + return root; +} + +// 删除节点 +Tree *deleteNode(Tree *root, int val) { + if (root == NULL) + return NULL; + + if (val < root->val) { + root->l = deleteNode(root->l, val); + } else if (val > root->val) { + root->r = deleteNode(root->r, val); + } else { + if (root->l == NULL && root->r == NULL) { + free(root); + return NULL; + } else if (root->l == NULL) { + Tree *temp = root->r; + free(root); + return temp; + } else if (root->r == NULL) { + Tree *temp = root->l; + free(root); + return temp; + } else { + Tree *minNode = root->r; + while (minNode->l != NULL) + minNode = minNode->l; + root->val = minNode->val; + root->r = deleteNode(root->r, minNode->val); + } + } + + updateHeight(root); + if (root->balance > 1) { + if (getHeight(root->l->l) >= getHeight(root->l->r)) { + return rightRotate(root); + } else { + return leftRightRotate(root); + } + } else if (root->balance < -1) { + if (getHeight(root->r->r) >= getHeight(root->r->l)) { + return leftRotate(root); + } else { + return rightLeftRotate(root); + } + } + + return root; +} + +void makeTree(Tree *root, int val) { + if (val < root->val) { + if (root->l == NULL) { + root->l = (Tree *)malloc(sizeof(Tree)); + root->l->val = val; + root->l->l = NULL; + root->l->r = NULL; + root->l->parent = root; + root->l->height = root->height + 1; + } else { + makeTree(root->l, val); + } + } else { + if (root->r == NULL) { + root->r = (Tree *)malloc(sizeof(Tree)); + root->r->val = val; + root->r->l = NULL; + root->r->r = NULL; + root->r->parent = root; + root->r->height = root->height + 1; + + } else { + makeTree(root->r, val); + } + } +} + +// 中序遍历 +void inOrder(Tree *root) { + if (root == NULL) + return; + inOrder(root->l); + printf("%d ", root->val); + inOrder(root->r); +} + +// 二叉排序树T查找成功的平均查找长度 +double ASL(Tree *root, int level) { + if (root == NULL) + return 0; + return level + ASL(root->l, level + 1) + ASL(root->r, level + 1); +} + +// 找到二叉树中值为val的节点并删除 +void delNode(Tree *root, int val) { + if (root == NULL) + return; + if (root->val == val) { + if (root->l == NULL && root->r == NULL) { + if (root->parent->l == root) { + root->parent->l = NULL; + } else { + root->parent->r = NULL; + } + free(root); + } else if (root->l == NULL) { + if (root->parent->l == root) { + root->parent->l = root->r; + } else { + root->parent->r = root->r; + } + free(root); + } else if (root->r == NULL) { + if (root->parent->l == root) { + root->parent->l = root->l; + } else { + root->parent->r = root->l; + } + free(root); + } else { + Tree *p = root->r; + while (p->l != NULL) { + p = p->l; + } + root->val = p->val; + delNode(p, p->val); + } + } else if (root->val > val) { + delNode(root->l, val); + } else { + delNode(root->r, val); + } +} + +void detail(Tree *root) { + if (root == NULL) + return; + printf("val: %d, height: %d, balance: %d, parent: %d\n", root->val, + root->height, root->balance, + root->parent == NULL ? -1 : root->parent->val); + detail(root->l); + detail(root->r); +} + +void pt(Tree *root) { + printf("inOrder:\n"); + inOrder(root); + printf("\n"); + detail(root); + printf("ASL: %lf\n", ASL(root, 1) / n); + printf("\n"); +} + +// 打印二叉树 +void printTree(Tree *root, int level) { + if (root == NULL) + return; + printTree(root->r, level + 1); + for (int i = 0; i < level; i++) + printf(" "); + printf("%d\n", root->val); + printTree(root->l, level + 1); +} + + +int main() { + freopen("data.in", "r", stdin); + scanf("%d", &n); + Tree *root = NULL; + for (int i = 0; i < n; i++) { + int val; + scanf("%d", &val); + root = insertNode(root, val); + } + pt(root); + + scanf("%d", &m); + for (int i = 0; i < m; i++) { + int x; + scanf("%d", &x); + root = deleteNode(root, x); + pt(root); + printTree(root, 0); + printf("----------------\n"); + } + + return 0; +} +``` + +#### 输入 +10 +2 5 10 8 7 9 4 6 1 3 +3 +2 +5 +9 + +#### 输出 +二叉树图形输出为竖着 +``` +1 2 3 4 5 6 7 8 9 10 +val: 5, height: 4, balance: 0, parent: -1 +val: 2, height: 3, balance: -1, parent: 5 +val: 1, height: 1, balance: 0, parent: 2 +val: 4, height: 2, balance: 1, parent: 2 +val: 3, height: 1, balance: 0, parent: 4 +val: 8, height: 3, balance: 0, parent: 5 +val: 7, height: 2, balance: 1, parent: 8 +val: 6, height: 1, balance: 0, parent: 7 +val: 10, height: 2, balance: 1, parent: 8 +val: 9, height: 1, balance: 0, parent: 10 +ASL: 2.900000 + +inOrder: +1 3 4 5 6 7 8 9 10 +val: 5, height: 4, balance: -1, parent: -1 +val: 3, height: 2, balance: 0, parent: 5 +val: 1, height: 1, balance: 0, parent: 3 +val: 4, height: 1, balance: 0, parent: 3 +val: 8, height: 3, balance: 0, parent: 5 +val: 7, height: 2, balance: 1, parent: 8 +val: 6, height: 1, balance: 0, parent: 7 +val: 10, height: 2, balance: 1, parent: 8 +val: 9, height: 1, balance: 0, parent: 10 +ASL: 2.500000 + + 10 + 9 + 8 + 7 + 6 +5 + 4 + 3 + 1 +---------------- +inOrder: +1 3 4 6 7 8 9 10 +val: 6, height: 4, balance: -1, parent: -1 +val: 3, height: 2, balance: 0, parent: 6 +val: 1, height: 1, balance: 0, parent: 3 +val: 4, height: 1, balance: 0, parent: 3 +val: 8, height: 3, balance: -1, parent: 6 +val: 7, height: 1, balance: 0, parent: 8 +val: 10, height: 2, balance: 1, parent: 8 +val: 9, height: 1, balance: 0, parent: 10 +ASL: 2.100000 + + 10 + 9 + 8 + 7 +6 + 4 + 3 + 1 +---------------- +inOrder: +1 3 4 6 7 8 10 +val: 6, height: 3, balance: 0, parent: -1 +val: 3, height: 2, balance: 0, parent: 6 +val: 1, height: 1, balance: 0, parent: 3 +val: 4, height: 1, balance: 0, parent: 3 +val: 8, height: 2, balance: 0, parent: 6 +val: 7, height: 1, balance: 0, parent: 8 +val: 10, height: 1, balance: 0, parent: 8 +ASL: 1.700000 + + 10 + 8 + 7 +6 + 4 + 3 + 1 +---------------- +``` + +# 实验九 学生成绩分析 +## 问题描述 +录入、保存一个班级学生多门课程的成绩,并对成绩进行分析。 +## 基本要求 +1. 通过键盘输入各学生的多门课程的成绩,建立相应的文件input.dat。 +2. 对文件 input.dat 中的数据进行处理,要求具有如下功能: + 1. 按各门课程成绩排序,并生成相应的文件输出。 + 2. 计算每人的平均成绩,按平均成绩排序,并生成文件。 + 3. 求出各门课程的平均成绩、最高分、最低分、不及格人数、60-69分人数、70-79分人数、 80-89分人数、90分以上人数。 + 4. 根据姓名或学号查询某人的各门成绩,重名情况也能处理。 + +## 代码 +```c +#include +#include +#include + +typedef struct { + char *uid; + char *name; + int math, eng, cs; +} Score; + +// 保存到文件 +void save_to_file(Score **scores, int n, char *file_name) { + FILE *fp = fopen(file_name, "w"); + fprintf(fp, "%d\n", n); + for (int i = 0; i < n; i++) { + fprintf(fp, "%s %s %d %d %d\n", scores[i]->uid, scores[i]->name, + scores[i]->math, scores[i]->eng, scores[i]->cs); + } + fclose(fp); +} + +int main() { + + Score *scores[105]; + int n; + // 如果 score.dat 不存在,就从键盘输入 + FILE *fp = fopen("score.dat", "r"); + if (fp == NULL) { + printf("score.dat not found, please input:\n"); + scanf("%d", &n); + fp = fopen("score.dat", "w"); + fprintf(fp, "%d\n", n); + for (int i = 0; i < n; i++) { + scores[i] = (Score *)malloc(sizeof(Score)); + char *uid = (char *)malloc(sizeof(char) * 105); + char *name = (char *)malloc(sizeof(char) * 105); + scanf("%s %s %d %d %d", uid, name, &scores[i]->math, + &scores[i]->eng, &scores[i]->cs); + scores[i]->uid = uid; + scores[i]->name = name; + fprintf(fp, "%s %s %d %d %d\n", scores[i]->uid, scores[i]->name, + scores[i]->math, scores[i]->eng, scores[i]->cs); + } + fclose(fp); + } else { + // 从 score.dat 读取 + fscanf(fp, "%d", &n); + for (int i = 0; i < n; i++) { + scores[i] = (Score *)malloc(sizeof(Score)); + char *uid = (char *)malloc(sizeof(char) * 105); + char *name = (char *)malloc(sizeof(char) * 105); + fscanf(fp, "%s %s %d %d %d", uid, name, &scores[i]->math, + &scores[i]->eng, &scores[i]->cs); + scores[i]->uid = uid; + scores[i]->name = name; + } + fclose(fp); + } + + printf("Total: %d\n", n); + + printf("当前数据:\n"); + // 输出所有人的信息 + for (int i = 0; i < n; i++) { + printf("%s %s %d %d %d\n", scores[i]->uid, scores[i]->name, + scores[i]->math, scores[i]->eng, scores[i]->cs); + } + printf("--------------------\n"); + + // 按照每个科目排序并输出对应的排名 + printf("按照数学成绩排序:\n"); + for (int i = 0; i < n; i++) { + int max = i; + for (int j = i + 1; j < n; j++) { + if (scores[j]->math > scores[max]->math) { + max = j; + } + } + Score *tmp = scores[i]; + scores[i] = scores[max]; + scores[max] = tmp; + printf("%s %s %d %d %d\n", scores[i]->uid, scores[i]->name, + scores[i]->math, scores[i]->eng, scores[i]->cs); + } + save_to_file(scores, n, "math.dat"); + printf("--------------------\n"); + + printf("按照英语成绩排序:\n"); + for (int i = 0; i < n; i++) { + int max = i; + for (int j = i + 1; j < n; j++) { + if (scores[j]->eng > scores[max]->eng) { + max = j; + } + } + Score *tmp = scores[i]; + scores[i] = scores[max]; + scores[max] = tmp; + printf("%s %s %d %d %d\n", scores[i]->uid, scores[i]->name, + scores[i]->math, scores[i]->eng, scores[i]->cs); + } + save_to_file(scores, n, "eng.dat"); + printf("--------------------\n"); + + printf("按照计算机成绩排序:\n"); + for (int i = 0; i < n; i++) { + int max = i; + for (int j = i + 1; j < n; j++) { + if (scores[j]->cs > scores[max]->cs) { + max = j; + } + } + Score *tmp = scores[i]; + scores[i] = scores[max]; + scores[max] = tmp; + printf("%s %s %d %d %d\n", scores[i]->uid, scores[i]->name, + scores[i]->math, scores[i]->eng, scores[i]->cs); + } + save_to_file(scores, n, "cs.dat"); + printf("--------------------\n"); + + // 计算平均分,按平均成绩排序,写到 average.dat + printf("平均分:\n"); + FILE *fp_average = fopen("average.dat", "w"); + fprintf(fp_average, "%d\n", n); + double avg_scores[105]; + for (int i = 0; i < n; i++) { + avg_scores[i] = + (scores[i]->math + scores[i]->eng + scores[i]->cs) / 3.0; + } + // sort + for (int i = 0; i < n; i++) { + int max = i; + for (int j = i + 1; j < n; j++) { + if (avg_scores[j] > avg_scores[max]) { + max = j; + } + } + int tmp = avg_scores[i]; + avg_scores[i] = avg_scores[max]; + avg_scores[max] = tmp; + Score *tmp_score = scores[i]; + scores[i] = scores[max]; + scores[max] = tmp_score; + } + for (int i = 0; i < n; i++) { + printf("%s %s %d %d %d %.2f\n", scores[i]->uid, scores[i]->name, + scores[i]->math, scores[i]->eng, scores[i]->cs, + avg_scores[i] * 1.0); + fprintf(fp_average, "%s %s %d %d %d %.2f\n", scores[i]->uid, + scores[i]->name, scores[i]->math, scores[i]->eng, scores[i]->cs, + avg_scores[i] * 1.0); + } + fclose(fp_average); + printf("--------------------\n"); + + // 求出各门课程的平均成绩、最高分、最低分、不及格人数、60-69分人数、70-79分人数、 + // 80-89分人数、90分以上人数。 + int math_sum = 0, eng_sum = 0, cs_sum = 0; + int math_max = 0, eng_max = 0, cs_max = 0; + int math_min = 100, eng_min = 100, cs_min = 100; + int math_fail = 0, eng_fail = 0, cs_fail = 0; + int math_60_69 = 0, eng_60_69 = 0, cs_60_69 = 0; + int math_70_79 = 0, eng_70_79 = 0, cs_70_79 = 0; + int math_80_89 = 0, eng_80_89 = 0, cs_80_89 = 0; + int math_90 = 0, eng_90 = 0, cs_90 = 0; + for (int i = 0; i < n; i++) { + math_sum += scores[i]->math; + eng_sum += scores[i]->eng; + cs_sum += scores[i]->cs; + if (scores[i]->math > math_max) { + math_max = scores[i]->math; + } + if (scores[i]->eng > eng_max) { + eng_max = scores[i]->eng; + } + if (scores[i]->cs > cs_max) { + cs_max = scores[i]->cs; + } + if (scores[i]->math < math_min) { + math_min = scores[i]->math; + } + if (scores[i]->eng < eng_min) { + eng_min = scores[i]->eng; + } + if (scores[i]->cs < cs_min) { + cs_min = scores[i]->cs; + } + if (scores[i]->math < 60) { + math_fail++; + } + if (scores[i]->eng < 60) { + eng_fail++; + } + if (scores[i]->cs < 60) { + cs_fail++; + } + if (scores[i]->math >= 60 && scores[i]->math <= 69) { + math_60_69++; + } + if (scores[i]->eng >= 60 && scores[i]->eng <= 69) { + eng_60_69++; + } + if (scores[i]->cs >= 60 && scores[i]->cs <= 69) { + cs_60_69++; + } + if (scores[i]->math >= 70 && scores[i]->math <= 79) { + math_70_79++; + } + if (scores[i]->eng >= 70 && scores[i]->eng <= 79) { + eng_70_79++; + } + if (scores[i]->cs >= 70 && scores[i]->cs <= 79) { + cs_70_79++; + } + if (scores[i]->math >= 80 && scores[i]->math <= 89) { + math_80_89++; + } + if (scores[i]->eng >= 80 && scores[i]->eng <= 89) { + eng_80_89++; + } + if (scores[i]->cs >= 80 && scores[i]->cs <= 89) { + cs_80_89++; + } + if (scores[i]->math >= 90) { + math_90++; + } + if (scores[i]->eng >= 90) { + eng_90++; + } + if (scores[i]->cs >= 90) { + cs_90++; + } + } + printf("数学:\n平均分:%.2f 最高分:%d 最低分:%d 不及格人数:%d " + "60-69分人数:%d " + "70-79分人数:%d 80-89分人数:%d 90分以上人数:%d\n", + math_sum * 1.0 / n, math_max, math_min, math_fail, math_60_69, + math_70_79, math_80_89, math_90); + printf("英语:\n平均分:%.2f 最高分:%d 最低分:%d 不及格人数:%d " + "60-69分人数:%d " + "70-79分人数:%d 80-89分人数:%d 90分以上人数:%d\n", + eng_sum * 1.0 / n, eng_max, eng_min, eng_fail, eng_60_69, eng_70_79, + eng_80_89, eng_90); + printf("计算机:\n平均分:%.2f 最高分:%d 最低分:%d 不及格人数:%d " + "60-69分人数:%d " + "70-79分人数:%d 80-89分人数:%d 90分以上人数:%d\n", + cs_sum * 1.0 / n, cs_max, cs_min, cs_fail, cs_60_69, cs_70_79, + cs_80_89, cs_90); + + // 根据姓名或学号查询某人的各门成绩,重名情况也能处理。 + while (1) { + printf("请输入学号或姓名:\n"); + char *query = (char *)malloc(sizeof(char) * 105); + scanf("%s", query); + int found = 0; + for (int i = 0; i < n; i++) { + if (strcmp(scores[i]->uid, query) == 0 || + strcmp(scores[i]->name, query) == 0) { + printf("%s %s %d %d %d\n", scores[i]->uid, scores[i]->name, + scores[i]->math, scores[i]->eng, scores[i]->cs); + found = 1; + } + } + if (!found) { + printf("未找到\n"); + } + } + return 0; +} +``` + +## 输入 +``` +7 +001 L1 78 77 90 +002 L2 89 67 88 +003 L3 56 66 78 +004 L4 89 86 85 +005 L5 67 88 76 +006 L6 45 54 67 +007 L6 78 76 70 +``` + +## 输出 +``` +Total: 7 +当前数据: +001 L1 78 77 90 +002 L2 89 67 88 +003 L3 56 66 78 +004 L4 89 86 85 +005 L5 67 88 76 +006 L6 45 54 67 +007 L6 78 76 70 +-------------------- +按照数学成绩排序: +002 L2 89 67 88 +004 L4 89 86 85 +001 L1 78 77 90 +007 L6 78 76 70 +005 L5 67 88 76 +003 L3 56 66 78 +006 L6 45 54 67 +-------------------- +按照英语成绩排序: +005 L5 67 88 76 +004 L4 89 86 85 +001 L1 78 77 90 +007 L6 78 76 70 +002 L2 89 67 88 +003 L3 56 66 78 +006 L6 45 54 67 +-------------------- +按照计算机成绩排序: +001 L1 78 77 90 +002 L2 89 67 88 +004 L4 89 86 85 +003 L3 56 66 78 +005 L5 67 88 76 +007 L6 78 76 70 +006 L6 45 54 67 +-------------------- +平均分: +004 L4 89 86 85 86.67 +002 L2 89 67 88 81.00 +001 L1 78 77 90 81.00 +005 L5 67 88 76 77.00 +007 L6 78 76 70 74.67 +003 L3 56 66 78 66.00 +006 L6 45 54 67 55.00 +-------------------- +数学: +平均分:71.71 最高分:89 最低分:45 不及格人数:2 60-69分人数:1 70-79分人数:2 80-89分人数:2 90分以上人数:0 +英语: +平均分:73.43 最高分:88 最低分:54 不及格人数:1 60-69分人数:2 70-79分人数:2 80-89分人数:2 90分以上人数:0 +计算机: +平均分:79.14 最高分:90 最低分:67 不及格人数:0 60-69分人数:1 70-79分人数:3 80-89分人数:2 90分以上人数:1 +请输入学号或姓名: +L1 +001 L1 78 77 90 +请输入学号或姓名: +L2 +002 L2 89 67 88 +请输入学号或姓名: +L6 +007 L6 78 76 70 +006 L6 45 54 67 +> EOF < +``` + +# 实验十 一元稀疏多项式计算器 +## 问题分析 +这是一个一元稀疏多项式的简单计算器,需要设计一种数据结构来存储多项式,并实现多项式的输入、输出、加法、减法和计算多项式在某个点的值的功能。 + +## 解决方案 +采用带表头结点的单链表来存储多项式。链表的每个节点表示多项式的一项,节点包含两个成员变量:系数和指数。 + +1. 输入并建立多项式:从用户输入中获取系数和指数,并依次插入链表中。插入时需要保持链表的有序性(按指数降序排列),可以采用插入排序的方法。 +2. 输出多项式:遍历链表,按格式输出多项式的每一项。 +3. 多项式相加:遍历两个链表,同时比较节点的指数大小,按照大小关系进行处理。如果两个指数相等,则将系数相加并创建新的节点插入到结果链表中;如果一个指数较大,则将该项直接插入结果链表中;如果一个指数较小,则将该项的系数取负数后插入结果链表中。 +4. 多项式相减:与多项式相加类似,不同的是需要将第二个多项式的所有系数取负数。 +5. 计算多项式在某个点的值:遍历链表,依次将每一项的系数乘以x的指数次方,并累加到结果中。 + +## 代码 +```c +#include +#include +#include + +typedef struct Node { + int c, e; + struct Node *next; +} Node; + +typedef struct List { + Node head; + int len; +} List; + +// 初始化链表 +List *init_list() { + List *l = (List *)malloc(sizeof(List)); + l->head.next = NULL; + l->len = 0; + return l; +} + +// 降序插入 +void insert(List *l, int c, int e) { + Node *p = &(l->head); + while (p->next && p->next->e > e) { + p = p->next; + } + if (p->next && p->next->e == e) { + p->next->c += c; + if (p->next->c == 0) { + Node *q = p->next; + p->next = q->next; + free(q); + l->len--; + } + } else { + Node *q = (Node *)malloc(sizeof(Node)); + q->c = c, q->e = e; + q->next = p->next; + p->next = q; + l->len++; + } +} + +// 只输出 +void output(List *l) { + Node *p = l->head.next; + printf("%d,", l->len); + while (p) { + printf("%d,%d", p->c, p->e); + p = p->next; + if (p) + printf(","); + } + printf("\n"); +} + +// 只输出 +void plus(List *l1, List *l2) { + Node *p1 = l1->head.next, *p2 = l2->head.next; + while (p1 && p2) { + if (p1->e > p2->e) { + printf("%d,%d,", p1->c, p1->e); + p1 = p1->next; + } else if (p1->e < p2->e) { + printf("%d,%d,", p2->c, p2->e); + p2 = p2->next; + } else { + printf("%d,%d,", p1->c + p2->c, p1->e); + p1 = p1->next; + p2 = p2->next; + } + } + while (p1) { + printf("%d,%d,", p1->c, p1->e); + p1 = p1->next; + } + while (p2) { + printf("%d,%d,", p2->c, p2->e); + p2 = p2->next; + } + printf("\n"); +} + +// 只输出 +void minus(List *l1, List *l2) { + Node *p1 = l1->head.next, *p2 = l2->head.next; + while (p1 && p2) { + if (p1->e > p2->e) { + printf("%d,%d,", p1->c, p1->e); + p1 = p1->next; + } else if (p1->e < p2->e) { + printf("%d,%d,", -p2->c, p2->e); + p2 = p2->next; + } else { + printf("%d,%d,", p1->c - p2->c, p1->e); + p1 = p1->next; + p2 = p2->next; + } + } + while (p1) { + printf("%d,%d,", p1->c, p1->e); + p1 = p1->next; + } + while (p2) { + printf("%d,%d,", -p2->c, p2->e); + p2 = p2->next; + } + printf("\n"); +} + +int calc(List *l, int x) { + int sum = 0; + Node *p = l->head.next; + while (p) { + sum += p->c * pow(x, p->e); + p = p->next; + } + return sum; +} + +int main() { + // freopen("data.in", "r", stdin); + List *l = init_list(); + List *l2 = init_list(); + int n1, n2, c, e; + scanf("%d", &n1); + for (int i = 0; i < n1; i++) { + scanf("%d%d", &c, &e); + insert(l, c, e); + } + scanf("%d", &n2); + for (int i = 0; i < n2; i++) { + scanf("%d%d", &c, &e); + insert(l2, c, e); + } + output(l); + output(l2); + plus(l, l2); + minus(l, l2); + while (1) { + printf("请输入x的值:"); + scanf("%d", &c); + if (c == -1) + break; + printf("%d\n", calc(l, c)); + } + return 0; +} +``` + +## 输入 +``` +4 +1 2 +5 2 +3 4 +666 9 +2 +1 3 +2 4 +``` + +## 输出 +``` +3,666,9,3,4,6,2 +2,2,4,1,3 +666,9,5,4,1,3,6,2, +666,9,1,4,-1,3,6,2, +请输入x的值:1 +675 +请输入x的值:2 +341064 +请输入x的值:3 +13109175 +``` + +# 实验十三 迷宫问题 +## 问题描述: +迷宫实验是取自心理学的一个古典实验。在该实验中,把一只老鼠从一个无顶大盒子的门放入,在盒中设置了许多墙,对行进方向形成了多处阻挡。盒子仅有一个出口,在出口处放置一块奶酪,吸引老鼠在迷宫中寻找道路以到达出口。对同一只老鼠重复进行上述实验,一直到老鼠从入口到出口,而不走错一步。老鼠经多次试验终于得到它学习走迷宫的路线。 +## 设计功能要求: +迷宫由m行n列的二维数组设置,0表示无障碍,1表示有障碍。设入口为(1,1),出口为(m,n),每次只能从一个无障碍单元移到周围四个方向上任一无障碍单元。编程实现对任意设定的迷宫,求出一条从入口到出口的通路,或得出没有通路的结论。 +算法输入:代表迷宫入口的坐标 +算法输出:穿过迷宫的结果。 算法要点:创建迷宫,试探法查找路。 + +## 思路 +1. 用二维数组存储迷宫,0表示无障碍,1表示有障碍。 +2. 从入口开始,按照顺时针方向依次尝试,如果能走通就继续,否则回溯。 +3. 如果走到出口,就输出路径。 +4. 如果回溯到入口,就说明没有路径。 + +## 代码 +```c +#include +#include +#include + +int m, n; +int maze[105][105]; + +// 存储当前路径 +char *path[10005]; +int path_len = 0; + +void dfs(int x, int y) { + if (x == m && y == n) { + // 找到出口 + for (int i = 0; i < path_len; i++) { + printf("%s", path[i]); + if (i != path_len - 1) { + printf("->"); + } + } + printf("\n"); + return; + } + maze[x][y] = 1; + // 四个方向依次尝试 + if (x + 1 <= m && maze[x + 1][y] == 0) { + path[path_len] = (char *)malloc(sizeof(char) * 105); + sprintf(path[path_len++], "(%d, %d)", x + 1, y); + dfs(x + 1, y); + path_len--; + } + if (y + 1 <= n && maze[x][y + 1] == 0) { + path[path_len] = (char *)malloc(sizeof(char) * 105); + sprintf(path[path_len++], "(%d, %d)", x, y + 1); + dfs(x, y + 1); + path_len--; + } + if (x - 1 >= 1 && maze[x - 1][y] == 0) { + path[path_len] = (char *)malloc(sizeof(char) * 105); + sprintf(path[path_len++], "(%d, %d)", x - 1, y); + dfs(x - 1, y); + path_len--; + } + if (y - 1 >= 1 && maze[x][y - 1] == 0) { + path[path_len] = (char *)malloc(sizeof(char) * 105); + sprintf(path[path_len++], "(%d, %d)", x, y - 1); + dfs(x, y - 1); + path_len--; + } +} + +int main() { + freopen("data.in", "r", stdin); + scanf("%d%d", &m, &n); + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + scanf("%d", &maze[i][j]); + } + } + path[path_len] = (char *)malloc(sizeof(char) * 105); + strcpy(path[path_len++], "(1, 1)"); + dfs(1, 1); + + return 0; +} +``` + +## 输入 +``` +7 6 +0 0 1 1 0 0 +1 0 0 0 0 0 +1 0 0 0 0 1 +1 0 0 0 0 1 +1 1 0 0 0 0 +1 1 1 1 0 0 +``` + +## 输出 +``` +(1, 1)->(1, 2)->(2, 2)->(3, 2)->(4, 2)->(4, 3)->(5, 3)->(5, 4)->(5, 5)->(6, 5)->(7, 5)->(7, 6) +(1, 1)->(1, 2)->(2, 2)->(3, 2)->(4, 2)->(4, 3)->(5, 3)->(5, 4)->(5, 5)->(6, 5)->(6, 6)->(7, 6) +``` + +# 实验感悟 +通过本学期的数据结构实验, 我对数据结构有了更深的理解, 也对C语言有了更深的理解. 通过实验, 我学会了如何使用C和C++语言来实现各种数据结构, 也学会了如何使用各种数据结构来解决实际问题. + +# 更多 +Qt 版本的电子表格等待制作中, 预计在假期尝试. \ No newline at end of file