930 likes | 1.06k Views
数据结构. 数据结构的概念. 表:学生档案. 数据结构的概念. 数据( data ): 凡是能输入到计算机的描述客观事物的符号 数据元素( data element ): 也叫结点( node )或记录( record ),是数据的基本单位。如:表中的一行就是一个数据元素。 数据项( data item ): 是数据不可分割的最小单位。如:“学号”、”数学“等。 数据对象( data object ): 指性质相同的数据元素的集合。如:所有的男生就构成一个数据对象。
E N D
数据结构的概念 表:学生档案
数据结构的概念 • 数据(data):凡是能输入到计算机的描述客观事物的符号 • 数据元素(data element):也叫结点(node)或记录(record),是数据的基本单位。如:表中的一行就是一个数据元素。 • 数据项(data item):是数据不可分割的最小单位。如:“学号”、”数学“等。 • 数据对象(data object):指性质相同的数据元素的集合。如:所有的男生就构成一个数据对象。 • 数据结构(data structure):数据之间的关系。包括数据的逻辑结构和数据的存储结构(物理结构)。
数据的逻辑结构 • 计算机所处理的数据一般都存在某种内在的、特殊的关系,这种数据本身以及它们内在的相互之间的逻辑关系,叫做数据的逻辑结构。 • 用一个二元组 B=(D,S) 表示。 D表示结点构成的集合 S表示D中结点之间关系的集合 包括初等类型和组合类型 • 整型 • 实型 • 布尔型 • 字符型 • 指针型 • 集合结构:数据元素之间除了同属于一个集合的关系外,无任何其它关系。 • 线性结构:数据元素之间存在一对一的线性关系。 • 树型结构:数据元素之间存在一对多的层次关系。 • 图结构:数据元素之间存在多对多的任意关系。
数据的存储结构 顺序存储(数组) • 表中一行(即一个学生信息)抽象成一个结点ai,ai与a(i+1)在存储上是相邻的。 • 设a1的起始地址为S,每个元素占M个存储单元(字节),则第i个元素的起始存储位置为 • loc(ai)=loc(a1)+(i-1)m
数据的存储结构 • 链式存储(指针)
栈 • 先进后出表(FILO)或下推表 • 假设栈的最大长度为m,所有结点都具有同一类型stype,则定义栈如下: Const m=栈的最大长度; Type stack=array[1..m] of stype; {栈类型} Var s:stack; {栈} t:integer; {栈顶指针} xx:stype; • 栈的基本运算: • 入栈 push • 出栈 pop • 读取栈顶元素 top
入栈 push(s,x,t) • 过程push(s,x,t)往栈S中压入一个值为X的结点。 Procedure push(var s:stack; x:stype; var t:integer;); Begin if t=m then writeln(‘full!’) else begin t:=t+1; s[t]:=x; end; End;
出栈 pop(s,t) • 函数pop(s,t)从栈s中弹出一个结点。 Function pop(var s:stack; var t:integer;):stype; Begin if t=0 then writeln(‘empty!’) else begin pop:=s[t]; t:=t-1; end; End; Procedure pop(var s:stack; var t:integer); Begin if t=0 then writeln(‘empty!’) else begin xx:=s[t]; t:=t-1; end; end;
读取栈顶元素 top(s,t) • 函数top(s,t)读栈顶元素。 Function top(s:stack; t:integer;):stype; Begin if t=0 then writeln(‘empty!) else top:=s[t]; End;
栈的应用 • 计算表达式的值 • 非递归的回溯法
计算表达式的值 • 输入一个表达式,该表达式含+、-、*、/、(、)和操作数,所含字符数不超过255,以@结束,输出该表达式的值。 • 分析:字符串输入,不能进行数值计算,所以,需要将输入的中缀表达式转换成后缀表达式。 输入字符串:e 后缀表达式:a 存放运算符的栈:s
计算表达式的值 当e[i]为: • 数字: e[i]压入a; • (: e[i]压入s; • ): 将s中栈顶至(间的所有运算符出栈进入a,丢弃(; • +、-: 将s中栈顶至(间的所有运算符出栈进入a, e[i]进入s; • *、/: 将s中栈顶至(前的第一个+或-前的所有运算符出栈进入a,e[i]压入s; • 例e:5 * 8 + 3 * ( 5 * 2 / 3 – 4 ) + 7 • 则a:5 8 * 3 5 2 * 3 / 4 - * + 7 +
队列 • 先进先出表(FIFO) • 插入的一端称为队尾r,删除的一端称为队首f f r f r f r
定义队列: Const m=队列元素的上限; Type equeue=array[1..m] of qtype; Var q:equeue; r,f:integer; • 初始:f=r=0 • 队满:r=m • 队空:f=r 队列的主要运算: • 入队(ADD) • 出队(DEL)
入队ADD(q,x,r) • 过程ADD(q,x,r)在队列q的尾端插入元素x Procedure ADD(var q:equeue; x:qtype; var r:integer;); Begin if r=m then writeln(‘full!’) else begin r:=r+1; q[r]:=x; end; End;
出队 DEL(q,y,f) • 过程DEL(q,y,f)取出q队列的队首元素y Procedure DEL(var q:equeue; var y:qtype; var f:integer;); Begin if f=r then writeln(‘empty’) else begin f:=f+1; y:=q[f]; end; End;
假溢出 • 随着队列一端插入,一端删除,队列在数组中不断向队尾方向移动,而在队首产生一片不以利用的空闲存储区,最后会导致当r=m时,不能再加入元素,形成假溢出。 r f r r f f f r 初始 加入3个元素 删除3个元素 加入m-3个元素,队满 f=r=0 f=0 r=3 f=r=3 r=m f=3
循环队列 • 初始:f=r=m • 入队:r:=r+1; r:=r mod m +1 if r=m+1 then r:=1; • 出队:f:=f+1; f:=f mod m +1 if f=m+1 then f:=1; • 队空:f=r; • 队满:f=r mod m+1; m m-1 f 3 2 r 1
r a b c e f g h i j k 树 • 树的递归定义: • 有且仅有一个结点没有前驱(父结点),该结点为树的根 • 除根外,其余所有结点有且仅有一个前驱 • 除根外,每一个结点都通过唯一的路径连到根上 根结点 第一层 分支结点 第二层 第三层 叶结点 第四层
树 • 结点的度=该结点的子树树目 • 树的度=max(所有结点的度) • 树的深度(高度)=树中最大的层次 • 森林:若干棵互不相交的树的集合 • 有序树和无序树
树的表示方法 • 自然界的树形表示法:用结点和边表示树 • 括号表示法:先将根结点放入一对()中,然后把它的子树按由左而右的顺序放入()中,而对子树也采用同样方法处理:同层子树放入它们根结点后面的()中,同层子树之间用逗号隔开: (r(a(e,f(j)),b(g),c(h(k),i)))
树的存储结构 • 静态记录数组 所有结点存储在一个数组中,数组元素为记录类型,包括数据域和长度为n(n为树的度)的数组,分别存储该结点的每一个儿子的下标。 Const n=树的度; max=结点数的上限; Type node=record data:datatype; ch:array[1..n]of integer; end; treetype=array[1..max] of node; Var tree:treetype;
树的存储结构 • 动态多重链表 每个结点由数据域和n(n为树的度)个指针域共n+1个域组成。 const n=树的度; Type treetype=^node; node=record data:datatype; next:array[1..n] of treetype; end; var root:treetype;
二叉树 • 二叉树的递归定义 • 二叉树是以结点为元素的有限集,它或者为空,或者满足以下条件: • 有一个特定的结点称为根; • 余下的结点分为互不相交的子集L和R,其中R是根的左子树,L是根的右子树,L和R又是一棵二叉树。 • 二叉树和树是两个不同的概念: • 树的每个结点可以有任意多个后继,而二叉树中每个结点的后继不能超过2; • 树的子树可以不分次序(有序树除外),而二叉树的子树有左右之分。
a 空二叉树 b 只有一个结点的二叉树 c 只有左子树的二叉树 d 只有右子树的二叉树 e 左、右子树均有的二叉树 二叉树的形态 • 二叉树的五种基本形态
二叉树的两个特殊形态 • 满二叉树:一棵深度为K且有2K-1个结点的二叉树称为满二叉树 • 完全二叉树:如果一棵二叉树最多只有最下面两层结点度数可以小于2,并且最下面一层的结点都集中在该层最左边的若干位置上,则称此二叉树为完全二叉树。
二叉树的存储结构 • 顺序存储结构 Const m=树中结点数上限; Type node=record data:datatype; prt,lch,rch:0..m; end; Treetype=array[1.m]of node; Var tree:treetype;
二叉树的存储结构 • 链式存储结构 Type bitrpetr=^node; node=record data:datatype; lch,rch:bitrpetr; end; Var bt:bitreptr;
a b c d e f g h i j l k 二叉树的遍历 • 前序:abdheicfjkgl • 中序:dhbeiajkfclg • 后序:hdiebkjflgca
前序遍历 • 二叉链表 Procedure preorder(bt:bitreptr); Begin if bt<>nil then begin 访问处理bt^.data; preorder(bt^.lch); preorder(bt^.rch); end; End; • 二叉树的顺序存储结构 • Procedure preorder(i:integer;); • Begin if i<>0 then • begin • 访问处理tree[i].data; • preorder(tree[i].lch); • preorder(tree[i].rch); • end; • End;
中序遍历 • 二叉链表 Procedure inorder(bt:bitreptr); Begin if bt<>nil then begin inorder(bt^.lch);访问处理bt^.data; inorder(bt^.rch); end; End; • 二叉树的顺序存储结构 • Procedure inorder(i:integer;); • Begin if i<>0 then • begin • inorder(tree[i].lch); • 访问处理tree[i].data; • inorder(tree[i].rch); • end; • End;
后序遍历 • 二叉链表 Procedure postorder(bt:bitreptr); Begin if bt<>nil then begin postorder(bt^.lch); postorder(bt^.rch); 访问处理bt^.data; end; End; • 二叉树的顺序存储结构 • Procedure inorder(i:integer;); • Begin if i<>0 then • begin • inorder(tree[i].lch); • inorder(tree[i].rch); • 访问处理tree[i].data; • end; • End;
r a r r w b x a b c a b c f c d w f s t u x w f s t u x s h e d e i j d e i j i t h m o n h m o n m j u o n 普通有序树的遍历 • 普通树转换成二叉树 • 长子变左儿子 • 兄弟变右儿子
r s t e a b f c d g t r s s t r e a d e a d b f b f c g c g 森林的遍历 • 森林转换成二叉树
由中序和后序确定前序 • 中序和后序确定前序 • 中序:s’=s1’……sk’……sn’ • 后序:s’’=s1’’…………sn’’ • 显然, sn’’为根,在前序中直接输出,设在中序中与sn’’相同的字符为sk’ • 若k>1,则左子树存在, s1’……sk-1’为左子树的中序遍历,s1’’……sk-1’’为左子树的后序遍历 • 若k<n,则右子树存在, sk+1’……sn’为右子树的中序遍历,sk’’……sn-1’’为右子树的后序遍历
由中序和后序确定前序 Procedure solve1(s1,s2:string); Var k:integer; Begin if length(s2)=1 then write(s2) {递归出口} else begin k:=pos(s2[length(s2)],s1); write(s1[k]); if k>1 then solve1(copy(s1,1,k-1),copy(s2,1,k-1)); if k<length(s1) then solve1(copy(s1,k+1,length(s1)-k),copy(s2,k,length(s2)-k)); end; End;
由中序和前序确定后序 Procedure solve2(s1,s2:string); Var k:integer; Begin if length(s2)=1 then write(s2) else begin k:=pos(s2[1],s1); if k>1 then solve2(copy(s1,1,k1),copy(s2,2,k)); if k<length(s1) then solve2(copy(s1,k+1,length(s1)-k), copy(s2,k+1,length(s2)-k)); end; End;
二叉树的应用 • 二叉排序树 • 最优二叉树
二叉排序树 • 二叉排序树是具有以下性质的非空二叉树: • 若根的左子树不空,则左子树的所有结点值均小于根结点值; • 若根的右子树不空,则右子树的所有结点值均不小于根结点值; • 根结点的左、右子树也分别为二叉排序树。 例:输入序列a1,a2……an(1<=n<=1000),将a按照递增顺序排列后输出。 • 构造二叉排序树的方法: • 令a1为二叉树的根; • 若a2<a1,则令a2为a1左子树的根结点,否则令a2为a1右子树的根结点; • 对a3,a4……an递归重复②。
构造二叉排序树 例:a序列为:35 40 30 90 82 32 33 37 35 30 40 32 37 90 33 82 中序遍历:30 32 33 35 37 40 82 90
构造二叉排序树 procedure createtree; begin fillchar(b,sizeof(b),0); b[1].data:=a[1]; for i:=2 to n do begin b[i].data:=a[i]; p:=1; while true do if a[i]<b[p].data then if b[p].l<>0 then p:=b[p].l else begin b[p].l:=i; break; end else if b[p].r<>0 then p:=b[p].r else begin b[p].r:=i; break; end; end; end; 稳定的
构造二叉排序树 • 主程序 Begin readln(n); for i:=1 to n do read(a[i]); writeln; createtree; inorder(1); End.
<80 <60 <70 <90 不及格 <70 <60 中 良 优 及格 <80 不及格 及格 中 <90 良 优 最优二叉树 • 最优二叉树(哈夫曼树、最优搜索树) 例:计算最优的判定过程 全校学生的成绩由百分制转换成五等分制,在五个等级上分布不均匀,颁布规律如下: 百分制分数范围 0~59 60~69 70~79 80~89 90~100 分布情况% 5 15 40 30 10 现有10000个数据,以下两种判定转化过程: K1=10000*(1*5%+2*15%+3*40%+4*(30%+10%))=31500 K2=10000*(2*(40%+30%+10%)+3*(5%+15%))=22000
最优二叉树 • 结点的路径长度:从根到每个结点的路径长度 • 叶结点的权值:叶结点被赋予的实数值 • 设Wk为第k个结点的权值,Pk为第k个叶结点的带权路径长度。 L=W1*P1+W2*P2……Wn*Pn 则使L最小的树称为最优二叉树。
24 7 5 2 4 6 24 A B C D E 6 7 5 6 13 11 A B E 6 A E B 7 6 5 7 6 11 13 11 C D A E 2 4 构造最优二叉树 • 构造方法: • 将给定的N个结点构成N棵二叉树的集合F,其中每棵二叉树Ti中只有一个权值为Wi的根结点Ki,其左右、子树均为空; • 在F中选取根结点权值最小的两棵二叉树作为左、右子树,构造一棵新的二叉树,并且置新的二叉树的根结点的权值为其左、右子树根结点的权值之和; • 在F中删除这两棵二叉树,同时将新得到的二叉树加入F中; • 重复②③,直到在F中只含有一棵二叉树为止。
最优二叉树 • 最优二叉树中非叶子结点的度均为2 • 如果叶结点数为N,则总结点数为2*N-1 Const n=叶结点的上限; m=2*n-1; Type node=record data:integer; prt,lch,rch,lth:0..m; end; wtype=array[1..n] of integer; treetype=array[1..m] of node; {tree[1..n]为叶子结点, tree[n+1..2*n-2]为分支结点,tree[2*n-1]为根} Var tree:treetype;
构造最优二叉树 Procedure hufm(w:wtype;var tree; var bt:integer); function min(h:integer):integer; begin m1:=maxint; for p:=1 to h do if (tree[p].prt=0) and (m1>tree[p].data) then begin i:=p; m1:= tree[p].data; end; min:=i; end; Begin fillchar(tree,sizeof(tree),0); for i:=1 to n do read(tree[i].data); for k:=n+1 to m do begin i:=min(k-1); tree[i].prt:=k; tree[k].lch:=i; j:=min(k-1); tree[j].prt:=k; tree[k].rch:=j; tree[k].data:=tree[i].data+tree[j].data; end; bt:=m; End;
求最优判断 Procedure ht(t:integer); {通过前序遍历计算每个叶子的路径长度} Begin if t=m then tree[t].lth:=0 else tree[t].lth:=tree[tree[t].prt].lth+1 if tree[t].lth<>0 then begin ht(tree[t].lch); ht(tree[t].rch); end; End; BEGIN readln(n); for i:=1 to 5 do readln(w[i]); hufm(w,tree,bt); ht(bt); writlen(m*tree[bt].data); for i:=1 to 5 do write(n*tree[i].lth*tree[i].data:0:0); END.
图 • 图是较线性表和树更为复杂的一种数据结构,在这种数据结构中,数据结点间的联系是任意的,因此它可以更广泛地表示数据元素之间的关系。 • 线性表和树是图的特例。