7-1 还原二叉树
给定一棵二叉树的前序遍历序列和中序遍历序列,要求计算该二叉树的高度。
输入格式:
输入首先给出正整数 (50),为树中结点总数。随后 2 行先后给出前序和中序遍历序列,均是长度为 的不包含重复英文字母(区别大小写)的字符串。
输出格式:
输出为一个整数,即该二叉树的高度。
输入样例:
9
ABDFGHIEC
FDHGIBEAC
输出样例:
5
#include<iostream>
#include<string>
using namespace std;
const int N = 100;
string S, T;//T前序S中序
int Deep(int root, int start ,int end) {
if (start > end) return 0;
int i = start;
while (T[root] != S[i]) i++;
int left_deep = Deep(root + 1, start, i - 1);
int right_deep = Deep(root + i + 1 - start, i + 1, end);
return max(left_deep, right_deep) + 1;
}
int main(){
int n; cin >> n;
cin >> T >> S;
int result = Deep(0, 0, n - 1);
cout << result;
}
7-2 朋友圈
某学校有N个学生,形成M个俱乐部。每个俱乐部里的学生有着一定相似的兴趣爱好,形成一个朋友圈。一个学生可以同时属于若干个不同的俱乐部。根据“我的朋友的朋友也是我的朋友”这个推论可以得出,如果A和B是朋友,且B和C是朋友,则A和C也是朋友。请编写程序计算最大朋友圈中有多少人。
输入格式:
输入的第一行包含两个正整数N(30000)和M(1000),分别代表学校的学生总数和俱乐部的个数。后面的M行每行按以下格式给出1个俱乐部的信息,其中学生从1~N编号:
第i个俱乐部的人数Mi(空格)学生1(空格)学生2 … 学生Mi
输出格式:
输出给出一个整数,表示在最大朋友圈中有多少人。
输入样例:
7 4
3 1 2 3
2 1 4
3 5 6 7
1 6
输出样例:
4
#include<iostream>
#include<queue>
using namespace std;
const int N=3e4+5;
int n,m;
int res;
int pre[N],cnt[N];
int find(int x){ //查找函数
while(x!=pre[x]) x=pre[x];
return x;
}
void join(int x,int y){//合并函数
int px=find(x);
int py=find(y);
if(px!=py) pre[py]=px;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){ //初始化根结点
pre[i]=i;
}
while(m--){ //输入数据,并实现操作
int num;cin>>num;
int fir;cin>>fir;
num--;
while(num--){
int mem;cin>>mem;
join(fir,mem);
}
}
for(int i=1;i<=n;i++){ //统计数据
int k=find(i);
cnt[k]++;
}
for(int i=1;i<=n;i++){
res=max(res,cnt[i]);
}
cout<<res;
}
7-3 修理牧场
农夫要修理牧场的一段栅栏,他测量了栅栏,发现需要 块木头,每块木头长度为整数 个长度单位,于是他购买了一条很长的、能锯成 块的木头,即该木头的长度是 的总和。
但是农夫自己没有锯子,请人锯木的酬金跟这段木头的长度成正比。为简单起见,不妨就设酬金等于所锯木头的长度。例如,要将长度为 20 的木头锯成长度为 8、7 和 5 的三段,第一次锯木头花费 20,将木头锯成 12 和 8;第二次锯木头花费 12,将长度为 12 的木头锯成 7 和 5,总花费为 32。如果第一次将木头锯成 15 和 5,则第二次锯木头花费 15,总花费为 35(大于 32)。
请编写程序帮助农夫计算将木头锯成 块的最少花费。
输入格式:
输入首先给出正整数 (),表示要将木头锯成 块。第二行给出 个正整数(),表示每段木块的长度。
输出格式:
输出一个整数,即将木头锯成 块的最少花费。
输入样例:
8
4 5 1 2 1 3 1 1
输出样例:
49
#include<iostream>
#include<queue>
using namespace std;
int main(){
int n; cin >> n;
priority_queue<int, vector<int>, greater<int>> q;
for(int i = 0; i < n; i++){
int a; cin >> a;
q.push(a);
}
int wpl = 0;
while(q.size() > 1){
int fir = q.top();
q.pop();
int sec = q.top();
q.pop();
int sum = fir + sec;
wpl += sum;
q.push(sum);
}
cout << wpl;
return 0;
}
7-4 根据后序和中序遍历输出前序遍历
本题要求根据给定的一棵二叉树的后序遍历和中序遍历结果,输出该树的前序遍历结果。
输入格式:
第一行给出正整数 (),是树中结点的个数。随后两行,每行给出 个整数,分别对应后序遍历和中序遍历结果,数字间以空格分隔。题目保证输入正确对应一棵二叉树。
输出格式:
在一行中输出Preorder:
以及该树的前序遍历结果。数字间有1个空格,行末不得有多余空格。
输入样例:
7
2 3 1 5 7 6 4
1 2 3 4 5 6 7
输出样例:
Preorder: 4 1 3 2 6 5 7
#include<iostream>
using namespace std;
const int N = 100;
//定义结构
int T[N], S[N], num[N], n,id=0;//T后序,S中序
//后序建立二叉树
void get(int root ,int start, int end){
if (end < start) {
return;
}
int i = end;
while (i>start&&T[root] != S[i]) i--;
num[id++]=T[root];
get(root-1+i-end,start, i-1);
get(root-1,i+1,end);
}
int main() {
int n; cin >> n;
for (int i = 0; i < n; i++) {
cin >> T[i];
}
for (int i = 0; i < n; i++) {
cin >> S[i];
}
cout<<"Preorder: ";
get(n-1,0,n-1);
for(int i=0;i<id-1;i++){
cout<<num[i]<<" ";
}cout<<num[id-1];
}
7-5 玩转二叉树
给定一棵二叉树的中序遍历和前序遍历,请你先将树做个镜面反转,再输出反转后的层序遍历的序列。所谓镜面反转,是指将所有非叶结点的左右孩子对换。这里假设键值都是互不相等的正整数。
输入格式:
输入第一行给出一个正整数N
(30),是二叉树中结点的个数。第二行给出其中序遍历序列。第三行给出其前序遍历序列。数字间以空格分隔。
输出格式:
在一行中输出该树反转后的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。
输入样例:
7
1 2 3 4 5 6 7
4 1 3 2 6 5 7
输出样例:
4 6 1 7 5 3 2
#include <iostream>
#include <queue>
using namespace std;
const int N=50;
int n; // 结点总数
int cnt; // 遍历输出时计数
typedef struct BiNode {
int data;
BiNode *lchild,*rchild;
}BiNode,*BiTree;
void Create(BiTree &T,int pre[],int in[],int len) // 根据前序序列和中序序列创建二叉树
{
int pos;
for(int i=0;i<len;i++) // 在中序序列里查找根结点位置
{
if(in[i]==pre[0])
{
pos=i;
break;
}
}
T=new BiNode;
T->data=in[pos];
int Llen=pos,Rlen=len-1-pos;
if(Llen==0)
T->lchild=NULL;
else
Create(T->lchild,pre+1,in,Llen);
if(Rlen==0)
T->rchild=NULL;
else
Create(T->rchild,pre+pos+1,in+pos+1,Rlen);
}
void Traverse(BiTree T) // 反转层序遍历
{
queue<BiTree> Q;
Q.push(T);
while(!Q.empty())
{
BiTree q=Q.front();//获取队列第一个元素
cout<<q->data;
cnt++;
if(cnt<n)
cout<<' ';
if(q->rchild) // 先输出右孩子,再输出左孩子,达到反转的目的
Q.push(q->rchild);
if(q->lchild)
Q.push(q->lchild);
Q.pop();
}
}
int main()
{
cin>>n;
int pre[N]={0},in[N]={0};
for(int i=0;i<n;i++)
cin>>in[i];
for(int i=0;i<n;i++)
cin>>pre[i];
BiTree tree;
Create(tree,pre,in,n);
Traverse(tree);
return 0;
}
7-6 部落
在一个社区里,每个人都有自己的小圈子,还可能同时属于很多不同的朋友圈。我们认为朋友的朋友都算在一个部落里,于是要请你统计一下,在一个给定社区中,到底有多少个互不相交的部落?并且检查任意两个人是否属于同一个部落。
输入格式:
输入在第一行给出一个正整数(),是已知小圈子的个数。随后行,每行按下列格式给出一个小圈子里的人:
其中是小圈子里的人数,()是小圈子里每个人的编号。这里所有人的编号从1开始连续编号,最大编号不会超过。
之后一行给出一个非负整数(),是查询次数。随后行,每行给出一对被查询的人的编号。
输出格式:
首先在一行中输出这个社区的总人数、以及互不相交的部落的个数。随后对每一次查询,如果他们属于同一个部落,则在一行中输出Y
,否则输出N
。
输入样例:
4
3 10 1 2
2 3 4
4 1 5 7 8
3 9 6 4
2
10 5
3 7
输出样例:
10 2
Y
N
#include<iostream>
using namespace std;
const int N=3e4+5;
int n,sum,cnt,pre[N];
int find(int x){
if(x==pre[x]) return x;
else{
pre[x]=find(pre[x]);
return pre[x];
}
}
void join(int x,int y){
int px=find(x);
int py=find(y);
if(px!=py) pre[py]=px;
}
int main(){
for(int i=1;i<N;i++){
pre[i]=i;
}
cin>>n;
for(int i=0;i<n;i++){
int num;cin>>num;
int fir;cin>>fir;
sum=max(sum,fir);
num--;
while(num--){
int mem;cin>>mem;
sum=max(sum,mem);
join(fir,mem);
}
}
for(int i=1;i<=sum;i++){
if(pre[i]==i) cnt++;
}
cout<<sum<<' '<<cnt<<endl;
int q;cin>>q;
while(q--){
int a,b;cin>>a>>b;
if(find(a)==find(b)) cout<<"Y"<<endl;
else cout<<"N"<<endl;
}
}
//建立4个森林,判断两个待判的根节点是否相同
7-7 列出叶结点
对于给定的二叉树,本题要求你按从上到下、从左到右的顺序输出其所有叶结点。
输入格式:
首先第一行给出一个正整数 (),为树中结点总数。树中的结点从 0 到 编号。随后 行,每行给出一个对应结点左右孩子的编号。如果某个孩子不存在,则在对应位置给出 "-"。编号间以 1 个空格分隔。
输出格式:
在一行中按规定顺序输出叶结点的编号。编号间以 1 个空格分隔,行首尾不得有多余空格。
输入样例:
8
1 -
- -
0 -
2 7
- -
- -
5 -
4 6
输出样例:
4 1 5
#include <iostream>
#include <cstdio>
using namespace std;
struct Node
{
// int data;
int left;
int right;
};
int main()
{
int n;
cin >> n;
getchar(); //千万千万注意这个getchar(),否则程序会直接崩溃
Node tree[15]; //因为这个数组的下标就是data,所以我不需要额外的data域
int checked[15] = {}; //检查根节点的(因为根节点不是任何结点的子节点,所以根节点不会出现在输入中)
for(int i = 0;i < n;i++)
{
char l,r;
scanf("%c %c",&l,&r);//输入用char
getchar();
if(l == '-')tree[i].left = -1;
else
{
tree[i].left = (l - '0');
checked[l - '0'] = 1;
}
if(r == '-')tree[i].right = -1;
else
{
tree[i].right = (r - '0');
checked[r - '0'] = 1;
}
}
int ans[15] = {};//借数组当队列,队列ans[i]是层序遍历的存储结果
for(int i = 0;i < n;i++)
{
if(checked[i] == 0)
{
ans[0] = i; //找到根节点,存入数组ans[0]
break;
}
}
int k = 1; //注意一下,k是从 1 开始的(因为根节点存进了ans[0])
for(int i = 0;i < n;i++) //层序遍历存储:从根节点开始,左孩子,右孩子,左孩子的(左孩子,右孩子),右孩子的(左孩子,右孩子)…
{
if(tree[ans[i]].left != -1)ans[k++] = tree[ans[i]].left; //tree的下标是ans[i]
//理解:从根节点ans[0]开始的左孩子ans[1]、右孩子ans[2],然后左孩子ans[1]的(左孩子ans[3],右孩子ans[4])...
if(tree[ans[i]].right != -1)ans[k++] = tree[ans[i]].right;
}
int flag = 0;
for(int i = 0;i < n;i++)
{
if(tree[ans[i]].left == -1 && tree[ans[i]].right == -1)//tree的下标是ans[i],因为ans[i]是层序遍历的存储结果
{
if(flag)cout << " ";
flag = 1;
cout << ans[i];
}
}
return 0;
}
7-8 完全二叉树的层序遍历
一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是完美二叉树。对于深度为 的,有 个结点的二叉树,若其结点对应于相同深度完美二叉树的层序遍历的前 个结点,这样的树就是完全二叉树。
给定一棵完全二叉树的后序遍历,请你给出这棵树的层序遍历结果。
输入格式:
输入在第一行中给出正整数 (),即树中结点个数。第二行给出后序遍历序列,为 个不超过 100 的正整数。同一行中所有数字都以空格分隔。
输出格式:
在一行中输出该树的层序遍历序列。所有数字都以 1 个空格分隔,行首尾不得有多余空格。
输入样例:
8
91 71 2 34 10 15 55 18
输出样例:
18 34 55 71 2 10 15 91
#include<iostream>
using namespace std;
void creat(int i,int n,int *tree)
{
if (i<=n)//判断条件是到n
{
creat(2*i,n,tree);
creat(2*i+1,n,tree);
cin >> tree[i];
}
}
int main ()
{
int n;
cin >> n;
int tree[35];
creat (1,n,tree);
for (int i=1;i<=n;i++)//编号从1开始到n
{
if (i==n)
cout << tree[i];
else
cout << tree[i] << " ";
}
return 0;
}
7-9 交换二叉树中每个结点的左孩子和右孩子
以二叉链表作为二叉树的存储结构,交换二叉树中每个结点的左孩子和右孩子。
输入格式:
输入二叉树的先序序列。
提示:一棵二叉树的先序序列是一个字符串,若字符是‘#’,表示该二叉树是空树,否则该字符是相应结点的数据元素。
输出格式:
输出有两行:
第一行是原二叉树的中序遍历序列;
第二行是交换后的二叉树的中序遍历序列。
输入样例:
ABC##DE#G##F###
输出样例:
CBEGDFA
AFDGEBC
#include <iostream>
#include <string>
using namespace std;
struct TreeNode {
char data;
TreeNode *right, *left;
TreeNode(char val) : data(val), right(nullptr), left(nullptr) {}
};
typedef struct TreeNode TreeNode;
TreeNode* BuildTree(const string &T, int& i) {
if (i >= T.length()) return nullptr;
char val = T[i++];
if (val == '#') return nullptr;
TreeNode* root = new TreeNode(val);
root->left = BuildTree(T, i);
root->right = BuildTree(T, i);
return root;
}
void inorderTraversal(TreeNode* root) {
if (!root) return;
inorderTraversal(root->left);
cout << root->data;
inorderTraversal(root->right);
}
// 交换每个节点的左右子树
void swapChildren(TreeNode* root) {
if (!root) return;
swapChildren(root->left);
swapChildren(root->right);
TreeNode* temp = root->left;
root->left = root->right;
root->right = temp;
}
int main() {
string S;
cin >> S;
int index = 0;
TreeNode* root = BuildTree(S, index);
// 输出原始二叉树的中序遍历
inorderTraversal(root);
cout << endl;
// 交换每个节点的左右子树
swapChildren(root);
// 输出交换后的二叉树的中序遍历
inorderTraversal(root);
return 0;
}
7-10 建立与遍历二叉树
以字符串的形式定义一棵二叉树的先序序列,若字符是‘#’, 表示该二叉树是空树,否则该字符是相应结点的数据元素。读入相应先序序列,建立二叉链式存储结构的二叉树,然后中序遍历该二叉树并输出结点数据。
输入格式:
字符串形式的先序序列(即结点的数据类型为单个字符)
输出格式:
中序遍历结果
输入样例:
在这里给出一组输入。例如:
ABC##DE#G##F###
输出样例:
在这里给出相应的输出。例如:
CBEGDFA
#include<iostream>
#include<string>
using namespace std;
const int N = 10001001;
struct TreeNode {
char data;
TreeNode* left, * right;
TreeNode(char val) :data(val), left(nullptr), right(nullptr) {}
};
TreeNode* BuildTree(const string& T, int& i) {
if (i >= T.length()) {
return nullptr;
}
char val = T[i++];
if (val == '#') {
return nullptr;
}
TreeNode* root = new TreeNode(val);
root->left = BuildTree(T, i);
root->right = BuildTree(T, i);
return root;
}
//中序遍历
void inorderTraversal(TreeNode* root) {
if (root == nullptr) return;
inorderTraversal(root->left);
cout << root->data ;
inorderTraversal(root->right);
}
int main() {
string T;
cin >> T;
int i = 0;
TreeNode* root = BuildTree(T, i);
inorderTraversal(root);
return 0;
}
7-11 树的遍历
给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列。这里假设键值都是互不相等的正整数。
输入格式:
输入第一行给出一个正整数(),是二叉树中结点的个数。第二行给出其后序遍历序列。第三行给出其中序遍历序列。数字间以空格分隔。
输出格式:
在一行中输出该树的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。
输入样例:
7
2 3 1 5 7 6 4
1 2 3 4 5 6 7
输出样例:
4 1 6 3 5 7 2
#include<iostream>
using namespace std;
const int N = 1000000;
//定义结构
int T[N], S[N], num[N],n;//T后序,S中序
int id;
//后序建立二叉树
void get(int T[], int S[], int len, int p) {
if (len < 1) {
num [ p ] = -1;
return;
}
int i = 0;
while (T[len - 1] != S[i]) i++;
num[p] = T[len - 1];
id=max(id,p);
//if(i)
get(T, S, i, 2 * p);
//if(len-i-1)
get(T+i, S+i+1, len-i-1, 2 * p + 1);
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> T[i];
}
for (int i = 0; i < n; i++) {
cin >> S[i];
}
get(T, S, n, 1);
for (int i = 1; i <= id; i++) {
if(num[i]>0)
{
cout << num[i];
if(i<id)
cout<<' ';
}
}
}
7-12 哈夫曼树
哈夫曼树,第一行输入一个数n,表示叶结点的个数。
需要用这些叶结点生成哈夫曼树,根据哈夫曼树的概念,这些结点有权值,即weight,题目需要输出哈夫曼树的带权路径长度(WPL)。
输入格式:
第一行输入一个数n,第二行输入n个叶结点(叶结点权值不超过1000,2<=n<=1000)。
输出格式:
在一行中输出WPL值。
输入样例:
5
1 2 2 5 9
输出样例:
37
#include<iostream>
#include<queue>
using namespace std;
int main(){
int n; cin >> n;
priority_queue<int, vector<int>, greater<int>> q;
for(int i = 0; i < n; i++){
int a; cin >> a;
q.push(a);
}
int wpl = 0;
while(q.size() > 1){
int fir = q.top();
q.pop();
int sec = q.top();
q.pop();
int sum = fir + sec;
wpl += sum;
q.push(sum);
}
cout << wpl;
return 0;
}
7-13 树层次遍历
我们已知二叉树与其自然对应的树相比,二叉树中结点的左孩子对应树中结点的左孩子,二叉树中结点的右孩子对应树中结点的右兄弟。进而我们可以利用“基于带空指针信息的先根序列构建二叉树”的方法来构建其对应的树的左孩子-右兄弟存储结构。如8 5 1 0 6 0 2 0 0 3 4 0 0 7 0 0 0对应图1(a)所示的树,1 2 0 3 0 4 0 0 0对应如图1(b)所示的树。
请编写程序用上述方法构建树,并给出树的层次遍历序列。
输入格式:
输入为一组用空格间隔的整数,个数不超过100个,表示带空指针信息的二叉树先根序列。其中空指针信息用0表示
输出格式:
输入为一组整数,每个整数后一个空格,表示该树的层次遍历序列。
输入样例:
1 2 0 3 0 4 0 0 0
输出样例:
1 2 3 4
//定义结构体
//建立树
//遍历
#include<iostream>
#include<queue>
using namespace std;
int tree[100];
typedef struct TreeNode {
int data;
TreeNode* rbrother, * lchild;
}TreeNode,*pin;
void BuildTree(pin &T) {
int a;
cin >> a;
if (a == 0) T = NULL;
else {
T = new TreeNode;
T->data = a;
BuildTree(T->lchild);
BuildTree(T->rbrother);
}
}
void Traverse(pin T) {
queue<pin> Q;
Q.push(T); //把根结点入队,为了后面便于找他的左孩子
cout << T->data << ' ';//输出根节点的值
while (!Q.empty()) {
pin q = Q.front()->lchild;//让指针指向队头元素的左孩子
Q.pop();//弹出队头元素,因为没有用了
while(q) { //当p指针不为空时
cout << q->data << ' ';
Q.push(q);
q = q->rbrother;
}
}
}
int main() {
pin T;
BuildTree(T);
Traverse(T);
return 0;
}