0
点赞
收藏
分享

微信扫一扫

PTA团体程序设计天梯赛篇(二)----数据结构

夏侯居坤叶叔尘 2022-03-16 阅读 49

数据结构+贪心专题

数据结构

这是二叉搜索树吗?

题目链接
解题思路:
此题我们知道了二叉搜索树的性质:左子树小于根,右子树大于等于根。
且输入的是前序遍历,则对一个二叉树[l,r]:a[l]是根,[l+1,r]是左右子树范围。
其中,前x项若都小于根,剩下的都大于等于根:则从l+1开始的前x个就是左子树,剩下的都是右子树。如此就分出了左右子树[l1,r1][l2,r2],然后再对左右子树递归即可。

由于输出要后序遍历,则我们只需:递归左子树,递归右子树,存根 (按照后序遍历的顺序)即可。

代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;

const int N = 10010;

int n, flag;
int t[N];
vector<int>ans;

void solve(int l, int r) {
	if (r < l)
		return ;
	int i = l + 1, j = r;

	if (!flag) {
		while (i <= r && t[i] < t[l]) // 最后停止在大于等于的位置,比正确位置后移了一位
			i++;
		while (j > l && t[j] >= t[l]) // 最后停在小于的位置,比正确位置前移了一位
			j--;
		if (i - j != 1) // 只有当两者相差1也就是中间没有元素的时候才正确
			return ;
		solve(l + 1, i - 1);
		solve(j + 1, r);
		ans.push_back(t[l]); 
	} else {
		while (i <= r && t[i] >= t[l])
			i++;
		while (j > l && t[j] < t[l])
			j--;
		if (i - j != 1)
			return ;
		solve(l + 1, i - 1);
		solve(j + 1, r);
		ans.push_back(t[l]);
	}
}

int main() {
	int f = 0;
	cin >> n;
	for (int i = 1 ; i <= n ; ++i)
		cin >> t[i];
	solve(1, n);
	if (ans.size() == n) {
		puts("YES");
		for (auto x : ans) {
			if (f)
				cout << " ";
			cout << x;
			f++;
		}
	} else {
		ans.clear();
		flag = 1;
		solve(1, n);
		if (ans.size() == n) {
			puts("YES");
			for (auto x : ans) {
				if (f)
					cout << " ";
				cout << x, f++;
			}
		} else
			puts("NO");
	}
	return 0;
}

树的遍历

题目链接

解题思路:

思路:(1)通过后序遍历找到根结点,再通过根结点在中序遍历的位置找出左子树、右子树。(2)根据左子树在后序遍历结果的顺序,找到左子树的根结点,视左子树为一棵独立的树,转步骤(1)。(3)根据右子树在后序遍历结果的顺序,找到右子树的根结点,视右子树为一棵独立的树,转步骤(1)。

注意点:
函数参数需要一个创建二叉树的指针 ,和后序的左右指针,以及中序的左右指针,5个参数。

代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cstdlib>
using namespace std;

const int N = 1010;

int n ;
int in[N], nx[N];

typedef struct node {
	int val;
	struct node *l, *r;
} BitNode, *BiTree;


void build(BiTree &T, int L1, int R1, int L2, int R2) {
	T = (BiTree)malloc(sizeof (BitNode));
	T->val = nx[R1];
	int in_root ;
	for (int i = L2 ; i <= R2 ; ++i)
		if (in[i] == nx[R1]) {
			in_root = i ;
			break;
		}

	if ( in_root - L2 != 0 ) {
		build(T->l, L1, L1 + in_root - L2 - 1,  L2,  in_root - 1);
	} else
		T->l = NULL;

	if (R2 - in_root != 0) {
		build(T->r, R1 - (R2 - in_root), R1 - 1, in_root + 1, R2 );
	} else
		T->r = NULL;
}

void bfs(BiTree T) {
	queue<BiTree>q;
	q.push(T);
	int f = 0;
	while (q.size()) {
		auto u = q.front();
		q.pop();
		if (f)
			cout << " ";
		cout << u->val;
		f++;
		if (u->l)
			q.push(u->l);
		if (u->r)
			q.push(u ->r);
	}
}

int main() {
	BiTree t;
	cin >> n;
	for (int i = 1 ; i <= n ; ++i)
		cin >> nx[i];
	for (int j = 1; j <= n ; ++j)
		cin >> in[j];
	build(t, 1, n, 1, n);
	bfs(t);
	return 0;
}

玩转二叉树(中序+前序建树 + 翻转输出)

题目链接

解题思路:
题目的要求很清楚了,就是通过前序序列和中序序列将树建立出来。至于后面的镜面反转输出,我们在用BFS输出的时候,原本我们是先左子树再右子树。那么我们只需改变顺序,变成先右子树,再左子树就满足要求了。

代码:

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <queue>
using namespace std;
const int N = 50;
int n, in[N], pre[N];

typedef struct node {
	int val;
	node *l, *r;
} TNode, * Tree;

void build(Tree &T, int L1, int R1, int L2, int R2) {
	T = (Tree)malloc(sizeof (TNode));
	T->val = pre[L1];
	int root;
	for (int i = L2 ; i <= R2 ; ++i)
		if (in[i] == pre[L1]) {
			root = i;
			break;
		}

	if (root - L2 != 0) {
		build(T->l, L1 + 1, L1 + root - L2, L2, root - 1 );

	} else
		T->l = NULL;
	if (R2 - root != 0) {
		build(T->r, R1 - (R2 - root) + 1, R1, root + 1, R2);
	} else
		T->r = NULL;
}

void bfs(Tree T) {
	queue<Tree>q;
	q.push(T);
	int f = 0;
	while (q.size()) {
		auto u = q.front();
		q.pop();
		if (f)
			cout << " ";
		cout << u->val;
		f++;
		if (u->r)
			q.push(u->r);
		if (u->l)
			q.push(u->l);
	}
}

int main() {
	Tree t;
	cin >> n;
	for (int i = 1; i <= n  ; ++i)
		cin >> in[i];
	for (int j =  1; j <= n ; ++j)
		cin >> pre[j];

	build(t, 1, n, 1, n);
	bfs(t);

	return 0;
}

并查集

排座位

题目链接

解题思路:
由于题目说了只要朋友关系具有传递性。因此我们只对朋友关系利用并查集维护。至于敌人关系我们就可以用一个邻接矩阵来存。如果是敌人就是1.

代码:

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110;
int g[N][N];
int f[N], n, m, k;

int find(int x) {
	if (x == f[x])
		return x;
	else
		return f[x] = find(f[x]);
}

void join(int x, int y) {
	x = find(x), y = find(y);
	if (x != y)
		f[x] = y;
}

int main() {
	cin >> n >> m >> k;
	for (int i = 1; i <= n ; ++i)
		f[i] = i;
	while (m--) {
		int a, b, c;
		cin >> a >> b >> c;
		if (c == 1)
			join(a, b);
		else
			g[a][b] = g[b][a] = c;
	}
	while (k--) {
		int a, b;
		cin >> a >> b;
		if (find(a) == find(b) && g[a][b] == 0)
			cout << "No problem\n";
		else if (g[a][b] == 0)
			cout << "OK\n";
		else if (find(a) == find(b) && g[a][b] == -1)
			cout << "OK but...\n";
		else
			cout << "No way\n";
	}

	return 0;
}

家庭房产

题目链接

解题思路:

  1. 关键点1是如何在维护家庭关系的同时记录最小的值:由于并查集中的祖先其实就是等价类中的代表元素,因此我们只需要在合并的时候,一直令最小的值来充当代表元素即可。
  2. 对于数据的数理:起初我们用一个结构体记录下一个人的父母,房产,面积等信息。同时将这个值以及父母的值记录下来。之后,我们用另一个结构体来存下每一个家庭的值(即题目所要求的信息)我们遍历所有的值。如果标记过那么找到其代表元素,同时将代表元素标记上。这样在遍历之后,一个家庭所要求的信息都存在了代表元素上。那么我们对其进行排序后输出(由于题目是数值有大到下,因此有数值肯定比没有数值的大,因此排序完成之后有价值的值都放到了前边)即可。

代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;
const int N = 10100;
int n ;
int f[N], cnt;
bool st[N];

struct node {
	int f, m ;
	int child[10];
	int t, sum;
} p[N];

int find(int x) {
	if (f[x] == x)
		return x;
	return f[x] = find(f[x]);
}

void join(int x, int y) {
	x = find(x), y = find(y);
	if (x != y) {
		if (y < x)
			f[x] = y;
		else
			f[y] = x;
	}
}

struct Date {
	int flag, cnt, id ;
	double avg,  sum;
	bool operator < ( Date w) {
		if (sum == w.sum)
			return id < w.id;
		else
			return sum > w.sum;
	}
} sg[N];

bool cmp(Date a, Date b) {
	if (a.sum != b.sum)
		return a.sum > b.sum;
	else
		return a.id < b.id;
}

int main() {
	cin >> n;
	for (int i = 1 ; i < N ; ++i)
		f[i] = i;

	for (int i = 1; i <= n ; ++i) {
		int a, b, c, k;
		cin >> a ;
		st[a] = 1;
		cin >> p[a].f >> p[a].m >> k;
		if (p[a].f != -1)
			join(a, p[a].f), st[p[a].f] = 1;
		if (p[a].m != -1)
			join(a, p[a].m), st[p[a].m ] = 1;
		for (int j = 0 ; j < k ; ++j) {
			int x;
			cin >> p[a].child[j];
			join(a, p[a].child[j]);
			st[p[a].child[j]] = 1;
		}
		cin >> p[a].t >> p[a].sum;
	}
	for (int i = 0 ; i < N ; ++i)
		if (st[i]) {
			int pa = find(i);
			sg[pa].flag = 1;
			sg[pa].id = pa;
			sg[pa].cnt ++;
			sg[pa].avg += p[i].t;
			sg[pa].sum += p[i].sum;
		}
	for (int i = 0 ; i < N ; ++i)
		if (sg[i].flag) {
			sg[i].avg = sg[i].avg * 1.0 / sg[i].cnt;
			sg[i].sum = sg[i].sum * 1.0 / sg[i].cnt;
		}
	sort(sg, sg + N - 1 );
	int idx = 0;
	for (int i = 0 ; sg[i].flag ; ++i)
		idx++;
	cout << idx << endl;
	for (int i = 0 ; sg[i].flag ; ++i)
		printf("%04d %d %.3lf %.3lf\n", sg[i].id, sg[i].cnt, sg[i].avg, sg[i].sum);
	return 0;
}

部落

题目链接

解题思路
对于一个圈子,我们选出一个人,令其是是圈子的根,同时利用一个bool数组记录出现的值。在对每一个圈子都操作完之后,我们遍历1~N的所有数,如果记录中就加一,同时如果它直接就是根则互不相交的部落的个数加1.

代码:

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e4 + 10;
int n, m ;
int f[N];
bool st[N];

int find(int x) {
	if (x ==  f[x])
		return x;
	else
		return f[x] = find(f[x]);
}

void join(int x, int y) {
	x = find(x), y = find(y);
	if (x != y)
		f[x] = y;
}

int main() {
	cin >> n;
	for (int i = 1 ; i < N ; ++i)
		f[i] = i;
	while (n--) {
		int k;
		cin >> k;
		int f = 0;
		while (k--) {
			int x;
			cin >> x;
			st[x] = 1;
			if (!f)
				f = x;
			else
				join(x, f);
		}
	}
	int cnt = 0, num = 0;
	for (int i = 1 ; i < N ; ++i) {
		if (st[i])
			num++;
		if (st[i] && f[i] == i)
			cnt++;
	}
	cout << num << " " << cnt << endl;
	cin >> m;
	while (m--) {
		int x, y;
		cin >> x >> y;
		if (find(x) == find(y))
			cout << "Y" << endl;
		else
			cout << "N\n";
	}


	return 0;
}

线性结构

链表

重排链表

题目链接

解题思路:
对于这里的地址节点,直接当成字符串处理就好多了。利用map存一个节点的前后节点。重派的操作分为奇偶来进行曹组。t一直是跟再head的后边,并连上head。而head的情况则要根据奇偶次数来判断是往前还是往后。如图

注意事项:
有的测试数据中会包含错误结点,这些结点不会被用上。

代码:

#include <iostream>
#include <algorithm>
#include <string>
#include <map>
using namespace std;
int n, cnt ;
string rt, trail;
map<string, string>pre, ne;
map<string, int > ans;

void get(string h) {
	while (h != "-1") {
		h = ne[h];
		cnt++;
	}
}


int main() {
	cin >> rt >> n;
	for (int i = 1 ; i <= n ; ++i) {
		string s1, s2;
		int x;
		cin >> s1 >> x >> s2;
		if (s2 == "-1")
			trail = s1;
		ans[s1] = x ;
		ne[s1] = s2;
		pre[s2] = s1;
	}
	get(rt); // 由于有的节点没用,不在链表中因此需要先求出真实的长度
	string head = rt, t = trail, tp, nx ;
	int f = 0;
	while (1) {
		tp = t , nx = ne[t];
		ne[t] = head;
		t = head;
        f++;
		if (f & 1) // 奇数
			head = pre[tp];
		else
			head = nx;
		if (f == cnt)
        {
            ne[t] = "-1";
            break;
        }
	}
    
	string s = trail;
	for (int i = 1 ; i <= n ; ++i) {
		cout << s << " " << ans[s] << " " << ne[s] << endl;
		s = ne[s];
        if(s == "-1")break;
	}
	return 0;
}
举报

相关推荐

0 条评论