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=0R(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;
}
  • 52
    点赞
  • 107
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
哈夫曼编码是一种常用的数据压缩算法,可以将原始数据转换为更短的编码,从而减少存储空间。它的基本思想是:根据字符出现的频率,构建一颗二叉树,使得出现频率高的字符离根节点近,出现频率低的字符离根节点远。然后,对于每个字符,从根节点出发,沿着对应的路径到达该字符所在的叶子节点,记录下路径,作为该字符的编码。 哈夫曼编码的具体实现步骤如下: 1. 统计每个字符在原始数据中出现的频率。 2. 根据字符的频率构建哈夫曼树。构建方法可以采用贪心策略,每次选择出现频率最低的两个字符,将它们作为左右子节点,父节点的权值为两个子节点的权值之和。重复这个过程,直到只剩下一个根节点。 3. 对哈夫曼树进行遍历,记录下每个字符的编码,为了避免编码产生歧义,通常规定左子节点为0,右子节点为1。 4. 将原始数据中的每个字符,用它对应的编码来代替。这一步可以通过哈夫曼树来实现。 5. 将编码后的数据存储起来。此时,由于每个字符的编码长度不同,所以压缩后的数据长度也不同,但总体上来说,压缩效果通常是比较好的。 实现哈夫曼编码的关键在于构建哈夫曼树和计算每个字符的编码。构建哈夫曼树可以采用优先队列来实现,每次从队列中取出两个权值最小的节点,合并成一个节点,再将合并后的节点插入队列中。计算每个字符的编码可以采用递归遍历哈夫曼树的方式,从根节点出发,如果走到了左子节点,则将0添加到编码中,如果走到了右子节点,则将1添加到编码中,直到走到叶子节点为止。 以下是基于C++的代码实现,供参考: ```c++ #include <iostream> #include <queue> #include <string> #include <unordered_map> using namespace std; // 定义哈夫曼树节点的结构体 struct Node { char ch; // 字符 int freq; // 出现频率 Node* left; // 左子节点 Node* right; // 右子节点 Node(char c, int f) : ch(c), freq(f), left(nullptr), right(nullptr) {} }; // 定义哈夫曼树节点的比较函数,用于优先队列的排序 struct cmp { bool operator() (Node* a, Node* b) { return a->freq > b->freq; } }; // 构建哈夫曼树的函数 Node* buildHuffmanTree(unordered_map<char, int> freq) { priority_queue<Node*, vector<Node*>, cmp> pq; for (auto p : freq) { pq.push(new Node(p.first, p.second)); } while (pq.size() > 1) { Node* left = pq.top(); pq.pop(); Node* right = pq.top(); pq.pop(); Node* parent = new Node('$', left->freq + right->freq); parent->left = left; parent->right = right; pq.push(parent); } return pq.top(); } // 遍历哈夫曼树,计算每个字符的编码 void calcHuffmanCode(Node* root, unordered_map<char, string>& code, string cur) { if (!root) return; if (root->ch != '$') { code[root->ch] = cur; } calcHuffmanCode(root->left, code, cur + "0"); calcHuffmanCode(root->right, code, cur + "1"); } // 将原始数据编码成哈夫曼编码 string encode(string s, unordered_map<char, string> code) { string res; for (char c : s) { res += code[c]; } return res; } // 将哈夫曼编码解码成原始数据 string decode(string s, Node* root) { string res; Node* cur = root; for (char c : s) { if (c == '0') { cur = cur->left; } else { cur = cur->right; } if (!cur->left && !cur->right) { res += cur->ch; cur = root; } } return res; } int main() { string s = "abacabad"; unordered_map<char, int> freq; for (char c : s) { freq[c]++; } Node* root = buildHuffmanTree(freq); unordered_map<char, string> code; calcHuffmanCode(root, code, ""); string encoded = encode(s, code); string decoded = decode(encoded, root); cout << "Original string: " << s << endl; cout << "Encoded string: " << encoded << endl; cout << "Decoded string: " << decoded << endl; return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值