2024西工大数据结构实验(C)
11 道无聊题,什么时候实验课能出一些有意思的题目。
1 线性表
1.1 合并有序数组
经典题目,双指针法,有序链表类似。
#include <stdio.h>
int main () {
int n, m;
scanf("%d", &n);
int arr1[n];
for (int i = 0; i < n; ++i) scanf("%d", &arr1[i]);
scanf("%d", &m);
int arr2[m];
for (int i = 0; i < m; ++i) scanf("%d", &arr2[i]);
int arr[n + m], head1 = 0, head2 = 0, head = 0;
while (head1 < n && head2 < m) {
if (arr1[head1] < arr2[head2]) arr[head++] = arr1[head1++];
else arr[head++] = arr2[head2++];
}
while (head1 < n) arr[head++] = arr1[head1++];
while (head2 < m) arr[head++] = arr2[head2++];
for (int i = 0; i < n + m; ++i) printf("%d\n", arr[i]);
return 0;
}
1.2 高精度计算 Pi 值
考察高精度
a
x
ax
ax,
x
a
\frac{x}{a}
ax,
x
+
y
x+y
x+y。数组就能做,题目要求用双向链表,闲得蛋疼。
考虑级数:
π
=
∑
i
=
0
∞
R
(
n
)
\pi=\sum_{i=0}^\infty R(n)
π=∑i=0∞R(n),
R
(
n
+
1
)
=
n
R
(
n
)
2
n
+
1
R(n+1)=\frac{nR(n)}{2n+1}
R(n+1)=2n+1nR(n),
R
(
1
)
=
2
R(1)=2
R(1)=2.
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int val;
struct node *prev, *next;
} Node, List;
List* init(void) {
Node *s = (Node*) malloc(sizeof(Node));
s->val = -1, s->prev = s->next = s;
return s;
}
void pushBack(List *head, int val) {
Node *curr = (Node*) malloc(sizeof(Node));
curr->val = val, curr->prev = head->prev, curr->next = head;
head->prev = head->prev->next = curr;
}
List* createList(size_t size, int val) {
List *s = init();
for (int i = 0; i < size; ++i) pushBack(s, val);
return s;
}
void traverse_n(List *head, size_t cnt) {
printf("%d.", head->next->val);
for (Node* curr = head->next->next; curr != head && cnt > 0; curr = curr->next, --cnt)
printf("%d", curr->val);
printf("\n");
}
int main () {
List *Rn = createList(550, 0);
List *Sn = createList(550, 0);
Rn->next->val = 2, Sn->next->val = 2;
for (int n = 1; n <= 2000; ++n) {
// R(n) = R(n) * n
int carry = 0;
for (Node *curr = Rn->prev; curr != Rn; curr = curr->prev) {
int res = curr->val * n + carry;
carry = res / 10; curr->val = res % 10;
}
// R(n) = R(n) / (2n + 1)
carry = 0;
for (Node *curr = Rn->next; curr != Rn; curr = curr->next) {
int div = (n << 1) + 1;
int res = curr->val + carry * 10;
curr->val = res / div; carry = res - curr->val * div;
}
// S(n) += R(n)
carry = 0;
for (Node *curr1 = Sn->prev, *curr2 = Rn->prev; curr1 != Sn && curr2 != Rn;
curr1 = curr1->prev, curr2 = curr2->prev) {
int res = curr1->val + curr2->val + carry;
curr1->val = res % 10; carry = res / 10;
}
}
int cnt;
scanf("%d", &cnt);
traverse_n(Sn, cnt);
return 0;
}
2 广义表
2.1 稀疏矩阵转置
交换行与列后插入排序(稳定排序算法,不改变原有行顺序——即交换后列顺序)。
#include <stdio.h>
#define MAXSIZE 400
int S[MAXSIZE][3] = {};
void transpose(int cnt) {
for (int i = 0; i < cnt; ++i) {
int tmp = S[i][0];
S[i][0] = S[i][1], S[i][1] = tmp;
}
// insertion: stable
for (int i = 1; i < cnt; ++i) {
int raw = S[i][0], col = S[i][1], val = S[i][2];
int j = i - 1;
while ((j >= 0) && raw < S[0][j]) {
S[j + 1][0] = S[j][0], S[j + 1][1] = S[j][1], S[j + 1][2] = S[j][2];
--j;
}
S[j + 1][0] = raw, S[j + 1][1] = col, S[j + 1][2] = val;
}
}
int main () {
int n, m;
scanf("%d %d", &n, &m);
int cnt = 0;
while(scanf("%d %d %d", &S[cnt][0], &S[cnt][1], &S[cnt][2])) {
if (S[cnt][0] == 0) break;
++cnt;
}
transpose(cnt);
for (int i = 0; i < cnt; ++i)
printf("%d %d %d\n", S[i][0], S[i][1], S[i][2]);
return 0;
}
2.2 稀疏矩阵加法
双指针法就是好用啊。
#include <stdio.h>
int main () {
int n, m, t1, t2;
scanf("%d %d %d %d", &n, &m, &t1, &t2);
int S1[t1][3] = {}, S2[t2][3] = {}, S[t1 + t2][3] = {};
for (int i = 0; i < t1; ++i)
scanf("%d %d %d", &S1[i][0], &S1[i][1], &S1[i][2]);
for (int i = 0; i < t2; ++i)
scanf("%d %d %d", &S2[i][0], &S2[i][1], &S2[i][2]);
int h1 = 0, h2 = 0, h = 0;
while (h1 < t1 && h2 <t2) {
if (S1[h1][0] < S2[h2][0]) {
S[h][0] = S1[h1][0], S[h][1] = S1[h1][1], S[h][2] = S1[h1][2];
++h1, ++h;
}
else if (S1[h1][0] > S2[h2][0]) {
S[h][0] = S2[h2][0], S[h][1] = S2[h2][1], S[h][2] = S2[h2][2];
++h2, ++h;
}
else {
if (S1[h1][1] < S2[h2][1]) {
S[h][0] = S1[h1][0], S[h][1] = S1[h1][1], S[h][2] = S1[h1][2];
++h1, ++h;
}
else if (S1[h1][1] > S2[h2][1]) {
S[h][0] = S2[h2][0], S[h][1] = S2[h2][1], S[h][2] = S2[h2][2];
++h2, ++h;
}
else {
S[h][0] = S1[h1][0], S[h][1] = S1[h1][1], S[h][2] = S1[h1][2] + S2[h2][2];
++h1, ++h2, ++h;
}
}
}
for (int i = 0; i < t1 + t2 && S[i][0]; ++i)
printf("%d %d %d\n", S[i][0], S[i][1], S[i][2]);
return 0;
}
2.3 稀疏矩阵加法(十字链表)
十字链表纯纯炫技的(循环链表套娃),唯一优点是写起来很麻烦可以锻炼耐心(嗯,优点)。
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int raw, col, val;
struct Node *down, *right;
} Node, CList;
CList *createCList(int raw, int col) {
CList *c = (CList*) malloc(sizeof(CList));
c->raw = raw, c->col = col, c->val = -1;
c->down = c->right = c;
for (int i = raw; i > 0; --i) {
Node *tmp = (Node*) malloc(sizeof(Node));
tmp->val = -1, tmp->raw = i, tmp->col = 0;
tmp->right = tmp, tmp->down = c->down, c->down = tmp;
}
for (int i = col; i > 0; --i) {
Node *tmp = (Node*) malloc(sizeof(Node));
tmp->val = -1, tmp->raw = 0, tmp->col = i;
tmp->down = tmp, tmp->right = c->right, c->right = tmp;
}
return c;
}
void insertOrAdd(CList *head, int raw, int col, int val) {
Node *node = (Node*) malloc(sizeof(Node)), *curr = head;
for (int i = 1; i <= raw; ++i) curr = curr->down;
while (curr->right->col < col && curr->right->col != 0) curr = curr->right;
// 狠狠地偷懒, 把插入和加法逻辑写在一起, 不需要额外写加法了.
if (curr->right->col == col) {
curr->right->val += val;
free(node);
return;
}
node->right = curr->right, curr->right = node;
curr = head;
for (int i = 1; i <= col; ++i) curr = curr->right;
while (curr->down->raw < raw && curr->down->raw != 0) curr = curr->down;
node->down = curr->down, curr->down = node;
node->raw = raw, node->col = col, node->val = val;
}
void traverse(CList *S) {
for (Node *r = S->down; r != S; r = r->down) {
for (Node *c = r->right; c != r; c = c->right) {
printf("%d %d %d\n", c->raw, c->col, c->val);
}
}
}
int main () {
int n, m, t1, t2, r, c, v;
scanf("%d %d %d %d", &n, &m, &t1, &t2);
CList *S1 = createCList(n, m);
for (int i = t1; i > 0 ; --i) {
scanf("%d %d %d", &r, &c, &v);
insertOrAdd(S1, r, c, v);
}
for (int i = t2; i > 0 ; --i) {
scanf("%d %d %d", &r, &c, &v);
insertOrAdd(S1, r, c, v);
}
traverse(S1);
return 0;
}
2.4 稀疏矩阵乘法
双指针还要写回溯,不如直接遍历了。
// multiplication of spare matrix
#include <stdio.h>
#define MAXSIZE 400
int S1[MAXSIZE][3] = {};
int S2[MAXSIZE][3] = {};
int S[MAXSIZE][3] = {};
int main () {
int r1, c1, r2, c2, t1 = 0, t2 = 0, sum = 0, h = 0;
scanf("%d %d", &r1, &c1);
while(scanf("%d %d %d", &S1[t1][0], &S1[t1][1], &S1[t1][2])) {
if (S1[t1][0] == 0) break;
++t1;
}
scanf("%d %d", &r2, &c2);
while(scanf("%d %d %d", &S2[t2][0], &S2[t2][1], &S2[t2][2])) {
if (S2[t2][0] == 0) break;
++t2;
}
for (int i = 1; i <= r1; ++i) {
for (int j = 1; j <= c2; ++j) {
for (int h1 = 0; h1 <= t1; ++h1) {
for (int h2 = 0; h2 <= t2; ++h2) {
if (S1[h1][0] == i && S2[h2][1] == j && S1[h1][1] == S2[h2][0])
sum += S1[h1][2] * S2[h2][2];
}
}
if (!sum) continue;
S[h][0] = i, S[h][1] = j, S[h][2] = sum; sum = 0; ++h;
}
}
for (int i = 0; i < h; ++i)
printf("%d %d %d\n", S[i][0], S[i][1], S[i][2]);
return 0;
}
3 树
3.1 哈夫曼编码器
贪心策略构建哈夫曼树,叶节点上溯得到逆序编码。
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <limits.h>
#include <stdlib.h>
typedef struct {
int ch, weight;
int parent, left, right;
} HTree;
typedef struct {
bool bit [128];
char ch, begin;
} HCode;
HTree *createHTree(int n) {
HTree *ht = (HTree*) calloc(2 * n, sizeof(HTree));
// 限制于多种因素, 只能采用这种读入方式.
int tmp = 1;
while(tmp <= n) {
char ch = getchar();
if (ch != ' ' && ch != '\n') ht[tmp++].ch = ch;
}
for (int i = 1; i <= n; ++i) scanf("%d", &ht[i].weight);
int min1, min2, rn, ln;
for (int i = n + 1; i <= 2 * n - 1; ++i) {
min1 = min2 = INT_MAX; rn = ln = 0;
for (int j = 1; j <= i - 1; ++j) {
if (ht[j].weight < min1 && !ht[j].parent)
min2 = min1, rn = ln, min1 = ht[j].weight, ln = j;
else if (ht[j].weight < min2 && !ht[j].parent)
min2 = ht[j].weight, rn = j;
}
ht[ln].parent = ht[rn].parent = i;
ht[i].left = ln, ht[i].right = rn;
ht[i].weight = ht[ln].weight + ht[rn].weight;
}
return ht;
}
HCode *createHCode(HTree *ht, int n) {
HCode *hc = (HCode*) calloc(n + 1, sizeof(HCode));
for (int i = 1; i <= n; ++i) {
hc[i].begin = n; hc[i].ch = ht[i].ch;
int cn = i, pn = ht[cn].parent;
while (pn) {
if (ht[pn].left == cn) hc[i].bit[hc[i].begin] = 0;
else hc[i].bit[hc[i].begin] = 1;
--hc[i].begin, cn = pn, pn = ht[cn].parent;
}
++hc[i].begin;
}
return hc;
}
void encode(HCode *hc, int n) {
char text[1001] = "";
scanf("%s", text);
for (int i = 0; i < strlen(text); ++i) {
for (int j = 1; j <= n; ++j) {
if (text[i] == hc[j].ch) {
for (int k = hc[j].begin; k <= n; ++k) {
printf("%d", hc[j].bit[k]);
}
}
}
}
// 什么? 你问为什么不写译码? 别问, 问就是偷懒.
// 要是能用cpp字典就写译码了.
printf("\n%s\n", text);
}
int main () {
int n;
scanf("%d", &n);
HTree *ht = createHTree(n);
HCode *hc = createHCode(ht, n);
encode(hc, n);
return 0;
}
4 图
4.1 Dijkstra 最短路径长度
从一个顶点开始逐次找路径最短的下一个节点并更新最短路径长度表。
#include <stdio.h>
#include <stdbool.h>
#define MAXSIZE 105
#define INF 10000
int vn = 0;
int dist[MAXSIZE][MAXSIZE] = {};
bool isVisited[MAXSIZE] = {};
int len[MAXSIZE] = {};
void init(void) {
scanf("%d", &vn);
for (int i = 0; i < vn; ++i)
for (int j = 0; j < vn; ++j)
scanf("%d", &dist[i][j]);
}
void dijkstra(void) {
for (int i = 0; i < vn; ++i) len[i] = INF;
isVisited[0] = true; len[0] = 0;
for (int i = 0; i < vn; ++i)
if (dist[0][i] != INF) len[i] = dist[0][i];
for (int i = 0; i < vn - 1; ++i) {
int minLen = INF, next;
for (int j = 0; j < vn; ++j)
if (!isVisited[j] && len[j] < minLen)
minLen = len[j], next = j;
isVisited[next] = true;
for (int j = 0; j < vn; ++j)
if (!isVisited[j] && dist[next][j] != INF) {
int tmp = len[next] + dist[next][j];
len[j] = len[j] < tmp ? len[j] : tmp;
}
}
}
void printLen(void) {
for (int i = 0; i < vn; ++i)
printf("%d\n", len[i]);
}
int main () {
init();
dijkstra();
printLen();
return 0;
}
4.2 Dijkstra 最短路径
在前一题基础上用栈记录。
// shortest path (dijkstra)
#include <stdio.h>
#include <stdbool.h>
#define MAXSIZE 105
#define INF 10000
int vn = 0;
int dist[MAXSIZE][MAXSIZE] = {};
bool isVisited[MAXSIZE] = {};
int len[MAXSIZE] = {};
int stack[MAXSIZE] = {};
int top = -1;
void init(void) {
scanf("%d", &vn);
for (int i = 0; i < vn; ++i)
for (int j = 0; j < vn; ++j)
scanf("%d", &dist[i][j]);
}
void dijkstra(void) {
for (int i = 0; i < vn; ++i) len[i] = INF;
isVisited[0] = true; len[0] = 0;
for (int i = 0; i < vn; ++i)
if (dist[0][i] != INF) len[i] = dist[0][i];
for (int i = 0; i < vn - 1; ++i) {
int minLen = INF, next;
for (int j = 0; j < vn; ++j)
if (!isVisited[j] && len[j] < minLen)
minLen = len[j], next = j;
isVisited[next] = true;
for (int j = 0; j < vn; ++j)
if (!isVisited[j] && dist[next][j] != INF) {
int tmp = len[next] + dist[next][j];
len[j] = len[j] < tmp ? len[j] : tmp;
}
}
}
void printPath(void) {
int a, b;
scanf("%d %d", &a, &b);
stack[++top] = b;
while (b != a)
for (int i = 0; i < vn; ++i)
if (dist[i][b] != INF && len[i] < len[b] &&
len[i] + dist[i][b] == len[b])
stack[++top] = b = i;
while(top > -1) printf("%d\n", stack[top--]);
}
int main () {
init();
dijkstra();
printPath();
return 0;
}
4.3 Floyd 最短路径长度
不断以下一个顶点为中间顶点更新邻接矩阵即可。
#include <stdio.h>
#define MAXSIZE 105
int vn = 0;
int dist[MAXSIZE][MAXSIZE] = {};
void init(void) {
scanf("%d", &vn);
for (int i = 0; i < vn; ++i)
for (int j = 0; j < vn; ++j)
scanf("%d", &dist[i][j]);
}
void floyd(void) {
for (int n = 0; n < vn; ++n)
for (int i = 0; i < vn; ++i)
for (int j = 0; j < vn; ++j)
if (dist[i][n] + dist[n][j] < dist[i][j])
dist[i][j] = dist[i][n] + dist[n][j];
}
void printLen(void) {
int n;
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
int a, b;
scanf("%d %d", &a, &b);
printf("%d\n", dist[a][b]);
}
}
int main () {
init();
floyd();
printLen();
return 0;
}
4.4 Floyd 最短路径
在前一题基础上用栈回溯。
#include <stdio.h>
#define MAXSIZE 105
int vn = 0;
int dist[MAXSIZE][MAXSIZE] = {};
int path[MAXSIZE][MAXSIZE] = {};
int stack[MAXSIZE] = {};
int top = -1;
void init(void) {
scanf("%d", &vn);
for (int i = 0; i < vn; ++i)
for (int j = 0; j < vn; ++j) {
scanf("%d", &dist[i][j]);
path[i][j] = -1;
}
}
void floyd(void) {
for (int n = 0; n < vn; ++n)
for (int i = 0; i < vn; ++i)
for (int j = 0; j < vn; ++j)
if (dist[i][n] + dist[n][j] < dist[i][j]) {
dist[i][j] = dist[i][n] + dist[n][j];
path[i][j] = n;
}
}
void findPath(int a, int b) {
stack[++top] = b;
if (path[a][b] == -1) {
stack[++top] = a;
return;
}
findPath(a, path[a][b]);
}
void printPath(void) {
int n;
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
int a, b;
scanf("%d %d", &a, &b);
top = -1;
findPath(a, b);
while (top > -1) printf("%d\n", stack[top--]);
}
}
int main () {
init();
floyd();
printPath();
return 0;
}