LL(1)算法
编译原理的语法分析中一个入门的算法就是LL(1)算法了,这里做一个总结。首先比较重要的是FIRST集和FOLLOW集合的生成。
先上个例子吧:

首先说一下FIRST集的生成,这个就要看产生式右部对应的首字母的“终结符”的个数的表现了,例如:A-> +TA|-TA|k 所以 A的FIRST集为+-k ; 同理B->*FB|/FB|k,所以B的FIRST集是*/k; D的FIRST集是xyz; 接着我们再分析F,对F分析可以得FIRST集是 ( 和 D的FIRST的并集,即(xyz,同理可以得到T和E的FIRST都是(xyz。
我们接着分析FOLLOW集。首先要说一点,可以观察按照FOLLOW集按照“非终结符顺序”从上到下是递增的(这是什么规律,但是几乎所有的LL1(1)分析表都有这个现象),从上至下依次分析,对于E,观察所有的产生式的右部:有F-> (E)|D 这个式,则我们可以得知E的FOLLOW集为#). 接下来我们分析A的FOLLOW集,观察所有的产生式的右部,发现没有跟着A的终结符,哎,没办法,照抄上面的吧:即A得FOLLOW集和E的一样,都是#)。 然后我们分析T,发现T后面总是跟着A,则A的FIRST集应该包含于T的FOLLOW集中(当然,有空k的要去掉k), 再加上上面“遗传”下来的,所以T的FOLLOW集是+-#). 同理分析下去就会得到全部的非终结符的FOLLOW集。
接着是如何运用FIRST和FOLLOW集生成LL(1)分析表。首先对所有的非终结符的FIRST进行填充,并留下这些FIRST的空k符号。这一遍刷完之后,再特别处理空k,对有空k的非终结符,将它的FOLLOW集中的所有的元素,都写成空k的式输入到LL(1)分析表中去。这样就大功告成了。
不妨再贴一个例子:

题目1:
输入开始符号,非终结符,终结符,产生式,LL(1)分析表
输出LL(1)分析表
G[E]:E →E+T | E-T | T
T →T*F | T/F | F
F →(E) | D
D →x | y | z
消除左递归G1[E]:
E →TA
A →+TA | -TA | e
T →FB
B →*FB | /FB | e
F →(E) | D
D →x | y | z
输入开始符号;
非终结符个数,非终结符,空格符分隔;
终结符个数,终结符,空格符分隔;
产生式的个数,各产生式的序号,产生式的左边和右边符号,空格符分隔;
LL(1)分析表中的产生式个数,序号,行符号,列符号,产生式编号,空格符分隔;
第一行:空,安终结符循序输出终结符,结束符‘#’,每个符号占5格;
其余行:非终结符符号,各对应终结符的产生式的右边,每个符号占5格;
E
6 E A T B F D
9 + - * / ( ) x y z
13
1 E TA
2 A +TA
3 A -TA
4 A k
5 T FB
6 B *FB
7 B /FB
8 B k
9 F (E)
10 F D
11 D x
12 D y
13 D z
25
1 E ( 1
2 E x 1
3 E y 1
4 E z 1
5 A + 2
6 A - 3
7 A ) 4
8 A # 4
9 T ( 5
10 T x 5
11 T y 5
12 T z 5
13 B + 8
14 B - 8
15 B * 6
16 B / 7
17 B ) 8
18 B # 8
19 F ( 9
20 F x 10
21 F y 10
22 F z 10
23 D x 11
24 D y 12
25 D z 13
+ - * / ( ) x y z #
E TA TA TA TA
A +TA -TA k k
T FB FB FB FB
B k k *FB /FB k k
F (E) D D D
D x y z AC代码:
#include <iostream>
using namespace std; string S=""; //开始符号
struct { int number;string sign[]; string res_LL[][]; } not_endsign={}; //非终结符
struct { int number;string sign[]; } end_sign={}; //终结符
struct { int number;int order[]; string left[],right[]; } production={}; //产生式
struct { int number;int order[]; string rank[],col[];int production_num[]; } LL={}; //LL(1)分析表 void input();
void print(string a); int main(){
input();
end_sign.sign[end_sign.number] = "#";
end_sign.number++; //刷整个的分析表,将分析表中的数据填入到not_endsign中去
for(int i=;i<LL.number;i++){
//得到LL一条数据的“行”对应的“非终结符”
int j;
for(j=;j<not_endsign.number&& not_endsign.sign[j]!=LL.rank[i];j++ ); //得到LL一条数据的“列”对应的“终结符”
int z;
for(z=;z<end_sign.number&&end_sign.sign[z]!=LL.col[i];z++ ); //得到LL一条数据的要赋予的值
not_endsign.res_LL[j][z] = production.right[LL.production_num[i]- ];
} //单独处理“#” cout<<" ";
for(int i=;i<end_sign.number;i++){
cout<<" "<<end_sign.sign[i];
}
cout<<endl; for(int i=;i<not_endsign.number;i++){
print(not_endsign.sign[i]);
cout<<not_endsign.sign[i];
for(int j=;j<end_sign.number ;j++){
print(not_endsign.res_LL[i][j] );
cout<<not_endsign.res_LL[i][j];
}
cout<<endl;
} return ;
} void print(string a){
for(int i=;i<-a.length();i++){
cout<<" ";
}
return ;
}
void input(){
cin>>S;
cin>>not_endsign.number;
for(int i=;i<not_endsign.number;i++){
cin>>not_endsign.sign[i];
} cin>>end_sign.number;
for(int i=;i<end_sign.number;i++){
cin>>end_sign.sign[i];
} cin>>production.number;
for(int i=;i<production.number;i++){
cin>>production.order[i]>>production.left[i]>>production.right[i];
} cin>>LL.number;
for(int i=;i<LL.number;i++){
cin>>LL.order[i]>>LL.rank[i]>>LL.col[i]>>LL.production_num[i];
}
return ;
}
题目2:
输入开始符号,非终结符,终结符,产生式,LL(1)分析表
输出LL(1)分析表
输入开始符号;
非终结符个数,非终结符,空格符分隔;
终结符个数,终结符,空格符分隔;
产生式的个数,各产生式的序号,产生式的左边和右边符号,空格符分隔;
LL(1)分析表中的产生式个数,序号,行符号,列符号,产生式编号,空格符分隔;
输入一个算术式符号串,用#结束
输出推导过程,每一步一行,中间“ & ”前是已经识别的子串,后是栈中信息。
E
6 E A T B F D
9 + - * / ( ) x y z
13
1 E TA
2 A +TA
3 A -TA
4 A k
5 T FB
6 B *FB
7 B /FB
8 B k
9 F (E)
10 F D
11 D x
12 D y
13 D z
25
1 E ( 1
2 E x 1
3 E y 1
4 E z 1
5 A + 2
6 A - 3
7 A ) 4
8 A # 4
9 T ( 5
10 T x 5
11 T y 5
12 T z 5
13 B + 8
14 B - 8
15 B * 6
16 B / 7
17 B ) 8
18 B # 8
19 F ( 9
20 F x 10
21 F y 10
22 F z 10
23 D x 11
24 D y 12
25 D z 13
(x+(y-x*z)*(y+x*z))+x/z#
# & E#
# & TA#
# & FBA#
# & (E)BA#
#( & E)BA#
#( & TA)BA#
#( & FBA)BA#
#( & DBA)BA#
#( & xBA)BA#
#(x & BA)BA#
#(x & A)BA#
#(x & +TA)BA#
#(x+ & TA)BA#
#(x+ & FBA)BA#
#(x+ & (E)BA)BA#
#(x+( & E)BA)BA#
#(x+( & TA)BA)BA#
#(x+( & FBA)BA)BA#
#(x+( & DBA)BA)BA#
#(x+( & yBA)BA)BA#
#(x+(y & BA)BA)BA#
#(x+(y & A)BA)BA#
#(x+(y & -TA)BA)BA#
#(x+(y- & TA)BA)BA#
#(x+(y- & FBA)BA)BA#
#(x+(y- & DBA)BA)BA#
#(x+(y- & xBA)BA)BA#
#(x+(y-x & BA)BA)BA#
#(x+(y-x & *FBA)BA)BA#
#(x+(y-x* & FBA)BA)BA#
#(x+(y-x* & DBA)BA)BA#
#(x+(y-x* & zBA)BA)BA#
#(x+(y-x*z & BA)BA)BA#
#(x+(y-x*z & A)BA)BA#
#(x+(y-x*z & )BA)BA#
#(x+(y-x*z) & BA)BA#
#(x+(y-x*z) & *FBA)BA#
#(x+(y-x*z)* & FBA)BA#
#(x+(y-x*z)* & (E)BA)BA#
#(x+(y-x*z)*( & E)BA)BA#
#(x+(y-x*z)*( & TA)BA)BA#
#(x+(y-x*z)*( & FBA)BA)BA#
#(x+(y-x*z)*( & DBA)BA)BA#
#(x+(y-x*z)*( & yBA)BA)BA#
#(x+(y-x*z)*(y & BA)BA)BA#
#(x+(y-x*z)*(y & A)BA)BA#
#(x+(y-x*z)*(y & +TA)BA)BA#
#(x+(y-x*z)*(y+ & TA)BA)BA#
#(x+(y-x*z)*(y+ & FBA)BA)BA#
#(x+(y-x*z)*(y+ & DBA)BA)BA#
#(x+(y-x*z)*(y+ & xBA)BA)BA#
#(x+(y-x*z)*(y+x & BA)BA)BA#
#(x+(y-x*z)*(y+x & *FBA)BA)BA#
#(x+(y-x*z)*(y+x* & FBA)BA)BA#
#(x+(y-x*z)*(y+x* & DBA)BA)BA#
#(x+(y-x*z)*(y+x* & zBA)BA)BA#
#(x+(y-x*z)*(y+x*z & BA)BA)BA#
#(x+(y-x*z)*(y+x*z & A)BA)BA#
#(x+(y-x*z)*(y+x*z & )BA)BA#
#(x+(y-x*z)*(y+x*z) & BA)BA#
#(x+(y-x*z)*(y+x*z) & A)BA#
#(x+(y-x*z)*(y+x*z) & )BA#
#(x+(y-x*z)*(y+x*z)) & BA#
#(x+(y-x*z)*(y+x*z)) & A#
#(x+(y-x*z)*(y+x*z)) & +TA#
#(x+(y-x*z)*(y+x*z))+ & TA#
#(x+(y-x*z)*(y+x*z))+ & FBA#
#(x+(y-x*z)*(y+x*z))+ & DBA#
#(x+(y-x*z)*(y+x*z))+ & xBA#
#(x+(y-x*z)*(y+x*z))+x & BA#
#(x+(y-x*z)*(y+x*z))+x & /FBA#
#(x+(y-x*z)*(y+x*z))+x/ & FBA#
#(x+(y-x*z)*(y+x*z))+x/ & DBA#
#(x+(y-x*z)*(y+x*z))+x/ & zBA#
#(x+(y-x*z)*(y+x*z))+x/z & BA#
#(x+(y-x*z)*(y+x*z))+x/z & A#
#(x+(y-x*z)*(y+x*z))+x/z & # AC代码:
#include <iostream>
#include <stack>
using namespace std; string S=""; //开始符号
struct { int number;string sign[]; string res_LL[][]; } not_endsign={}; //非终结符
struct { int number;string sign[]; } end_sign={}; //终结符
struct { int number;int order[]; string left[],right[]; } production={}; //产生式
struct { int number;int order[]; string rank[],col[];int production_num[]; } LL={}; //LL(1)分析表
string test; void input();
void print(string left,stack<string > right); int main(){
input(); //定义输出结果
string left;
stack<string > right; right.push(S) ;
print(left,right); while(!right.empty()){
string top = right.top();
string firstletter = test.substr(,);
if(top==firstletter){
left += top;
test = test.substr(,test.length()- );
right.pop();
print(left,right); continue;
}
else {
//替换掉 top
for(int i=;i<LL.number;i++){
if(LL.rank[i]==top &&LL.col[i]==firstletter ){
right.pop();
string temp = production.right[LL.production_num[i]- ];
if(temp=="k") continue;
while(temp.length()!=){
string temp0 = temp.substr( temp.length()-,);
right.push(temp0);
temp = temp.substr(,temp.length()-);
} }
} } print(left,right);
} return ;
} void print(string left,stack<string > right){
cout<<"#"<<left<<" & ";
string temp="";
while(!right.empty()){
cout<<right.top();
temp+=right.top();
right.pop();
}
cout<<"#"<<endl; while(temp.length()!=){
string temp0 = temp.substr( temp.length()-,);
right.push(temp0);
temp = temp.substr(,temp.length()-);
} return ;
} void input(){
cin>>S;
cin>>not_endsign.number;
for(int i=;i<not_endsign.number;i++){
cin>>not_endsign.sign[i];
} cin>>end_sign.number;
for(int i=;i<end_sign.number;i++){
cin>>end_sign.sign[i];
} cin>>production.number;
for(int i=;i<production.number;i++){
cin>>production.order[i]>>production.left[i]>>production.right[i];
} cin>>LL.number;
for(int i=;i<LL.number;i++){
cin>>LL.order[i]>>LL.rank[i]>>LL.col[i]>>LL.production_num[i];
} cin>>test;
return ;
}
LL(1)算法的更多相关文章
- B树——算法导论(25)
B树 1. 简介 在之前我们学习了红黑树,今天再学习一种树--B树.它与红黑树有许多类似的地方,比如都是平衡搜索树,但它们在功能和结构上却有较大的差别. 从功能上看,B树是为磁盘或其他存储设备设计的, ...
- 分布式系列文章——Paxos算法原理与推导
Paxos算法在分布式领域具有非常重要的地位.但是Paxos算法有两个比较明显的缺点:1.难以理解 2.工程实现更难. 网上有很多讲解Paxos算法的文章,但是质量参差不齐.看了很多关于Paxos的资 ...
- 【Machine Learning】KNN算法虹膜图片识别
K-近邻算法虹膜图片识别实战 作者:白宁超 2017年1月3日18:26:33 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本系列文章是作者结 ...
- 红黑树——算法导论(15)
1. 什么是红黑树 (1) 简介 上一篇我们介绍了基本动态集合操作时间复杂度均为O(h)的二叉搜索树.但遗憾的是,只有当二叉搜索树高度较低时,这些集合操作才会较快:即当树的高度较高(甚至一种极 ...
- 散列表(hash table)——算法导论(13)
1. 引言 许多应用都需要动态集合结构,它至少需要支持Insert,search和delete字典操作.散列表(hash table)是实现字典操作的一种有效的数据结构. 2. 直接寻址表 在介绍散列 ...
- 虚拟dom与diff算法 分析
好文集合: 深入浅出React(四):虚拟DOM Diff算法解析 全面理解虚拟DOM,实现虚拟DOM
- 简单有效的kmp算法
以前看过kmp算法,当时接触后总感觉好深奥啊,抱着数据结构的数啃了一中午,最终才大致看懂,后来提起kmp也只剩下“奥,它是做模式匹配的”这点干货.最近有空,翻出来算法导论看看,原来就是这么简单(先不说 ...
- 神经网络、logistic回归等分类算法简单实现
最近在github上看到一个很有趣的项目,通过文本训练可以让计算机写出特定风格的文章,有人就专门写了一个小项目生成汪峰风格的歌词.看完后有一些自己的小想法,也想做一个玩儿一玩儿.用到的原理是深度学习里 ...
- 46张PPT讲述JVM体系结构、GC算法和调优
本PPT从JVM体系结构概述.GC算法.Hotspot内存管理.Hotspot垃圾回收器.调优和监控工具六大方面进行讲述.(内嵌iframe,建议使用电脑浏览) 好东西当然要分享,PPT已上传可供下载 ...
- 【C#代码实战】群蚁算法理论与实践全攻略——旅行商等路径优化问题的新方法
若干年前读研的时候,学院有一个教授,专门做群蚁算法的,很厉害,偶尔了解了一点点.感觉也是生物智能的一个体现,和遗传算法.神经网络有异曲同工之妙.只不过当时没有实际需求学习,所以没去研究.最近有一个这样 ...
随机推荐
- php实现设计模式之 迭代器模式
<?php /*迭代器模式: 提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示.(行为模式) * 1.迭代器角色(Iterator):迭代器角色负责定义访问和遍历元素的 ...
- 从零开始学 Java - Spring 支持 CORS 请求踩的坑
谁没掉进过几个大坑 记得好久之前,总能时不时在某个地方看到一些标语,往往都是上面一个伟人的头像,然后不管是不是他说的话,下面总是有看起来很政治正确且没卵用的屁话,我活到目前为止,最令我笑的肚子痛得是下 ...
- Linux(四)__javaee开发环境的搭建
一.VMware tools 通过VMware tools来实现主机和VM共享文件,详细介绍 记得重启就能实现本机和虚拟机之间复制粘贴文件. 二.搭建java环境: 一般linux都会预装openjd ...
- 设计模式学习之路——Decorator装饰模式(结构模式)
子类复子类,子类何其多 假如我们需要为游戏中开发一种坦克,除了各种不同型号的坦克外,我们还希望在不同场合中为其增加以下一种或多种功能:比如红外线夜视功能,比如水陆两栖功能,比如卫星定位功能等等. 动机 ...
- The habits of highly successful people
1.Morning Routine (早上列行公事) Probably the most common habit ultra-successful people have is they can t ...
- 2016年4月最佳的20款 jQuery 插件推荐
这个列表包括20个我们觉得是最有用的免费的 jQuery 插件,它们都是最具创新性和最省时省力的解决方案,很多都是现代化的设计和开发中碰到的问题的处理方案.如果你熟悉下面列出的任何插件,请与我们的读者 ...
- 如何使用CSS绘制一个响应式的矩形
背景: 最近因为需要用到绘制类似九宫格的需求,所以研究了一下响应式矩形的实现方案. 有如下几种方案: 使用js来设置元素的高度 使用vw单位 div {width: 50vw; height: 50 ...
- JAVA简单工厂模式(从现实生活角度理解代码原理)
简单工厂模式(Simple Factory),说他简单是因为我们可以将此模式比作一个简单的民间作坊,他们只有固定的生产线生产固定的产品.也可以称他为静态工厂设计模式,类似于之前提到过静态代理设计模式, ...
- Attempt to insert non-property list object 报错原因
NSUserDefault 支持的存储类型有:NSString. NSNumber.NSDate. NSArray.NSDictionary.BOOL.NSInteger.NSFloat等系统定义的数 ...
- Android自定义控件6--轮播图广告的实现
本文接着实现轮播图广告的监听滚动 本文地址:http://www.cnblogs.com/wuyudong/p/5920757.html,转载请注明源地址. 首先添加布局文件,实现小白点 shape_ ...