2001NOIP普及组真题 3. 求先序排列

2024-06-12 04:36

本文主要是介绍2001NOIP普及组真题 3. 求先序排列,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

线上OJ:

线上OJ:
【01NOIP普及组】求先序排列

核心思想:

1、先构建二叉树,再按照要求输出
2、构建的方法,可以使用字符数组,也可以使用字符串
3、构建树的核心是:通过递归,根据后序遍历和中序遍历构建树

第一步、后序遍历的最后一个一定是根 第二步、在中序遍历中找到根 第三步、根左侧的都为左子树,右侧的都为右子树。对左子树和右子树分别再次递归

传入参数说明:
int left_pos, int right_pos:待构建树的后序遍历的左右坐标
int left_mid, int right_mid:待构建树的中序遍历的左右坐标

解法一、使用字符数组 构建树

#include <bits/stdc++.h>
using namespace std;const int N = 105;
char s1[N], s2[N];  // s1 中序遍历,s2 后序遍历 struct Node
{char val;			// 存储当前节点的编号 int lchild, rchild;	// 存储当前节点的左子树和右子树节点的编号 
};
Node node[N];
int p = 0; 	// 存储节点编号 /*
核心思想:通过递归,根据后序遍历和中序遍历创建树第一步、后序遍历的最后一个一定是根第二步、在中序遍历中找到根第三步、根左侧的都为左子树,右侧的都为右子树。对左子树和右子树分别再次递归 传入参数说明:int left_pos, int right_pos:待构建树的后序遍历的左右坐标int left_mid, int right_mid:待构建树的中序遍历的左右坐标
*/ 
int creatTree(int left_pos, int right_pos, int left_mid, int right_mid)
{if(left_pos > right_pos || left_mid > right_mid) 	// 判断递归退出边界 return 0;int id = ++p;	// 如果没越界,则为该节点创建新的编号node[id].val = s2[right_pos]; 	// 第一步、后序遍历的最后一个一定是根 // 第二步、在中序遍历中找到根。根左侧的都为左子树,右侧的都为右子树int len;	// 当找到根节点后,用于存储左子树的长度 int i; 		// 用于存储找到的根的下标 for(i = left_mid; i <= right_mid; i++){if( s1[i] == s2[right_pos] ) // 在中序遍历中找根(根为后续遍历的最后一个) {len = i - left_mid;  // i坐标前面的,是左子树的长度 break;}			} 	// 第三步、根左侧的都为左子树,右侧的都为右子树。对左子树和右子树分别再次递归。node[id].lchild = creatTree(left_pos, left_pos + len - 1, left_mid, i - 1);node[id].rchild = creatTree(left_pos + len, right_pos - 1, i + 1, right_mid);return id;
} void preOrder(int id)
{if(id == 0)   return;cout << node[id].val;preOrder(node[id].lchild);preOrder(node[id].rchild);
}int main()
{cin >> s1 >> s2;int len_s1 = strlen(s1);int len_s2 = strlen(s2);int root = creatTree(0, len_s2 - 1, 0, len_s1 - 1);	// 创建树 preOrder(root);	// 后续遍历输出节点 	
}

解法二、使用字符串 构建树

#include <bits/stdc++.h>
using namespace std;const int N = 105;
string s1, s2;  // s1 中序遍历,s2 后序遍历 struct Node
{char val;			// 存储当前节点的编号 int lchild, rchild;	// 存储当前节点的左子树和右子树节点的编号 
};
Node node[N];
int p = 0;	// 存储节点编号int createTree(string sp, string si)//用后序序列sp与中序序列si构建二叉树,返回树根 
{int id = ++p;node[id].val = sp[sp.length()-1];int i = si.find(node[id].val);		// 在中序遍历中找到根,利用 string的 s.find() 函数即可,返回即为索引 int len_l = i, len_r = si.length() - 1 - i;//左右子树序列长度 //序列长度大于0,才可以建立一棵树if(len_l > 0)  node[id].lchild = createTree(sp.substr(0, len_l), si.substr(0, len_l));if(len_r > 0)  node[id].rchild = createTree(sp.substr(len_l, len_r), si.substr(len_l + 1, len_r));return id;
}void preOrder(int id)
{if(id == 0)   return;cout << node[id].val;preOrder(node[id].lchild);preOrder(node[id].rchild);
}int main()
{cin >> s1 >> s2;int root = createTree(s2, s1);preOrder(root);return 0;
}

解法三、不构建树,直接输出

#include <bits/stdc++.h>
using namespace std;string s1, s2;  // s1 中序遍历,s2 后序遍历 void calc(int left_pos, int right_pos, int left_mid, int right_mid)
{cout << s2[right_pos];  // 这一行的位置决定了输出结果是先序遍历还是后序遍历int i = s1.find(s2[right_pos]);		// 第一步、后序遍历的最后一个一定是根。根据这个“根”,在中序遍历中找到其坐标 i int len = i - left_mid;// 在中序遍历中,如果 i 不是左边界(即 i > left_mid),则说明有左子树。需要进行递归if(i > left_mid) calc(left_pos, left_pos + len - 1, left_mid, i - 1);	// 在中序遍历中,如果 i 不是右边界(即 i < right_mid),则说明有右子树。需要进行递归 if(i < right_mid) calc(left_pos + len, right_pos - 1, i + 1, right_mid);// cout << s2[right_pos];  // 这一行的位置决定了输出结果是先序遍历还是后序遍历。放在此处就是后序遍历
}int main()
{cin >> s1 >> s2;calc(0, s2.length() - 1, 0, s1.length() - 1);cout << endl;return 0;
}    

这篇关于2001NOIP普及组真题 3. 求先序排列的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1053210

相关文章

PHP字符串全排列

方法一: $str = 'abc';$a =str_split($str);perm($a, 0, count($a)-1);function perm(&$ar, $k, $m) {if($k == $m){ echo join('',$ar), PHP_EOL;}else {for($i=$k; $i<=$m; $i++) {swap($ar[$k], $ar[$i]);perm($ar

回溯——9.全排列

力扣题目链接 给定一个 没有重复 数字的序列,返回其所有可能的全排列。 示例: 输入: [1,2,3]输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ] 解题思路 问题建模:题目要求给出一个数组的所有排列组合,属于典型的全排列问题,这可以用回溯法来解决。回溯法通过递归的方式,依次将数组中的每个元素放入排列中,直到生成

代码随想录刷题day25丨491.递增子序列 ,46.全排列 ,47.全排列 II

代码随想录刷题day25丨491.递增子序列 ,46.全排列 ,47.全排列 II 1.题目 1.1递增子序列 题目链接:491. 非递减子序列 - 力扣(LeetCode) 视频讲解:回溯算法精讲,树层去重与树枝去重 | LeetCode:491.递增子序列_哔哩哔哩_bilibili 文档讲解:https://programmercarl.com/0491.%E9%80%92%E

python 实现第k个字典排列算法

第k个字典排列算法介绍 "第k个字典排列"算法通常指的是在给定的字符集合(例如,字符串中的字符)中,找到所有可能排列的第k个排列。这个问题可以通过多种方法解决,但一个常见且高效的方法是使用“下一个排列”算法的变种,或称为“第k个排列”的直接算法。 方法一:使用“下一个排列”的变种 生成所有排列:首先生成所有排列,但显然这种方法对于较大的输入集合是不切实际的,因为它涉及到大量的计算和存储。 排序

笔试强训,[NOIP2002普及组]过河卒牛客.游游的水果大礼包牛客.买卖股票的最好时机(二)二叉树非递归前序遍历

目录 [NOIP2002普及组]过河卒 牛客.游游的水果大礼包 牛客.买卖股票的最好时机(二) 二叉树非递归前序遍历 [NOIP2002普及组]过河卒 题里面给的提示很有用,那个马的关系,后面就注意,dp需要作为long的类型。 import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息publ

C++解决:求排列数

描述 输入两个整数m,n,求m个数字中选n个数的排列数。(1<=n<=m<=50) 输入描述 两个正整数m和n。 输出描述 一个正整数表示排列数。 用例输入 1  6 5 用例输出 1  720 AC code #include<bits/stdc++.h>using namespace std;int fun(int n) {int sum=1;for(int i=n

找不同-第15届蓝桥省赛Scratch初级组真题第4题

[导读]:超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成,后续会不定期解读蓝桥杯真题,这是Scratch蓝桥杯真题解析第183讲。 如果想持续关注Scratch蓝桥真题解读,可以点击《Scratch蓝桥杯历年真题》并订阅合集,查阅教程更方便。 第15届蓝桥杯省赛已于2024年8月24日落下帷幕,编程题一共有5题,分别如下: 猪八戒落地 游乐场 画西瓜 找不同 消

每日一题~cf 970 div3 (A思维,B小模拟,C二分,D排列数建图成环,E 26个字母暴力+前缀和,F 逆元,G 数论gcd )

A 题意: 有 a 个1 ,b 个2.问是否能将这些数划分为两个数值相等的集合。 输出 YES 或者 NO —————— 问题等价于 将数组 分成两个数值相同的数组。所以sum 应该是偶数。也就是说 1 的个数是偶数。在i1的个数是偶数的情况下,将 2 分成两份,如果2 的个数是偶数,OK。如果是奇数那么需要1来补齐,如果1 的个数大于等于2那么可以补齐。(1 的个数是偶数,需要2个1来补齐,剩下

Leetcode刷题笔记:全排列

这是一个经典的回溯问题,下面是一个C++版本的解法: class Solution {public:void backtrack(vector<vector<int>>& res, vector<int>& nums, int start) {// 如果start到达nums的末尾,说明已经生成一个完整的排列if (start == nums.size()) {res.push_back(

再谈全排列

题目链接: . - 力扣(LeetCode) 每次做全排列的题目,我都要孕育好一阵子,到底怎么去思考这个问题呢? 首先,我觉得最好的方式就是画个树。 画了树之后,你就知道,这个问题,是一个循环遍历的问题,但是再遍历的过程中,你需要基于过去的状态(哪些元素被存储了),改变你之后的行为。 此外。我们还需要考虑终止条件,然后,这是一个回溯的问题,那么你需要考虑的就是回溯之后需要怎样的