今天学习了二叉树,了解了二叉树的创建和遍历的过程
今天所了解的遍历过程主要分为三种,前序中序和后序,都是DFS的想法
前序遍历:先输出在遍历左节点和右节点(输出->左->右)
中序遍历:先遍历左节点,再输出和遍历右节点(左->输出->右)
后序遍历:先遍历左节点和右节点,最后再输出(左->右->输出)
#define TElemType char
typedef struct BiNode{
TElmeType data;//数据
struct BiNode *left,*right;//节点的左右指针
}BiNode,*BiTree;
void preOrderTraverse(BiTree T)
{
if (T==NULL)return ;
printf("%c",T->data);
preOrderTraverse(T->left);
preOrderTraverse(T->right);
//前序遍历中-左-右,先输出,再遍历左和右
}
void InOrderTraverse(BiTree T)
{
if (T==NULL)return ;
//中序遍历左-中-右
InOrderTraverse(T->left);//先遍历左节点
cout<<T->data<<endl;
InOrderTraverse(T->right);
}
void PostOrderTraverse(BiTree T)
{
if (T==NULL)return ;
//后序遍历左右中
PostOrderTraverse(T->left);
PostOrderTraverse(T->right);
cout<<T->data<<endl;
}
void CreatTree(BiTree *T)
{
TElmeType x;
cin>>x;
if (x=='*')
{
T=NULL;
return;
}
*T=new BiNode;
//T=(BiNode*)malloc(sizeof (BiNode));
*T->data=x;
CreatTree(&(*T)->left);
CreatTree(&(*T)->right);
}
bool isempty(BiTree T)
{
return T==NULL;
}
然后尝试了一道关于树的题目
https://www.luogu.com.cn/problem/P1087
题目描述
我们可以把由 0 和 1 组成的字符串分为三类:全 0 串称为 B 串,全 1 串称为 I 串,既含 0 又含 1 的串则称为 F 串。
FBI 树是一种二叉树,它的结点类型也包括 F 结点,B 结点和 I 结点三种。由一个长度为 2�2N 的 01 串 �S 可以构造出一棵 FBI 树 �T,递归的构造方法如下:
- �T 的根结点为 �R,其类型与串 �S 的类型相同;
- 若串 �S 的长度大于 11,将串 �S 从中间分开,分为等长的左右子串 �1S1 和 �2S2;由左子串 �1S1 构造 �R 的左子树 �1T1,由右子串 �2S2 构造 �R 的右子树 �2T2。
现在给定一个长度为 2�2N 的 01 串,请用上述构造方法构造出一棵 FBI 树,并输出它的后序遍历序列。
输入格式
第一行是一个整数 �(0≤�≤10)N(0≤N≤10),
第二行是一个长度为 2�2N 的 01 串。
输出格式
一个字符串,即 FBI 树的后序遍历序列。
输入输出样例
输入 #1复制
3 10001011
输出 #1复制
IBFBBBFIBFIIIFF
说明/提示
对于 40%40% 的数据,�≤2N≤2;
对于全部的数据,�≤10N≤10。
这道题就是先按照题目中给出的方法,不断分治找到当串的长度只有1的时候,创建节点,然后再分散开建立新的节点,为了便于得到左右节点的位置,可以设置新的函数
int ls(int p){
return p*2;
}
int rs(int p){
return p*2+1;
}
等完成树的创建后,最后再一个后序遍历结束
#include<bits/stdc++.h>
using namespace std;
char s[1024],tree[4096];
int ls(int p){
return p*2;
}
int rs(int p){
return p*2+1;
}
void build_FBItree(int p,int l,int r)
{
if (l==r)
{
if (s[r]=='1')tree[p]='I';
else tree[p]='B';
return;
}
int mid=(l+r)/2;
build_FBItree(ls(p),l,mid);
build_FBItree(rs(p),mid+1,r);
if (tree[ls(p)]=='B' && tree[rs(p)]=='B')tree[p]='B';
else if (tree[ls(p)]=='I' && tree[rs(p)]=='I')tree[p]='I';
else tree[p]='F';
}
void PostOrderTraverse(int p)
{
if (tree[ls(p)])PostOrderTraverse(ls(p));
if (tree[rs(p)])PostOrderTraverse(rs(p));
printf("%c",tree[p]);
}
int main()
{
int n;
cin>>n;
//cin>>s;
scanf("%s",s+1);
build_FBItree(1,1,strlen(s+1));
PostOrderTraverse(1);
return 0;
}
第二题是有关于搜索的
https://www.lanqiao.cn/problems/644/learning/?page=1&first_category_id=1&problem_id=644
这道题是需要找到所有的对称分割方法,可以轻松的知道,分割线一定是经过对称中心的,因此可以把起始点设置在中心对称处,然后开始向四个方向搜索,当碰到边界的时候就是一种结果
#include<bits/stdc++.h>
using namespace std;
int ans;
bool vis[10][10];
void dfs(int x,int y)
{
if (x==0 || y==0 || x==6 ||y==6)//到达边界的时候算一种方案
{
ans++;
return;
}
int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
for (int i=0;i<4;++i)
{
int tx=x+dir[i][0],ty=y+dir[i][1];
if (!vis[tx][ty])
{
vis[tx][ty]=true;
vis[6-tx][6-ty]=true;//由于对称的关系,所以只需要访问一半的对称图形就可以了
dfs(tx,ty);
vis[tx][ty]=false;//因为需要找到所有的情况,所以需要取消标记
vis[6-tx][6-ty]=false;
}
}
return;
}
int main()
{
vis[3][3]=true;//由于对称的关系,所以一定经过中心对称点,所以就从这个点开始搜索
dfs(3,3);
cout<<ans/4;//由于是中心对称的,所以得到的方案需要除以4
return 0;
}
题2https://www.lanqiao.cn/problems/106/learning/?page=1&first_category_id=1&problem_id=106
题目描述
考虑一种简单的正则表达式:
只由 x ( ) | 组成的正则表达式。
小明想求出这个正则表达式能接受的最长字符串的长度。
例如 ((xx|xxx)x|(x|xx))xx 能接受的最长字符串是: xxxxxx,长度是 6。
输入描述
一个由 x()| 组成的正则表达式。输入长度不超过 100,保证合法。
输出描述
这个正则表达式能接受的最长字符串的长度。
输入输出样例
示例
输入
((xx|xxx)x|(x|xx))xx
输出
6
运行限制
- 最大运行时间:1s
- 最大运行内存: 256M
这道题,主要用的是递归的方法,由于括号内的需要重新算,所以遇到括号就调用函数,计算括号内的值,遇到有括号就相当于出栈。
#include<bits/stdc++.h>
using namespace std;
string s;
int pos;
int dfs()
{
int len=s.size();
int tmp=0,ans=0;
while (pos<len)
{
if (s[pos]=='(')
{
//相当于入栈,在栈内计算括号的数值
pos++;
tmp+=dfs();
}
else if (s[pos]==')')
{
//相当于出栈
pos++;
break;
}
else if (s[pos]=='|')
{
//相当于已经找到了需要比较的一边
ans=max(ans,tmp);
pos++;
//需要找下一个比较的对象所以需要重置tmp
tmp=0;
}
else if (s[pos]=='x')
{
tmp++;
pos++;
}
}
//由于缺少最后的|所以需要在循坏退出之后再比较一次
ans=max(ans,tmp);
return ans;
}
int main()
{
cin>>s;
int ans=dfs();
cout<<ans;
return 0;
}
后面是每周作业
题3:高手去散步https://www.luogu.com.cn/problem/P1294
题目背景
高手最近谈恋爱了。不过是单相思。“即使是单相思,也是完整的爱情”,高手从未放弃对它的追求。今天,这个阳光明媚的早晨,太阳从西边缓缓升起。于是它找到高手,希望在晨读开始之前和高手一起在鳌头山上一起散步。高手当然不会放弃这次梦寐以求的机会,他已经准备好了一切。
题目描述
鳌头山上有 �n 个观景点,观景点两两之间有游步道共 �m 条。高手的那个它,不喜欢太刺激的过程,因此那些没有路的观景点高手是不会选择去的。另外,她也不喜欢去同一个观景点一次以上。而高手想让他们在一起的路程最长(观景时它不会理高手),已知高手的穿梭机可以让他们在任意一个观景点出发,也在任意一个观景点结束。
输入格式
第一行,两个用空格隔开的整数 �n 、 �.m. 之后 �m 行,为每条游步道的信息:两端观景点编号、长度。
输出格式
一个整数,表示他们最长相伴的路程。
输入输出样例
输入 #1复制
4 6 1 2 10 2 3 20 3 4 30 4 1 40 1 3 50 2 4 60
输出 #1复制
150
说明/提示
对于 100%100% 的数据:�≤20n≤20,�≤50m≤50,保证观景点两两之间不会有多条游步道连接。
思路:这道题可以用图的想法,一个地点可以通向不同的地方,并且是没有方向限制的,因为题目需要我们求最大的距离,所以我们可以建立一个二维数组,那么两个不同的桥就相当于它的坐标,所包含的值就是他们之间的距离,最后再搜索一下
#include <bits/stdc++.h>
using namespace std;
int vis[25],a[60][60];
int ans,maxn,m,n;
void dfs(int k,int sum)
{
ans=max(ans,sum);
for (int i=1;i<=n;++i)
{
if (!vis[i] && a[k][i]>0)//判断能不能走
{
vis[i]=1;//目的地打上标记
dfs(i,sum+a[k][i]);
vis[i]=0;
}
}
return ;
}
int main()
{
cin>>n>>m;
for (int i=0;i<m;++i)
{
int x,y,s;
cin>>x>>y>>s;
a[x][y]=s;
a[y][x]=s;
}
for (int i=1;i<=n;++i)
{
vis[i]=1;
dfs(i,0);
maxn=max(ans,maxn);
memset(vis,0,sizeof(vis));//清空标记数组
}
cout<<maxn;
return 0;
}
接水问题https://www.luogu.com.cn/problem/P1190
题目描述
学校里有一个水房,水房里一共装有 �m 个龙头可供同学们打开水,每个龙头每秒钟的供水量相等,均为 11。
现在有 �n 名同学准备接水,他们的初始接水顺序已经确定。将这些同学按接水顺序从 11 到 �n 编号,�i 号同学的接水量为 ��wi。接水开始时,11 到 �m 号同学各占一个水龙头,并同时打开水龙头接水。当其中某名同学 �j 完成其接水量要求 ��wj 后,下一名排队等候接水的同学 �k 马上接替 �j 同学的位置开始接水。这个换人的过程是瞬间完成的,且没有任何水的浪费。即 �j 同学第 �x 秒结束时完成接水,则 �k 同学第 �+1x+1 秒立刻开始接水。若当前接水人数 �′n′ 不足 �m,则只有 �′n′ 个龙头供水,其它 �−�′m−n′ 个龙头关闭。
现在给出 �n 名同学的接水量,按照上述接水规则,问所有同学都接完水需要多少秒。
输入格式
第一行两个整数 �n 和 �m,用一个空格隔开,分别表示接水人数和龙头个数。
第二行 �n 个整数 �1,�2,…,��w1,w2,…,wn,每两个整数之间用一个空格隔开,��wi 表示 �i 号同学的接水量。
输出格式
一个整数,表示接水所需的总时间。
输入输出样例
输入 #1复制
5 3 4 4 1 2 1
输出 #1复制
4
输入 #2复制
8 4 23 71 87 32 70 93 80 76
输出 #2复制
163
说明/提示
【输入输出样例 #1 说明】
第 11 秒,33 人接水。第 11 秒结束时,1,2,31,2,3 号同学每人的已接水量为 1,31,3 号同学接完水,44 号同学接替 33 号同学开始接水。
第 22 秒,33 人接水。第 22 秒结束时,1,21,2 号同学每人的已接水量为 2,42,4 号同学的已接水量为 11。
第 33 秒,33 人接水。第 33 秒结束时,1,21,2 号同学每人的已接水量为 3,43,4 号同学的已接水量为 22。44 号同学接完水,55 号同学接替 44 号同学开始接水。
第 44 秒,33 人接水。第 44 秒结束时,1,21,2 号同学每人的已接水量为 4,54,5 号同学的已接水量为 11。1,2,51,2,5 号同学接完水,即所有人完成接水的总接水时间为 44 秒。
【数据范围】
1≤�≤1041≤n≤104,1≤�≤1001≤m≤100,�≤�m≤n;
1≤��≤1001≤wi≤100。
思路:由于一开始没有人接水,所以前面几个人可以立刻去接水,那么就会产生时间,接下来就需要知道剩下的孩子中,去接水所需要的最大时间,由于这题的数据量很小,所以我可以每安排一个孩子就排一次序,那么第一个一定是最先走的,所以一定是它的时间加上下一个接水的,等到所有孩子完成接水后,当前数组的最后一个时间就是所需要的最长时间
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n,m;
cin>>n>>m;
int a[10005];
int ss[10005];
for (int i=0;i<n;++i)cin>>a[i];
//初始化水龙头,把最前面的m个人去接水
for (int i=0;i<m;++i)
{
ss[i]=a[i];
}
int max=-1;
for (int i=m;i<n;++i)
{
sort(ss,ss+m);
ss[0]+=a[i];
}
sort(ss,ss+m);
cout<<ss[m-1];
return 0;
}
题4:日志分析https://www.luogu.com.cn/problem/P1165
这道题主要就是再建立一个最大值栈,在只有第一个元素的时候,那个元素入栈,在接下来入栈的元素都要与这个最大值元素比较,如果更大就入栈,若遇到出栈指令,那么判断出栈元素和最大值栈的元素是否相等,如果相等那就都出栈
#include <bits/stdc++.h>
using namespace std;
int main()
{
stack<long long>st;
stack<long long >maxst;//建立最大值栈找最大值
int n;
cin>>n;
for (int i=0;i<n;++i)
{
int x;
cin>>x;
if (x==0)
{
long long y;
cin>>y;
if (st.empty())
{
maxst.push(y);//没有元素的情况下,剩余的元素就是最大值
}
else if (maxst.top()<y)
{
maxst.push(y);
}
st.push(y);
}
else if (x==1)
{
if (st.top()==maxst.top())//如果出栈的元素正好等于最大值的话最大值元素就出栈
{
maxst.pop();
}
st.pop();
}
else if (x==2)
{
if (st.empty())//没有元素的情况
{
cout<<0<<endl;
continue;
}
cout<<maxst.top()<<endl;
}
}
return 0;
}
题5:表达式括号匹配https://www.luogu.com.cn/problem/P1739
遇到左括号入栈,遇到右括号出栈,有一点特例就是,会出现开头就是右括号的情况,那么就直接输出失败
#include <bits/stdc++.h>
using namespace std;
int main()
{
string s;
cin>>s;
int top=-1;
for (int i=0;s[i]!='@';++i)
{
if (s[i]=='(')top++;
else if (s[i]==')')top--;
if (top<-1)
{
cout<<"NO";
return 0;
}
}
if (top==-1)cout<<"YES";
else cout<<"NO";
return 0;
}
题6:约瑟夫问题https://www.luogu.com.cn/problem/P1996
循环队列,假定数到m的人要走,那么用while循坏直到队列中没有元素停止,那么如果当前报的数不是m的话,这个人出去在进去,进到队尾,反之则出队
#include <bits/stdc++.h>
using namespace std;
int main()
{
int m,n;
cin>>m>>n;
queue<int>que;
for (int i=1;i<=m;++i)
{
que.push(i);
}
int cnt=1;
while (!que.empty())
{
if (cnt!=n)
{
que.push(que.front());
que.pop();
cnt++;
}
else
{
cout<<que.front()<<" ";
que.pop();
cnt=1;
}
}
}
题6:路障https://www.luogu.com.cn/problem/P3395
BFS和之前掉陨石很像,这个会在不同时间内放置路障,那么我们就建立一个二维数组,并且存放的是路障放置的时间(在这里,如果人走到那个位置恰好有路障,但是时间与人到那个地方时间相同的话,人是不会有事的),初始化路障好了之后,BFS一边就结束了,但这里要注意的是路障的个数是2n-2
#include <bits/stdc++.h>
using namespace std;
int a[1005][1005];
int vis[1005][1005];
struct Queue{
int x;
int y;
int time;
}QQQ[1000000];
int main()
{
int t;
int flag=0;
cin>>t;
while (t--)
{
int n;
cin>>n;
memset(QQQ,0,sizeof(QQQ));
memset(a,0,sizeof(a));
memset(vis,0,sizeof(vis));
if (n==1)
{
cout<<"Yes"<<endl;
continue;
}
for (int i=1;i<=2*n-2;++i)//注意审题2n-2个路障,一直当做是n个导致一直报错
{
int x,y;
cin>>x>>y;
a[x][y]=i;//每个路障位置都带着时间
}
int head=0,tail=0;
QQQ[tail].x=1,QQQ[tail].y=1,QQQ[tail].time=0;
vis[1][1]=1;
tail++;
int dic[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
while (head!=tail)
{
for (int i=0;i<4;++i)
{
int tx=QQQ[head].x+dic[i][0],ty=QQQ[head].y+dic[i][1],tt=QQQ[head].time+1;
if (tx==n && ty==n && (a[tx][ty]==0||tt<=a[tx][ty]) )
{
flag=1;
break;
}
if (tx>=1 && ty>=1 &&tx<=n && ty<=n && (a[tx][ty]==0||tt<=a[tx][ty]) && !vis[tx][ty])
{
QQQ[tail].x=tx,QQQ[tail].y=ty,QQQ[tail].time=tt;
vis[tx][ty]=1;
tail++;
}
}
if (flag)break;
head++;
}
if (flag)
{
cout<<"Yes"<<endl;
flag=0;
}
else cout<<"No"<<endl;
}
}
题7:【模板】单调栈https://www.luogu.com.cn/problem/P5788
单调栈就是单调的栈,它需要知道第一个大于当前元素的元素的位置,这就符合单调递增栈的想法,具体的想法主要是从最后一个元素开始找,如果遇到比他大的元素,那么当前栈顶元素就出栈,这就相当于当你需要一个身高递增的队伍的时候,那么卡在中间的比前人矮的人就变成无效的人了,就可以删除
如果需要找后面第一个比自己小的元素的索引就是,单调减同理
#include <bits/stdc++.h>
using namespace std;
int a[3000005];
int res[3000005];
int main()
{
int m;
stack<int>st;
scanf("%d",&m);
for (int i=1;i<=m;++i)cin>>a[i];
//单调栈,从后面开始排,用案例来说,会先判断栈是否为空和是否栈内的元素要小于要进去的元素
//如果小了,那么栈内的元素就失去了价值,需要出栈
//在压入新元素之前,需要判断栈是否为空,如果是的,代表没有元素大于它或者他是最后一个元素,因此输出0
for (int i=m;i>0;--i)
{
while (!st.empty() && a[st.top()]<=a[i])st.pop();
res[i]=st.empty()?0:st.top();
st.push(i);
}
for (int i=1;i<=m;++i)cout<<res[i]<<" ";
}
题8:滑动窗口 /【模板】单调队列滑动窗口 /【模板】单调队列 - 洛谷
可以用双端队列做,想法和单调栈类似
这是找最小值的核心代码
是否需要输出是看i-k的情况,只有当i大于等于k的时候,才会出现窗口才能输出
由于需要找的是最小值,所以当当前的元素大于即将进去的元素时,需要出队,这就相当于如果有更小的元素,那么队列里面的比他小的元素都要出队,然后这个小的进去,如果这个元素比他大,那就直接进去,因为当前的最小的元素很快就会被滑动窗口淘汰
for (int i=1;i<=n;++i)
{
while (!q.empty() && q.back()>a[i])q.pop_back();
q.push_back(a[i]);
if (i-k>=1 && a[i-k]==q.front())q.pop_front();
if (i-k>=0) printf("%d ",q.front());
}
最大值同理
#include <bits/stdc++.h>
using namespace std;
int a[1000009];
int main()
{
int n,k;
scanf("%d %d",&n,&k);
deque<int>q;
deque<int>p;
for (int i=1;i<=n;++i)cin>>a[i];
for (int i=1;i<=n;++i)
{
while (!q.empty() && q.back()>a[i])q.pop_back();
q.push_back(a[i]);
if (i-k>=1 && a[i-k]==q.front())q.pop_front();
if (i-k>=0) printf("%d ",q.front());
}
cout<<endl;
q.clear();
for (int i=1;i<=n;++i)
{
while (!q.empty() && q.back()<a[i])q.pop_back();
q.push_back(a[i]);
if (i-k>=1 && a[i-k]==q.front())q.pop_front();
if (i-k>=0) printf("%d ",q.front());
}
}
题9:小小的埴轮兵团https://www.luogu.com.cn/problem/P7505
同理运用双端队列完成
#include <bits/stdc++.h>
using namespace std;
deque<long long>q;
long long tot=0;
int main()
{
int n,m,k;
cin>>n>>m>>k;
for (long long i=0;i<n;++i)
{
long long a;
cin>>a;
q.push_back(a);
}
sort(q.begin(),q.end());
for (long long i=0;i<m;++i)
{
int op;
cin>>op;
if (op==3)
{
if (q.empty())cout<<0<<endl;
else cout<<q.size()<<endl;
}
else if (op==1)
{
long long x;
cin>>x;
tot+=x;
while (!q.empty())
{
long long v=q.back();
if (v+tot>k)q.pop_back();
else break;
}
}
else if (op==2)
{
long long x;
cin>>x;
tot-=x;
while (!q.empty())
{
long long v=q.front();
if (v+tot<-k)q.pop_front();
else break;
}
}
}
}
题10:表达式求值https://www.luogu.com.cn/problem/P1981
运用栈的思想,如果是先把第一个数放到栈里面,然后判断字符和数字,如果字符是乘号的话优先级比较高,那么就直接从栈里面取数字出来和这个读入的数字计算,结果放到栈里面,如果是加号的话,那么就把数字存到栈里面,最后再统一加起来
#include <bits/stdc++.h>
using namespace std;
#define MOD 10000
int main()
{
stack<long long >st;
long long a,b;
char c;
cin>>a;
a%=MOD;
st.push(a);
while (cin>>c>>b)
{
if (c=='*')
{
long long p=(st.top()*b)%MOD;
st.pop();
st.push(p);
}
else if (c=='+')
{
st.push(b);
}
else
{
break;
}
}
long long sum=0;
while (!st.empty())
{
sum=(sum+st.top())%MOD;
st.pop();
}
cout<<sum%MOD;
}
题11:GITARAhttps://www.luogu.com.cn/problem/P6704
建立七个栈,读入要弹奏的,如果当前的数小于栈顶元素的数字,那么出栈,出栈也要记录个数,结束出栈后,栈内的元素都是小于等于当前元素的,所以要判断,如果栈顶元素与弹奏的元素相同的话,那么不需要再谈了,如果是小于的话,那么还需要再谈一次。
#include <bits/stdc++.h>
using namespace std;
#define MOD 10000
int main()
{
int n,p;
cin>>n>>p;
stack<int>st[7];
int cnt=0;
for (int i=0;i<n;++i)
{
int a,b;
cin>>a>>b;
while (!st[a].empty()>0&&st[a].top()>b)
{
st[a].pop();
cnt++;
}
if (!st[a].empty())
{
if (st[a].top()==b)
{
continue;
}
else
{
st[a].push(b);
cnt++;
}
}
else
{
st[a].push(b);
++cnt;
}
}
cout<<cnt;
}
题12:验证栈序列https://www.luogu.com.cn/problem/P4387
把序列1和序列2的元素都放到两个数组里面,对于序列2需要特别的设置一个了计数器,然后就是开始遍历,把序列1中的元素压入栈中,并且同时判断,当前元素是否和序列2中相同,如果相同的话,就出栈,直到栈内没有元素为止,在继续从序列1中压入元素。元素压完后,再判断栈内是否有元素,如果没有就可以完成。
#include <bits/stdc++.h>
using namespace std;
#define MOD 10000
int main()
{
int q;
cin>>q;
stack<int>st1;
int a1[100005];
int a2[100005];
while (q--)
{
int n;
cin>>n;
for (int i=1;i<=n;++i)
{
cin>>a1[i];
}
for (int i=1;i<=n;++i)
{
cin>>a2[i];
}
int cnt=1;
for (int i=1;i<=n;++i)
{
st1.push(a1[i]);
while(st1.top()==a2[cnt])
{
st1.pop();
cnt++;
if (st1.empty())break;
}
}
if (st1.empty())
{
cout<<"Yes"<<endl;
}
else
{
cout<<"No"<<endl;
}
while (!st1.empty())st1.pop();
}
return 0;
}