数据结构 第5章广义表
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
if(i<n){ hstr=str.substr(1,i-2); str="("+str.substr(i,n-i); } else{ hstr=str.substr(1,n-2); str=""; } return hstr; }
何谓"递归函数 ? 何谓 递归函数"? 递归函数
一个含直接或间接调用本函数语句的函数被 称之为递归函数,它必须满足以下两个条件: 称之为递归函数,它必须满足以下两个条件: 在每一次调用自己时,必须是(在某种意义 ①在每一次调用自己时,必须是 在某种意义 更接近于解; 上)更接近于解; 更接近于解 函数中必须有一个终止处理或计算的准则。 ②函数中必须有一个终止处理或计算的准则。
的存储结构。 例:广义表 L=(a,(x,y),((z))) 的存储结构。
可由对L进行表头和表尾的分析得到 若列表不空 可由对 进行表头和表尾的分析得到--若列表不空, 进行表头和表尾的分析得到 若列表不空, 则可分解成表头和表尾(采用头尾链表存储结构)。 则可分解成表头和表尾(采用头尾链表存储结构)。 分析: 分析: ①L为一非空列表 为一非空列表
// 将非空串 分割成两部分: 将非空串str分割成两部分 分割成两部分: // hsub为最外层第一个 之前的子串,str为之后的子串 为最外层第一个','之前的子串 为最外层第一个 之前的子串, 为之后的子串 string sever(string& str) { int n,k,i; // k记录尚未配对的左括号个数 记录尚未配对的左括号个数 string ch,hstr; n=str.length(); for(i=0,k=-1; i<n&&(ch!=","||k!=0); ++i) { // 搜索最外层的第一个逗号 ch=str.substr(i,1); //从第 个位置起,取1个字符 从第i个位置起 从第 个位置起, 个字符 if(ch=="(") ++k; else if(ch==")") --k; }
⑷ 建立广义表 以广义表字符串S建立广义表 建立广义表。 以广义表字符串 建立广义表。 从前面的讨论可知, 从前面的讨论可知,非空广义表可分解成表头和表 尾两个部分。建表时,可分别建立( 尾两个部分。建表时,可分别建立(分解为两个子 问题): 问题): 表头有两种情况,为单原子或为列表, 两种情况 表头有两种情况,为单原子或为列表,当为列表时 与原问题相同; 与原问题相同; 表尾必为列表,与原问题相同。 必为列表 表尾必为列表,与原问题相同。 函数的功能 设string sever(string& S )函数的功能为: 函数的功能为 从广义表字符串S中提取表头串返回 并将S置成表 中提取表头串返回, 从广义表字符串 中提取表头串返回,并将 置成表 尾串。 尾串。
并且为了在存储结构中便于分辩原子和子表, 并且为了在存储结构中便于分辩原子和子表,令表 示广义表的链表中的结点为"异构 结点,如图所示, 异构"结点 示广义表的链表中的结点为 异构 结点,如图所示, 结点中设有一个"标志域 标志域tag", 并约定 tag=0 表示 结点中设有一个 标志域 , 原子结点, 表示表结点。 原子结点,tag=1 表示表结点。原子结点中的 data 域存储原子, 域存储原子,表结点中指针域的两个值分别指向表 头和表尾。 头和表尾。
5.2.2 广义表的头尾链表存储表示
glist.h #include <iostream> #include <string> using namespace std; enum ElemTag {ATOM,LIST}; typedef char AtomType;
struct GLNode{ ElemTag tag; union { //匿名共用体 匿名共用体 AtomType atom; struct { GLNode *hp,*tp; } ptr; }; }; typedef GLNode* GList;
为广义表的第i个元素 广义表LS可表示为 个元素, 可表示为: 设ai为广义表的第 个元素,广义表 可表示为: LS=(a1,a2,…, an) ( , 其中: 其中: 广义表的名字; ①LS——广义表的名字; 广义表的名字 广义表的长度; ②n——广义表的长度; 广义表的长度 或者是单元素 (小写) ③ ai--表中的元素 ; 或者是表元素 (大写) 空表; ④n=0 空表; 当广义表非空时, 表头( ),其余元素组 ⑤当广义表非空时,称a1为表头(head),其余元素组 ), 成的表( 表尾( ); 成的表(a2,a3, …, an)为表尾(tail); , 广义表的抽象数据类型: 广义表的抽象数据类型:P107
⑶ 求广义表的深度 int getDepth(GList L) //采用头尾链表存储结构,求广义表 的深度 采用头尾链表存储结构, 采用头尾链表存储结构 求广义表L的深度 { int max=0,dep; GList p; if(!L) return 1; //空表深度为 空表深度为1 空表深度为 if(L->tag==ATOM) return 0; //原子深度为 原子深度为0 原子深度为 for(p=L; p; p=p->ptr.tp){ dep=getDepth(p->ptr.hp); //求以 求以p->a.ptr.hp为头指针的子表深度 求以 为头指针的子表深度 if(dep>max) max=dep; } return max+1; // 非空表的深度是各元素的深度的最大值加 非空表的深度是各元素的深度的最大值加1 }
5.1.2 举例
分别用圆圈和方框表示表(表元素) 分别用圆圈和方框表示表(表元素)和单元素 ①A=() () 空表,其长度为零。 空表,其长度为零。
②B=(e) ( ) B中只有一个单元素,长度为 ,表头为 ,表尾为 中只有一个单元素, 中只有一个单元素 长度为1,表头为'e', 空。
,(b, , )) ③C=(a,( ,c,d)) ( ,( 长度为2,表头为‘ ,表尾为子表(( ((b, , 长度为 ,表头为‘a’,表尾为子表(( ,c, d))。 ))。
的表头为原子a ②L的表头为原子 的表头为原子
的表尾为列表((x,y),((z))) ③L的表尾为列表 的表尾为列表
的表头为列表(x,y) ④((x,y),((z)))的表头为列表 的表头为列表
其余分析同上。 其余分析同上。
5.2 广义表操作的实现
基于广义表是递归定义的结构, 基于广义表是递归定义的结构,因此实现广 义表操作的算法均为递归函数。 义表操作的算法均为递归函数。
5.1.3广义表的几个性质 广义表的几个性质
①有次序性: 有次序性: 广义表中的数据元素有固定的相对次序。 广义表中的数据元素有固定的相对次序。 线性排列 --线性表的推广 线性表的推广 层次结构 --树的推广 树的推广 有长度: ②有长度: 广义表的长度定义为最外层 最外层括弧中包含的数 广义表的长度定义为最外层括弧中包含的数 据元素个数。 据元素个数。 表元素个数一定,不能无限,可以是空表。 表元素个数一定,不能无限,可以是空表。
第5章 广义表 章
5.1 广义表的定义 5.2 广义表操作的实现
5.1 广义表的定义
5.1.1 定义
广义表( 广义表(Lists)简称表,它是线性表的推广。 )简称表,它是线性表的推广。 一个广义表是n( 一个广义表是 (n≥0)个元素的序列,当 )个元素的序列, n=0时称为空表。在一个非空的广义表中,其 时称为空表。 时称为空表 在一个非空的广义表中, 元素可以是某一确定类型的对象( 元素可以是某一确定类型的对象(这种元素 被称作单元素),也可以是由单元素构成的 单元素), 被称作单元素),也可以是由单元素构成的 这种元素可相对地被称作子表 子表或 表(这种元素可相对地被称作子表或表元 )。显然 广义表的定义是递归的, 显然, 素)。显然,广义表的定义是递归的,广义 表是一种递归的数据结构。 表是一种递归的数据结构。
5.1.4 广义表的存储结构
由于广义表中的数据元素可以是原子, 由于广义表中的数据元素可以是原子,也可 以是广义表,显然难以用顺序存储结构表示。 以是广义表,显然难以用顺序存储结构表示。 由于列表中的数据元素可能为原子或列表, 由于列表中的数据元素可能为原子或列表, 由此需要两种结构的结点:一种是表结点, 由此需要两种结构的结点:一种是表结点, 用以表示列表;一种是原子结点, 用以表示列表;一种是原子结点,用以表示 原子。 原子。
④D=(A,B,C) ( , , ) 长度为3,表头为A, 表尾为( , )。 长度为 ,表头为 表尾为(B,C)。
⑤E=(a,E) ( , ) 长度为2,表头为a,表尾为( ) 表头为 表头为E, 长度为 ,表头为 ,表尾为(E)--表头为 ,表尾为 ()。E相当于无穷表 相当于无穷表( ,( ,(a,( ,( ,(…))))。 ()。 相当于无穷表(a,( (a,( ))))。
void initGList(GList& L); // 创建空的广义表 创建空的广义表L int getLength(GList L); int getDepth(GList L); void creatGList(GList& L,string s); void printGList(GList& L,int k=0);
tag=0 atom
原子结点
tag=1
ptr---表结点的指针域 hp tp
表结点
广义表的头尾链表存储表示: 广义表的头尾链表存储表示:
enum ElemTag {ATOM,LIST}; typedef char AtomType; struct GLNode{ ElemTag tag; union { AtomType atom; struct { GLNode *hp,*tp; } ptr; }; }; typedef GLNode* GList;
5.2.3 操作的实现
glist.cpp
#include "glist.h" ⑴ 初始化 void initGList(GList& L) { L=NULL; } //创建空的广义表 创建空的广义表L 创建空的广义表
⑵ 求广义表的长度 int getLength(GList L) //返回广义表的长度,即元素个数 返回广义表的长度, 返回广义表的长度 { if(!L) return 0; else return 1+getLength(L->ptr.tp); }
5.2.1 分治法与递归求解
分治法是进行算法设计的一种方法,其严格定义为: 分治法是进行算法设计的一种方法,其严格定义为: 的函数或问题, 对于一个输入规模为 n 的函数或问题,用某种 个子集,从而产生m个 方法把输入分割成 k(1<k≤n) 个子集,从而产生 个 子问题,分别求解这m个问题 得出m个问题的子 个问题, 子问题,分别求解这 个问题,得出 个问题的子 再用某种方法把它们组合成原来问题的解。 解,再用某种方法把它们组合成原来问题的解。若 子问题还相当大,则可以反复使用分治法, 子问题还相当大,则可以反复使用分治法,直至最 后所分得的子问题足够小,以至可以直接求解为止。 后所分得的子问题足够小,以至可以直接求解为止。 在利用分治法求解时, 在利用分治法求解时,若所得子问题的性质和原问 题相同,则可递归求解。 题相同,则可递归求解。
③有深度: 有深度: 广义表的深度(多少层) 广义表的深度(多少层)定义为广义表书写 形式中括弧的最大重数,因此空表的深度为1, 形式中括弧的最大重数,因此空表的深度为 , 因为一个"单原子 不是广义表,所以没有"深 单原子"不是广义表 因为一个 单原子 不是广义表,所以没有 深 可言, 度"可言,但可以认为它的深度为 。 可言 但可以认为它的深度为0。 如:A,B:1;C:2;D:3; E:无穷大。 , : ; : ; : ; :无穷大。 可递归: ④可递归: 如E。 。 可共享: 子表可共享。 ⑤可共享: 子表可共享。