数据结构荣誉课--第三次实验解题报告

本文探讨了四种不同的算法问题,涉及二叉树最长路径、森林层次遍历、纸带切割优化以及序列乘积计算。针对每个问题,提供了详细的解题思路和代码实现,包括递归、优先队列等数据结构的应用,旨在展示高效解决复杂问题的策略。
摘要由CSDN通过智能技术生成

7-1 二叉树最长路径 (100 分)

题目

给定一棵二叉树T,求T中的最长路径的长度,并输出此路径上各结点的值。若有多条最长路径,输出最右侧的那条。

输入格式:
第1行,1个整数n,表示二叉树有n个结点, 1≤n≤100000.

第2行,2n+1个整数,用空格分隔,表示T的扩展先根序列, -1表示空指针,结点用编号1到n表示。

输出格式:
第1行,1个整数length,length表示T中的最长路径的长度。

第2行,length+1个整数,用空格分隔,表示最右侧的最长路径。

输入样例:
在这里给出一组输入。例如:
在这里插入图片描述

输出样例:
在这里给出相应的输出。例如:
在这里插入图片描述

在这里插入图片描述

思路

  • 思路一: 一开始用了先求树的高度,再遍历打印最长路径的方法,但一直有一个测试点因为超时过不去,我以为是我使用递归的原因,消了递归还是过不去后来觉得应该是函数内调用函数的原因,这里给出思路一的代码:
int getheight(Tree T) {//第一个方法:递归求树的高度
	if (T == NULL) return 0;
	int x, y;
	x = getheight(T->left);
	y = getheight(T->right);
	return max(x, y) + 1;
}
int Treelong1(Tree T) {//第二个方法:使用队列求出树的高度
	queue<Node*> q;
	if (!T) return 0;
	q.push(T);
	int level = 0;
	while (!q.empty())
	{
		int len = q.size();
		level++;
		while (len--) {
			Tree tmp = q.front();
			q.pop();
			if (tmp->left) q.push(tmp->left);
			if (tmp->right) q.push(tmp->right);
		}
	}
   while (!q.empty())
	{
		//int t = q.front()->data;
		q.pop();

	}
	return level;//返回树的高度
}
void Findlong(Tree T, int k) {//遍历打印最长路径
	int K=k;
	for (int i = 0; i < K; i++) {
		if (T != NULL) {
			k--;
			printf("%d", T->data);
			if (k != 0) printf(" ");//符合格式要求
			if (getheight(T->left) > getheight(T->right))//or 使用Treelong1函数
				T = T->left;                            //比较两字树的高度,哪边高往哪边走
			else T = T->right;
		}
	}
}
  • 思路二:虽然使用的是递归,但没有超市,说明这道题多使用几次递归是不会超时的,但在函数内嵌套使用其他函数会超时。思路如下:递归建树,遍历树,将树的结点存入一个数组,设两个变量len和longestlen,分别表示当前走过路径的长度和最长路径,如果len大于longestlen就更新longestlen,再将当前路径的各结点存入最长路径结点的数组longestpath,递归进行遍历直到递归出口。
    最后打印出longestpath里的树的结点,此处要注意格式要求。

代码

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
using namespace std;
typedef struct node {
	int data;
	struct node* left;
	struct node* right;
}Node,*Tree;
void BuildTree(Tree &T, int n){
	int data;
	int i = 0;
	scanf("%d", &data);
	if (data ==-1) {
		T = NULL;
		return;
	}
	else if (data != -1 && i <= n) {
		if (!(T = (Tree)malloc(sizeof(Node)))) exit(0);
		i++;
		T->data = data;
		BuildTree(T->left,n);
		BuildTree(T->right,n);
	}
}

void longest_path(Tree T, int* path, int& len, int* longestpath, int& longestlen) {
	if (T != NULL) {
		if (T->left == NULL && T->right == NULL) {
			path[len] = T->data;
			if (len >= longestlen) {
				for (int j = 0; j <= len; j++) {
					longestpath[j] = path[j];
				}
				longestlen = len;
			}
		}
		else {
			path[len++] = T->data;
			longest_path(T->left, path, len, longestpath, longestlen);
			longest_path(T->right, path, len, longestpath, longestlen);
			len--;
		}
	}
}

int main() {
	int n;
	Tree T;
	scanf("%d", &n);
	BuildTree(T, n);
	int path[100000] = { 0 };
	int longestpath[100000] = { 0 };
	int len = 0;
	int longestlen = 0;
	longest_path(T, path, len, longestpath, longestlen);
	printf("%d\n", longestlen);
	for (int i = 0; i <= longestlen; i++) {
		printf("%d", longestpath[i]);
		if (i != longestlen) printf(" ");
	}
}

7-2 森林的层次遍历 (100 分)

题目

给定一个森林F,求F的层次遍历序列。森林由其先根序列及序列中每个结点的度给出。

输入格式:
第1行,1个整数n,表示森林的结点个数, 1≤n≤100000.

第2行,n个字符,用空格分隔,表示森林F的先根序列。字符为大小写字母及数字。

第3行,n个整数,用空格分隔,表示森林F的先根序列中每个结点对应的度。

输出格式:
1行,n个字符,用空格分隔,表示森林F的层次遍历序列。

输入样例:
在这里给出一组输入。例如:
在这里插入图片描述

输出样例:
在这里给出相应的输出。例如:
在这里插入图片描述
在这里插入图片描述

思路

  • 先用递归方法建树,由于题目给的是森林,不止建一棵数,所以需要初始化一个变量num,记录处理的结点,num=n的时候表示森林已建成。
  • 用两个数组分别保存结点的值和结点的度,再利用vector的儿子链把每个结点的儿子保存起来,以此实现层次遍历。

代码

#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
#include "queue"
#include "vector"
using namespace std;
char Node[100001][2];
int Nodenum[100001];
vector<int> s[100001];
int num = 1;
void Buildonetree(int n) {
	for (int i = 0; i < Nodenum[n]; i++) {
		s[n].push_back(++num);
		Buildonetree(num);
	}
}
int main() {
	int n;
	queue<int> q;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%s", Node[i]);
	}
	for (int i = 1; i <= n; i++) {
		scanf("%d", &Nodenum[i]);
	}
	while (num<=n)
	{
		s[0].push_back(num);
		Buildonetree(num);
		num++;
	}
	q.push(0);
	while (!q.empty()){
		int x = q.front();
		q.pop();
		for (vector<int>::iterator i = s[x].begin(); i != s[x].end(); i++) q.push(*i);
		if (x) printf("%s", Node[x]);
		if (!q.empty()&&x!=0) printf(" ");
	}
}

7-3 纸带切割 (100 分)

题目

有一条细长的纸带,长度为 L 个单位,宽度为一个单位。现在要将纸带切割成 n 段。每次切割把当前纸带分成两段,切割位置都在整数单位上,切割代价是当前切割纸带的总长度。每次切割都选择未达最终要求的最长纸带切割,若这样的纸带有多条,则任选一条切割。如何切割,才能完成任务,并且总代价最小。

输入格式:
第1行,1个整数n,表示切割成的段数, 1≤n≤100000.

第2行,n个整数Li,用空格分隔,表示要切割成的各段的长度,1≤Li≤200000000,1≤i≤n.

输出格式:
第1行,1个整数,表示最小的总代价。

第2行,若干个整数,用空格分隔,表示总代价最小时每次切割的代价。

输入样例:
在这里给出一组输入。例如:
在这里插入图片描述

输出样例:
在这里给出相应的输出。例如:
在这里插入图片描述
在这里插入图片描述

思路

  • 刚开始看这道题是时候没读懂题目,听了同学的讲解,才发现是可以从样例看出来此题要用哈夫曼树。先建一个单调递增的优先队列(先写的第四题,写的时候看大佬用了优先队列,发现也可以用的这道题上),每次弹出两个最小元素,相加压入对内,每次的和保存在一个数组里,于此同时初始化一个变量sum,保存最小总代价,最后再将数组中的元素反向打印,此处要注意格式要求。

代码

#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
#include "iomanip"
#include "queue"
using namespace std;
//priority_queue<long long> q;
priority_queue<long long, vector<long long>, greater< long long> >q;
long long a[100000];
long long b[100000];
int len = 0;
int main() {
	int n;
	long long sum = 0;
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%lld", &a[i]);
		q.push(a[i]);
	}
	while (q.size() > 1) {
		long long tmp1 = q.top();
		q.pop();
		long long tmp2 = q.top();
		q.pop();
		sum += tmp1 + tmp2;
		b[len] = tmp1 + tmp2;
		len++;
		q.push(tmp1 + tmp2);
	}
	printf("%lld\n", sum);
	for (int i = len-1; i >=0; i--) {
		printf("%lld", b[i]);
		if (i !=0) printf(" ");
	}
}

7-4 序列乘积 (100 分)

题目

两个递增序列A和B,长度都是n。令 Ai 和 Bj 做乘积,1≤i,j≤n.请输出n*n个乘积中从小到大的前n个。

输入格式:
第1行,1个整数n,表示序列的长度, 1≤n≤100000.

第2行,n个整数Ai,用空格分隔,表示序列A,1≤Ai≤40000,1≤i≤n.

第3行,n个整数Bi,用空格分隔,表示序列B,1≤Bi≤40000,1≤i≤n.

输出格式:
1行,n个整数,用空格分隔,表示序列乘积中的从小到大前n个。

输入样例:
在这里给出一组输入。例如:
在这里插入图片描述

输出样例:
在这里给出相应的输出。例如:
在这里插入图片描述
在这里插入图片描述

思路

  • 最开始使用了暴力法。将所有的运算结果保存在一个数组,再使用sort函数排序数组,最后将前n个数打印出来,此方法因为内存超限,只得了60分。
    代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAX = 1e8+ 3;
int sum[MAX];
int main() {
	int n,k=0;
	scanf("%d", &n);
	int A[100000], B[100000];
	for (int i=0; i < n; i++) scanf("%d", &A[i]);
	for (int i=0; i < n; i++) scanf("%d", &B[i]);
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			sum[k] = A[i] * B[j];
			k++;
		}
	}
	sort(sum, sum + k-1);
	for (int t = 0; t < n; t++) {
        printf("%d", sum[t]);
        if(t!=n-1) printf(" ");}

}
  • 显示内存超限的时候我以为是我开的保存运算结果的数组不够大,然后试着只保存前n个结果,每保存n个数排序一次,将新的结果跟数组最后一个元素比较,如果新的结果小则更新数组最后一个元素,再次排序,以此类推,但发现还是不行。此题主要卡的点是运算结果的时候效率是O(n^2)的,所以要减少运算次数。看了大佬的代码,知道了可以使用结构体优先队列。
  • 开一个优先队列维护一个小根堆,分别保存一个数的数值,所在的行数和列数。将乘积看做是两个矩阵的乘法,从样例是以递增顺序给的,所以最小的数值只能出现在矩阵每行的最左端。一开始只需算A数组的第一个元素和B组元素的乘积将结果压入堆中,然后每次取最小后再把这个数右边的运算结果放入堆中。
  • 要注意格式要求。

代码

#define _CRT_SECURE_NO_WARNINGS
#include<queue>
#include<iostream>
using namespace std;
struct node {
	int i, j;
	long long data;
};
bool operator<(const node& p, const node& q) {
	return p.data > q.data;
}
priority_queue<node> q;
long long A[100001], B[100001];
int main() {
	node C;
	int n, k = 1,j=0;
	scanf("%d", &n);
	for (int i = 0; i < n; i++) scanf("%lld", &A[i]);
	for (int i = 0; i < n; i++) scanf("%lld", &B[i]);
	for (int i = 0; i < n; i++) {
		 C.data= A[i] * B[0];
		 C.i = i;
		 C.j = 0;
		q.push(C);
	}
    for(int i=0;i<n;i++){
		node tmp = q.top();
		q.pop();
		printf("%lld", tmp.data);
		if (i != n - 1) printf(" ");
		tmp.j=tmp.j+1;
		tmp.data = A[tmp.i] * B[tmp.j];
		q.push(tmp);
	}
	

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bear0604

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值