知识点
一、栈的应用
case1:括号匹配问题
- 栈里放左括号的位置,遇到一个右括号就出栈,若栈为空说明该右括号匹配失败,最终栈里剩余的都是匹配失败的左括号。
- s.top()才能取元素,s.pop()只是单纯出栈
#include<iostream>
#include<stack>
using namespace std;
stack<int>s;//存左括号所在位置
int main()
{
string a,b;
while(cin>>a)
{
b=a;//单纯为了生成一个与输入等长的串
for(int i=0; i<a.length(); i++)
{
b[i]=' ';//初始化
if(a[i]=='(')
s.push(i);
if(a[i]==')')
{
if(s.empty())//若为空则说明匹配失败,想想栈的机理,和括号匹配是一样
b[i]='?';
else s.pop();//不为空则把左括号出栈
}
}
while(!s.empty())
{
b[s.top()]='$';
s.pop();//pop是没有返回值,所以用top取值
}
cout<<a<<endl<<b<<endl;
}
return 0;
}
step1,将中缀表达式转为后缀表达式,这里复习一下数据结构/编译原理的知识,下面这个方法是十分简易的理解方法,可以看到,本质是确定了优先级(所以下面的方法中全部加上括号),于是前后缀表达式可以不带括号唯一确定表达式:
那么具体的实现方式如下:例子
遍历中缀表达式
{
if(数字)
加入后缀表达式
else if('(')
入栈
else if(')')
依次将栈中元素出栈并加入到后缀表达式,直到遇到'(',并将其从栈中删除
else if(运算符op) {
while(栈不空,op优先级<=栈顶)//设置"("优先级最低
{
依次出栈加入到后缀表达式
(即直到遇到比op优先级低的运算符或左括号或栈空为止)
}
op入栈
}
}
step2,计算后缀表达式:遍历,数字就入栈,遇到op就pop两个数字(先出栈的是第二个op数!)形成ans入栈,最后的数字就是结果。完整实现减下方代码。注意的点如下:
- 用struct 来作为queue和stack对象很巧妙,省去了一个stack,并且和queue一起操作浑然一体,用f标记是数字or OP可以在利用后缀表达式计算时,直接用f判断是数字还是op。
- change函数用于转后缀表达式,就是利用上面的算法。需要注意的是每一步需要单独i++,我处理括号时就忘了;其次在遇到op时,很巧妙的做法就是直接先while(!s.empty() && opm[str[i]] <= opm[s.top().op]) 进行出栈入队,这里的while也就是if的功效了,反正最后都要op入栈,就在while后面统一入栈就好,十分简洁。
- 用map做优先级映射,我将"("的优先级设为最低,就是为了在这里“遇到栈顶是“(”也入栈”。
- 这里关键还是理解,op入栈必须是栈顶优先级比该op高才入栈,一样的优先级也是需要让先入栈的先计算
- 最后需要将stack里op全部出栈
- 队列可以直接赋值queue<node>p=q 我是实现show利用的
- cal是计算后缀表达式,关键一点是先出栈的是第一个运算数!
- main中
- Input用了逗号表达式,就是在cin后,直接判断输入的值是否是终止标志,如果是直接退出循环,不需要再if break了
- 用string的迭代器,再erase(it)来消除空格,或者用Index位置来消除
- 要对stack清空初始化(因为,cal结束直接return top,还没pop最后那个ans。没有clear函数)
- queue最后一定是空的,所以不需要清空
#include<iostream>
#include<string>
#include<stack>
#include<queue>
#include<map>
using namespace std;
struct node {
double n;//值
char op;//操作符
int f;//用于cal:1是数字,0是op。后缀表达式没有括号
};
string str;
stack<node>s;//char类型也无法存储长数字,除非用两个栈
queue<node>q;
map<char,int> opm;//操作符优先级的映射
void change() {
double n;
node t;
for(int i=0; i<str.length(); ) {
if(str[i]>='0' && str[i]<='9') {
t.f=1;//是数字
t.n=str[i++]-'0';
while(i<str.length() && str[i]>='0' && str[i]<='9') { //把完整的数字读入
t.n=t.n*10+(str[i++]-'0');//so目前程序不能处理小数。
//若想处理小数,则遇到小数点后的数字就要每次乘以0.1加到原数即可
}
q.push(t);
} else if(str[i]=='(') { //包括下面,忘记i++了,没往下走,导致内存爆炸
t.op='(';
s.push(t);
i++;
} else if(str[i]==')') {
while(!s.empty() && s.top().op!='(') { //好像这里不需要判空
q.push(s.top());
s.pop();
}
s.pop();//把左括号出栈
i++;
} else {
t.f=0;//是op
// if(opm[str[i]] <= opm[s.top().op])//为什么加上会报错?虽然没必要这句,但是也不应该报错啊
while(!s.empty() && opm[str[i]] <= opm[s.top().op]) { //栈顶优先级不低,就要先出栈,因为同级的也是先进的先算,也要出栈。一定不要忘了判空!
q.push(s.top());
s.pop();
}
t.op=str[i++];//是左括号或优先级高于栈顶都入栈,其他的最终也要入栈
s.push(t);//so 各种情况最后 都要入栈
}
}
while(!s.empty()) {
q.push(s.top());
s.pop();
}
}
void show_exp() {
queue<node>p=q;//不然把q清空了,后面没法算
while(!p.empty()) {
if(p.front().f==1)
cout<<p.front().n<<" ";
else if(p.front().f==0)
cout<<p.front().op<<" ";
p.pop();
}
cout<<endl;
}
double cal() {
node t;
while(!q.empty()) {
if(q.front().f==1) { //是数字
s.push(q.front());
}
if(q.front().f==0) { //是op
double t2=s.top().n;//先出栈的后运算!!
s.pop();
double t1=s.top().n;
s.pop();
if(q.front().op=='+') t.n=t1+t2;
if(q.front().op=='-') t.n=t1-t2;
if(q.front().op=='*') t.n=t1*t2;
if(q.front().op=='/') t.n=t1/t2;
t.f=1;//ans就是数字
s.push(t);
}
q.pop();
}
return s.top().n;
}
int main() {
opm['(']=0;//因为遇到左括号都入栈
opm['+']=opm['-']=1;
opm['*']=opm['/']=2;
while(getline(cin,str), str!="0") { //是0时,程序结束
for(string::iterator it=str.end(); it!=str.begin(); it--) { //去掉空格。倒着,因为最后有空格,起始没有
if(*it==' ') str.erase(it);//不用迭代器 用index也可以用位置erase
}
while(!s.empty()) s.pop();
change();
show_exp();
printf("%.2lf\n", cal());
}
return 0;
}
case3:判断出栈顺序是否合法
#include<iostream>
#include<stack>
using namespace std;
const int N=1005;
stack<int>st;
int a[N];
int main(){
int m,n,T;
cin>>m>>n>>T;
while(T--){
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
while(!st.empty()) st.pop();
int now=1;
int ok=1;
for(int i=1;i<=n;i++){
st.push(i);
if(st.size()>m) {
ok=0;
break;
}
while(!st.empty()&&st.top()==a[now]){//判空必须在前面写。。
st.pop();
now++;
}
}
if(ok && st.empty())
printf("YES\n");
else printf("NO\n");
}
return 0;
}
队列
case1:Mice and Rice 再做一遍。。。
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N=100005;
struct node {
int w;
int r;//挂了的rank就是组数+1,因为每组一个人晋级
} mo[N]; //mo[i]表示第i个老鼠
int np,ng;//总人数,每组最大人数
int main() {
cin>>np>>ng;
for(int i=0; i<np; i++)
cin>>mo[i].w;
queue<int>q;
for(int i=0,m; i<np; i++) {
cin>>m;
q.push(m);
}
int t=np;//每轮的人数,因为每次都是直接往q里继续放,用t来卡住该轮的人
while(q.size()!=1) {
int gn;//本轮的组数
if(t%ng==0) gn=t/ng;
else gn=t/ng+1;
for(int i=0; i<gn; i++) {
int k=q.front();
for(int j=0; j<ng; j++) {
if(i*ng+j>=t) break;//超过本轮人数了,卡断
if(mo[q.front()].w > mo[k].w) {
k=q.front();
}
mo[q.front()].r=gn+1;
q.pop();
}
q.push(k);
}
t=gn;
}
mo[q.front()].r=1;
for(int i=0; i<np-1; i++)
cout<<mo[i].r<<' ';
cout<<mo[np-1].r;
return 0;
}
链表
静态链表
case1:1032 Sharing
#include<iostream>
#include<cstring>
#include<stack>
using namespace std;
const int N=100005;
struct node{
char data;
int next;
int ap;//出现
}L[N];//L[i]表示i指向的元素
int main(){
int s1,s2,n;
scanf("%d %d %d",&s1,&s2,&n);
while(n--){
int a,b;
char c;
scanf("%d %c %d",&a,&c,&b);
// cin>>a>>c>>b;
L[a].next=b;
L[a].data=c;
}
for(int i=s1;i!=-1;i=L[i].next){//注意是i!--1,如果写next的话,如果刚开始就是-1,压根没有next呢
L[i].ap=1;
}
for(int i=s2;i!=-1;i=L[i].next){
if(L[i].ap==1){
printf("%05d",i);
return 0;
}
}
printf("-1");
return 0;
}
case2:Linked List Sorting,用的静态链表,利用有效节点排序的方法,学一下,还没理解透
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=100005;
const int INF=0x7fffffff;
struct node {
int addr,data,next;
int flg;
} L[N];
int cmp(node a,node b){
if(!a.flg || !b.flg)
return a.flg>b.flg;//大的放前面,也就是有效节点放前面
else return a.data<b.data;
}
int n,s1;//开始节点
int main() {
cin>>n>>s1;
for(int i=0; i<N; i++) L[i].flg=0; //无效节点
for(int i=0; i<n; i++) {
int a,b,c;
cin>>a>>c>>b;
L[a].addr=a;
L[a].data=c;
L[a].next=b;
}
int p=s1,cnt=0;
while(p!=-1) {
L[p].flg=1;//标注有效节点
p=L[p].next;
cnt++;
}
if(!cnt) printf("0 -1");
else {
sort(L,L+N,cmp);
printf("%d %05d\n",cnt,L[0].addr);
for(int i=0;i<cnt;i++){
if(i!=cnt-1){//不到最后一点
printf("%05d %d %05d\n",L[i].addr,L[i].data,L[i+1].addr);
}
else printf("%05d %d -1\n",L[i].addr,L[i].data);
}
}
return 0;
}
哈夫曼树
是带权路径长度和最小的二叉树(最优二叉树),对应的编码是平均编码长度最短的编码。而带权路径和的求法,按照定义每个原始节点的的值 * 深度,求和,其实也就是所有中间节点的和,以此方便编程实现。
case1:哈夫曼树。关键是利用优先队列(小顶堆)动态维持升序排列,another case:搬水果 关键是想到用哈夫曼树!
#include<iostream>
#include<queue>
using namespace std;
priority_queue<int, vector<int>, greater<int>>q;//小顶堆
int main()
{
int n;
while(scanf("%d", &n)!=EOF)
{
while(!q.empty()) q.pop();//其实是最后剩一个数,也可在每个case的末尾pop
for(int i=0;i<n;i++)
{
int x;
cin>>x;
q.push(x);
}
int ans=0;
while(q.size()>1)//最后剩下一个,so到1个元素时终止
{
int a=q.top();
q.pop();
int b=q.top();
q.pop();
q.push(a+b);
ans+=a+b;//把每个中间结果相加就是 带权路径和
}
printf("%d\n", ans);
}
return 0;
}
二叉树(遍历、还原建树、完全二叉树)
case1:二叉树遍历(知前中 求后序遍历)。这是机好的例子,包括了Tree的数据结构、创建、遍历,以及由两遍历还原树的方法,还有需要注意的点如下:
- (*a).b 等价于 a->b。so,node *T的T->data,而node T可以直接T.data
- 数据结构就是data、2 child,需要层次遍历还有layer
- node *creat()函数创建树节点。《王道》用loc记录Tree数组的index,主要是需要设置左右子树为NULL,虽然后面不会直接用到,但这本质是静态分配存储树的内存空间,我试了不用数组的话,在creat函数写node *T=new node后return T(这就是动态内存分配,需要多少开多少内存,静态必须实现申请到位)不然就要return &Tree[loc++];
- 本题核心就是如何根据前中序遍历,还原tree:
- 首先观察发现,方法其实就是根据前序遍历第一个元素,然后在中序遍历中找到对应的元素位置,其左右边分别是左右子树(可以根据位置在前序遍历中,确定左右子树相应的序列),最后对子树也用相同方法,递归实现tree的还原
- 具体实现是通过build函数对str序列切分,通过str的坐标(如[str[s1],str[e1]])划分出当前要还原的部分的前中遍历,用rootidx标记中序遍历中的子树的根位置,画图看(就能方便看出 如rootidx-s2就是左子树序列的长度)
《算法笔记》 上面真的写的很好理解啊:这是后续+中序建树的
#include<iostream>
#include<cstring>
using namespace std;
struct node {
char data;//根据要求改数据类型
node *lchild;
node *rchild;
int layer;//有时候需要知道层数,eg层次遍历。本题暂时不需要
};
string str1,str2;//分别放前中遍历的字符串
node *creat() {
node *T=new node;//一定要new,意思是分配内存
T->lchild=T->rchild=NULL;//左右子树为空
return T;
}
void post_order(node *T) {//T是树根的指针
if(T->lchild!=NULL)//不能写T.lchild,因为T是指针必须用->,若不是指针可以用T.xxx
post_order(T->lchild);
if(T->rchild!=NULL)
post_order(T->rchild);
printf("%c", T->data);
}
node *build(int s1,int e1, int s2,int e2) {//str1中[s1,e1]为前序,str2中[s2,e2]为中序
node *T=creat();
T->data=str1[s1];//前序遍历的第一个字符是root
int rootidx;//root在str2的位置
for(int i=s2; i<=e2; i++) {
if(str2[i]==T->data) {
rootidx=i;
break;
}
}
//利用中序遍历,来判断左右子树是否为空。
if(rootidx!=s2) //若rootidx就是最左边的元素,说明没有左子树了
//rootidx-s2就是左子树序列长度
T->lchild=build(s1+1,s1+(rootidx-s2), s2,rootidx-1);
if(rootidx!=e2)
T->rchild=build(s1+1+(rootidx-s2),e1, rootidx+1,e2);
return T;
}
int main() {
while(cin>>str1>>str2) {
node *T=build(0,str1.length()-1, 0,str2.length()-1);//build后,相当于将原数保存在Tree数组中了
post_order(T);//T是这棵树的root的地址
printf("\n");
}
return 0;
}
case1.5:Tree Traversals Again,用栈模拟树的遍历,有点巧吧。。说是push是先序,pop是中序
#include<iostream>
#include<stack>
#include<vector>
using namespace std;
struct node {
int data;
node* lchild,*rchild;
};
int n,fir=0;
vector<int>preS,inS;
stack<int>st;
node* create() {
node *T=new node;
T->lchild=T->rchild=NULL;
return T;
}
void postOrder(node *T) {
if(T->lchild!=NULL) postOrder(T->lchild);
if(T->rchild!=NULL) postOrder(T->rchild);
if(!fir) {
cout<<T->data;
fir=1;
} else cout<<' '<<T->data;
}
node* build(int preL,int preR, int inL,int inR) {
node *T=create();
T->data=preS[preL];//cout<<T->data<<' ';
int k;
for(k=inL; k<=inR; k++) {
if(inS[k]==T->data)
break;
}
int numL=k-inL;
if(k>inL) T->lchild=build(preL+1,preL+numL,inL,k-1);
if(k<inR) T->rchild=build(preL+numL+1,preR,k+1,inR);
return T;
}
int main() {
cin>>n;
for(int i=0,j; i<2*n; i++) {
string a;
cin>>a;
if(a=="Push") {
cin>>j;
preS.push_back(j);
st.push(j);
} else {
inS.push_back(st.top());
st.pop();
}
}
node *T=build(0,n-1,0,n-1);
postOrder(T);
return 0;
}
case2:二叉树遍历(先序建数 求中序遍历)
- 先序遍历建树:a#b#cdef##### 的结果如右图
;意思就是一个个读元素,按照先序顺序把元素添上去,当然了,像这里a都没有左子树的,就从右子树开始往下了,
- 而编程就是通过递归实现:这里我用全局变量idx标记遍历到str哪个位置,每次递归都++即可,因为这里给的都是恰好能把每个子树都遍历到叶子节点,我就没有考虑idx会越str的界的情况
int idx;//标记str的index
string s;
node *creat() {
node *T=new node;//一定要new,意思是分配内存
T->lchild=T->rchild=NULL;//左右子树为空
return T;
}
node *build(char c) {
node *T=creat();//指针
if(c=='#')
T=NULL;
else {
T->data=c;
T->lchild=build(s[++idx]);
T->rchild=build(s[++idx]);
}
return T;
}
void in_order(node *T) {
if(T->lchild!=NULL)
in_order(T->lchild);
printf("%c ", T->data);
if(T->rchild!=NULL)
in_order(T->rchild);
}
int main() {
while(cin>>s) {
idx=0;//输入每个样例都要初始化
node *T=build(s[0]);
in_order(T);
printf("\n");
}
return 0;
}
完全二叉树
case1:树查找(完全二叉树,求指定层的所有元素)。一开始还惯性思维想怎么建树,可以在数组中利用2次方这个位置关系建树,不过后来想到其实利用位置关系来操作数组即可,非常简单。
#include<iostream>
#include<cmath>
using namespace std;
int a[1005];//2^10才1024,so最深11层
int n,x;
int main() {
while(cin>>n,n!=0) {
for(int i=1; i<=n; i++)//自己画一个完全二叉树,一般从1开始
cin>>a[i];
cin>>x;
if(pow(2,x-1)>n) printf("EMPTY");
else for(int i=pow(2,x-1); i<pow(2,x); i++) {
printf("%d ", a[i]);
}
printf("\n");
}
return 0;
}
case2:完全二叉树,共n个节点 求结点m所在的子树中一共包括多少个结点。其实是模拟,画图找规律,会发现这个主要需要知道,最大层数与m所在层数的差d,so最多也就把每一层的值加起来就行,由首项为1,公比为2的等比数列求和公式可以得到,最多有2^(d-1)-1个节点,但是还会有最下面一层不满的情况,这个可以根据,m的子树中最大的节点序号(找规律得其值为m*2^d+2^d-1)与n的值比较,若n小则说明不满,就需要先算到倒数第二层 2^d-1,再加上最后一层n-m*2^d+1。
另外,因为刚开始我可能TLE了(显示WA),原因可能是log或pow用的太多,于是我发现整个用的最多的就是2^d这一个pow,虽然各处用的原因不同(有的是求和公式化简来的,有的表示m的子树最下层最大节点数),但这个可以让程序只运行一次pow,从而所短时间,故用一个delta_l记录这个值,也是一个小技巧吧,最后也AC了,下面是我的code:
#include<iostream>
#include<cmath>//pow在这里
#include<algorithm>//min在这里
using namespace std;
typedef long long ll;
ll delta_l;//l1 l2都不要,只要知道差就行啦
ll pow_delta_l;//2^delta_l 这个会多次用到
ll m,n,lastn;//要查元素序号,最大元素序号,求要查元素的子树中最大的元素序号(也就是在l2层上最大的序号
int main() {
while(cin>>m>>n, m!=0) {
ll sum=0;
delta_l=ll(log(n*1.0/m)/log(2));//这个不能在ll外打括号!不然只是单纯的类型变了,值还是错的??
pow_delta_l=pow(2,delta_l);
lastn=m*pow_delta_l+pow_delta_l-1;//先根据2次方关系,算到最下面一层的第一个元素,然后加上剩下的元素书
if(n>=lastn) sum=2*pow_delta_l-1;//等比数列的n是元素数量,不是差值
else {
sum+=pow_delta_l-1;//a1*(1-q^n)/(1-q)化简
sum+=n-m*pow_delta_l+1;
}
printf("%lld\n", sum);
}
return 0;
}
后来发现有更巧妙的做法,利用递归,一层层算,代码量极少,但耗时,不过递归的使用值得学习
#include<stdio.h>
int countNode(int m,int n){
if(m>n) return 0;
return countNode(2*m,n)+countNode(2*m+1,n)+1;
}
int main(){
int m,n;
while(scanf("%d%d",&m,&n)!=EOF){
if(m==0&&n==0) break;
printf("%d\n",countNode(m,n));
}
return 0;
}
反转二叉树case1Invert a Binary Tree:
#include<iostream>
#include<queue>
using namespace std;
const int N=105;
struct node {
// int data;
int lchild,rchild;
int isroot;
} tree[N];
int n,fir1=0,fir2=0;
void rev(int T) { //反转
if(T==-1) return ;
rev(tree[T].lchild);
rev(tree[T].rchild);
swap(tree[T].lchild,tree[T].rchild);
}
void BFS(int T) {
queue<int>q;
q.push(T);
while(!q.empty()) {
int t=q.front();
q.pop();
if(!fir1) {
fir1=1;
printf("%d", t);
} else printf(" %d", t);
if(tree[t].lchild!=-1) q.push(tree[t].lchild);
if(tree[t].rchild!=-1) q.push(tree[t].rchild);
}
}
void inOrder(int T) {
if(tree[T].lchild!=-1) inOrder(tree[T].lchild);
if(!fir2) {
fir2=1;
printf("%d", T);
} else printf(" %d", T);
if(tree[T].rchild!=-1) inOrder(tree[T].rchild);
}
int main() {
cin>>n;
for(int i=0; i<n; i++) tree[i].isroot=1;
for(int i=0; i<n; i++) {
char c ;
cin>>c;
if(c=='-') tree[i].lchild=-1;
else {
tree[i].lchild=c-'0';
tree[c-'0'].isroot=0;
}
cin>>c;
if(c=='-') tree[i].rchild=-1;
else {
tree[i].rchild=c-'0';
tree[c-'0'].isroot=0;
}
}
int root=0;
for(; tree[root].isroot!=1; root++); //找到root
rev(root);
BFS(root);
printf("\n");
inOrder(root);
return 0;
}
二叉排序/搜索树
case1:二叉排序树(按二叉排序树建树,然后输出前中后遍历)。主要就是利用要插的数<当前data值,则插入左子树否则插入右子树。编程实现有些技巧
- node &insert函数有node *T和int x两个参数,而不是之前build函数不需要将*T传过来;且insert函数这里是在循环里一个个insert,不能像之前build在main一下就行,分析如下:
- 排序树每次都要比较当前节点与要插的值x的大小,so函数必须有T;而每次要插的值只有一个,并且需要不断递归找到其真正的位置,所以每次递归时,要插的数不变so不能在递归时改变x,但根据某序遍历时,建树过程要每次str[++i]的(典型的就是先序建树,有#代表空节点,so每次都要移动str的位置;而根据前中遍历还原树时,也是每次会改变切分str的坐标;但这里要插的x一直不变,so要用循环一次次插)
- 刚开始T赋值为NULL,因为在后面判断子树时,也是要判断NULL的,所以这样能分辨出该数第一个元素是否为空,从而进行相应的操作,so不能在main就node *T=creat(),不然进insert不好判断了(当然了,这样写也OK,在insert函数里判断data是否已经赋值也可以判断树是否为空,eg将T->data初始化为INF?)
//没写struct node、creat函数、前中后序遍历函数了
node *insert(node *T, int x) {
if(T==NULL) {//在判断到子树的时候,开始也是NULL。所以main里,整个树的第一个节点也是初始化为NULL
T=creat();
T->data=x;
} else if(x<T->data) {
T->lchild=insert(T->lchild,x);
} else if(x>T->data) {//本题说不要输出重复元素,不考虑相等的值。否则插哪都一样
T->rchild=insert(T->rchild,x);
}
return T;
}
int main() {
int n,x;
while(cin>>n) {
node *T=NULL;//不写creat为了判断是第一个节点,在insert函数里面再creat
for(int i=0; i<n; i++) {
cin>>x;
T=insert(T,x);//这样才能不断对 T更新
}
preorder(T); printf("\n");
inorder(T); printf("\n");
postorder(T); printf("\n");
}
return 0;
}
case2:二叉搜索树 判断两排序树是否相等(前中或中后两遍历结果相同->即说明两树相同)。这道题知道这个就不难做了,就是通过和case1一样的方法,通过排序树序列建树,然后分别将要比较的两棵树的 前中遍历str相加,比较两者是否相等即可。需要注意的是遍历时,注意不同的数要用不同的str分别记录,我这里用k来标记要算哪个树的前中遍历序列
//struct node、creat、insert和上面case完全一样
string s0,s;//记录排序树序列。每个样例对应每个s,都和s0比
string s1,s2;//记录两个树的 前中遍历序列
node *preorder(node *T,int k) {
if(T!=NULL) {
if(k==1) s1+=T->data;
else if(k==2) s2+=T->data;
}
if(T->lchild!=NULL) preorder(T->lchild,k);
if(T->rchild!=NULL) preorder(T->rchild,k);
}
node *inorder(node *T,int k) {
if(T->lchild!=NULL) inorder(T->lchild,k);
if(T!=NULL) {
if(k==1) s1+=T->data;
else if(k==2) s2+=T->data;
}
if(T->rchild!=NULL) inorder(T->rchild,k);
}
int main() {
int n;
while(cin>>n>>s0, n!=0) {
s1="";
node *T1=NULL;
for(int i=0; i<s0.length(); i++) {
T1=insert(T1,s0[i]);
}
preorder(T1,1);
inorder(T1,1);
while(n--) {
s2="";
cin>>s;
node *T2=NULL;
for(int i=0; i<s.length(); i++) {
T2=insert(T2,s[i]);
}
preorder(T2,2);
inorder(T2,2);
// cout<<s1<<" "<<s2<<endl;
if(s1==s2) printf("YES\n");
else printf("NO\n");
}
}
return 0;
}
case3:1043Is It a Binary Search Tree,判断给定序列建的BST是不是BST或者镜像BST的preOrder,如果是,输出该tree的postOrder
- 求镜像tree的遍历就是在原本某序遍历的基础上,先rchild再lchild即可,所以这题两个方案:
- 建两个tree,一个按照升序,一个降序,分别处理就行,traverse函数还是前后遍历两个
- 一个tree,只是traverse的时候要写mirror的前后遍历函数
#include<iostream>
#include<vector>
using namespace std;
struct node {
int data;
node *lchild;
node *rchild;
};
vector<int>ori,pre,mir_pre,ans;//ans放正确的树的后序遍历
node *create() {
node *T=new node;
T->lchild=T->rchild=NULL;
return T;
}
node *insert(node *T, int x) {
if(T==NULL) {
T=create();
T->data=x;
} else if(x<T->data)
T->lchild=insert(T->lchild,x);
else
T->rchild=insert(T->rchild,x);
return T;
}
void preOrder(node *T) {
if(T!=NULL) pre.push_back(T->data);
if(T->lchild!=NULL) preOrder(T->lchild);
if(T->rchild!=NULL) preOrder(T->rchild);
}
void mir_preOrder(node *T) {
// swap(T->lchild,T->rchild);
if(T!=NULL) mir_pre.push_back(T->data);
if(T->rchild!=NULL) mir_preOrder(T->rchild);
if(T->lchild!=NULL) mir_preOrder(T->lchild);
}
void postOrder(node *T) {
if(T->lchild!=NULL) postOrder(T->lchild);
if(T->rchild!=NULL) postOrder(T->rchild);
if(T!=NULL) ans.push_back(T->data);
}
void mir_postOrder(node *T) {
if(T->rchild!=NULL) mir_postOrder(T->rchild);
if(T->lchild!=NULL) mir_postOrder(T->lchild);
if(T!=NULL) ans.push_back(T->data);
}
int main() {
int n,x;
cin>>n;
node *T=NULL;
for(int i=0;i<n;i++) {
scanf("%d",&x);
ori.push_back(x);
T=insert(T,x);
}
preOrder(T);
mir_preOrder(T);
if(ori!=pre && ori!=mir_pre) {
printf("NO");
return 0;
}
if(ori==pre) postOrder(T);
else if(ori==mir_pre) mir_postOrder(T);
printf("YES\n");
for(int i=0; i<n-1; i++)
printf("%d ",ans[i]);
printf("%d",ans[n-1]);
return 0;
}
树
case1:1090Highest Price in Supply Chain,好题!关键是懂得将其抽象为树。下面有DFS与BFS两方法
#include<iostream>
#include<cmath>
#include<queue>
using namespace std;
const int N=100005;
vector<int>tree[N];//范围就是0~n-1
int n,maxDep=0,num=0;//深度不能init为-1,因为有0的情况
double p,r;
void DFS(int T,int dep) { //如果用BFS,可以直接
if(tree[T].size()==0) { //到叶子Node了
if(dep>maxDep) {
maxDep=dep;
num=1;
} else if(dep==maxDep) num++;
return ;
}
for(int i=0; i<tree[T].size(); i++) {
DFS(tree[T][i],dep+1);
}
}
int layer[N]= {0};
void BFS(int T) {
queue<int>q;
q.push(T);
while(!q.empty()) {
int t=q.front();
q.pop();
for(int i=0; i<tree[t].size(); i++) {
layer[tree[t][i]]=layer[t]+1;
maxDep=layer[tree[t][0]];
q.push(tree[t][i]);
}
}
}
int main() {
cin>>n>>p>>r;
int root;
for(int i=0,a; i<n; i++) {
cin>>a;
if(a==-1) root=i;
else tree[a].push_back(i);
}
// DFS(root,0);//只要知道最大层数就行了,最后pow一下
BFS(root);
for(int i=0; i<n; i++) {
if(layer[i]==maxDep) num++;
}
printf("%.2lf %d", p*pow(1+r/100,maxDep),num);
return 0;
}