2024西工大数据结构实验(头歌 C)

annesede 2024-07-01 11:05:08 阅读 93

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 稀疏矩阵转置

交换行与列后插入排序(稳定排序算法,不改变原有行顺序——即交换后列顺序);注意矩阵行列号从

0

0

0 开始。

#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 && S[cnt][1] == 0 && S[cnt][2] == 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 稀疏矩阵加法

双指针法就是好用啊,需要考虑相加为

0

0

0 时,下同。

#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;

}

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;

}

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;

}

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;

}

else {

S[h][0] = S1[h1][0], S[h][1] = S1[h1][1], S[h][2] = S1[h1][2] + S2[h2][2];

++h1, ++h2;

}

}

if (S[h][2] != 0) ++h;

}

for (int i = 0; i < h && 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 *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;

// 单独判断相加后为 0 情况

if (curr->right->val == 0) {

Node *vert = curr->right, *temp = vert;

while (vert->down != temp) vert = vert->down;

curr->right = temp->right, vert->down = temp->down;

free(temp);

}

return;

}

Node *node = (Node*) malloc(sizeof(Node));

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 稀疏矩阵乘法

双指针还要写回溯,不如直接遍历了。

#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 最短路径

在前一题基础上用栈记录。

#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;

}



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。