【教学课件】第十二章动态数据结构.ppt
《【教学课件】第十二章动态数据结构.ppt》由会员分享,可在线阅读,更多相关《【教学课件】第十二章动态数据结构.ppt(92页珍藏版)》请在三一办公上搜索。
1、第十二章 动态数据结构,管理动态变量动态数据结构栈 stack队列 queue 链表linkage table树tree图 graph程序设计实例本章小结作业,考虑上一章的职工卡片问题,用计算机管理这些卡片,要把卡片保存在计算机内。首先,用什么数据结构存储:一张卡片是一个结构体,所有卡片自然用结构体数组。第三,操作问题:若增加一个人,应该在数组中加一个元素,会产生数组不够大的可能。若增加一张卡片在数组中间,应该把加入位置以后的其它元素依次向后移动。若在中间删除一张卡片,会在数组中间留下一个“洞”,应该把“洞”以后的元素依次向前移动,使用数组带来的问题是:操作不方便;数组尺寸不好确定。第二,数组
2、多大:为保存全部卡片,并且人数不固定,就应该给一个足够大的数组。最好把这些卡片存储成动态的,需要多大存储量(有多少张卡片)就用多大。中间加一张卡片时不要向后串别的卡片,删除一张卡片时不要留下“洞”。,如图的链式结构可以满足要求:当增加一张卡片时,只需要向计算机系统申请一块空间,联到链的适当位置上。例如,要增加一张卡片 50 插入到 2、3 之间,则只需要如下修改指针:若删除一节,只需要将其从链上摘下来即可。例删除2节得链上已经没有2节了,删掉的节所占的存储空间还可以还回计算机系统,以便作其它用途。,这就是一种动态数据结构中的链表。动态数据结构上的一项是一个动态变量,指针是标识动态变量的有力手段
3、。动态变量与静态变量的区别在于:静态变量是程序中由程序员“显式”说明的变量。它有一个名字,在编译时,编译程序已经给它分配存储空间。这块存储空间用变量的名字来标识。,动态变量在程序中没有“显式”说明,它没有名字在编译时编译程序不知道有该变量,不给(也不可能给)它分配空间。动态变量是在程序运行时随程序存储数据的需要,申请空间函数(例如malloc,当然也是由程序员安排的)随机的动态的申请来的空间。它没有名字,一般动态变量都由指针标识。当使用完毕后,由释放空间函数(例如free)释放,还回计算机存储管理系统,以备它用。,注意:这里所说的静态变量不是C语言中由静态存储类别static声明的变量;动态变
4、量也不是C语言中由自动存储类别auto声明的变量。而是一般程序设计概念中的静态变量、动态变量,管理动态变量,动态变量在程序运行时,随程序存储数据的需要,向计算机系统申请;使用完后还回计算机系统。本节介绍申请计算机存储空间函数malloc释放存储空间函数free,内存 程序运行时,涉及用户程序的内存存储结构如右图所示,首先是目标代码区;然后是静态存储区,用于存放那些可用绝对地址标识的,主要是具有静态存储类别的数据和变量;接着是目标代码运行时用到的库程序代码区;最后剩余空间是栈区和堆区,栈区和堆区从剩余空间的两端,动态的向中间增长。栈区用来存储程序中声明的函数的局部变量等具有自动存储类别的数据和变
5、量;堆区用来存储经过动态申请空间函数申请的变量。,sizeof 运算符单目运算符 sizeof 的操作数是类型。运算结果是求得相应类型的尺寸,即存储相应类型数据所需要的字节数。sizeof(int)/*结果是2*/sizeof(char)/*结果是1*/sizeof(struct date)/*若 struct date 是第十一章定义的日期类型,结果是6*/,malloc 函数:原型 void*malloc(unsigned long size);功能 申请足够大内存区域用来存储长度为size的数据对象,返回该区域的首指针,并保证该区域符合任何数据类型对存储区域开始地址和对齐的要求。返回指针
6、是void类型的,调用者必须使用显示强制类型转换,把该指针转换成所需要类型的指针。,例:float*p;p=(float*)malloc(sizeof(float);struct date*pdate;pdate=(struct date*)malloc(sizeof(struct date);,free函数动态申请的内存如果不再使用,应当适时释放这样可以提高程序运行效率。free函数用来释放经过malloc申请的动态空间。free的函数原型 void free(void*ptr);功能释放由malloc申请的内存区域。free的参数ptr是一个指针,指向以前由malloc申请的一个内存区域。
7、,例申请float*p;p=(float*)malloc(sizeof(float);struct date*pdate;pdate=(struct date*)malloc(sizeof(struct date);释放free(p);free(pdate);,free(ptr)/*释放ptr所指向由malloc申请的内存空间*/一块存储区域一经释放,便不能再使用。使用free特别注意,操作不当会产生不可预料的结果。如下情况下使用free都会造成灾难性后果。ptr无值;ptr的值为NULL;ptr所指向的空间不是经过malloc申请来的;对一次申请的存储区进行多次释放(实际可能是ptr无值或值
8、为NULL)。,实用问题:若指针变量指向的用malloc申请来的动态变量,是孤立的不能与其它变量相联系,显然作用不大。引进动态变量的目的是构造动态数据结构。例如象本章开始介绍的那样,构造一个链表等。这就要求一个数据项上除基本的数据信息外,还应包含与其它数据项相联系的信息,也就是包含指针。该结构必须用结构体类型描述,链表上一节的类型定义形式。,栈 stack,在第六章已经用数组实现过栈和队列,但是,数组有一定的局限性。如图可以用单向链表实现栈,指针变量top指向栈顶。栈的操作有:初始化:stackintial 压栈:stackpush 弹栈:stackpop,设有声明:typedef.items
9、;typedef struct stackcell items data;struct stackcell*predocessor;stackcelltype;typedef stackcelltype*pstacktype;pstacktype top;,如下实现栈的操作:void stackinitial(void)top=NULL;void stackpush(items x)pstacktype p;p=(pstacktype)malloc(sizeof(stackcelltype);p-data=x;p-prodocessor=top;top=p;void stackpop(item
10、s*x)pstacktype p;if(top!=NULL)*x=top-data;p=top;top=top-predecessor;free(p);else printf(“栈下溢n”);,看一下这三个操作:初始化后(调用stackinitail)得。调用一次 stackpush(1)得。再调用一次stackpush(2)得。调用一次stackpop(&b)得。,2,队列,如图可用单向链表实现队列,现在要用两个指针变量,一个指向队头(front),一个指向队尾(rear)。队列的操作有 初始化(queueinitial)进队排在队尾(inqueue)出队从队头删一项(outqueue),设
11、有如下说明:typedef.items;typedef struct queue items data struct queue*next;queuetype;typedef queuetype*pqueuetype;pqueuetype front,rear;操作:void queueinital(void)front=NULL;rear=NULL;,void inqueue(item x)pqueuetype p;p=(pqueuetype)malloc(sizeof(queuetype);p-data=x;p-next=NULL;if(rear=NULL)rear=p;front=p;e
12、lse rear-next=p;rear=p;,void outgueue(item*x)pqueuetype p;if(front=NULL)printf(“队空n”);else*x=front-data;p=front;front=front-next;if(front=NULL)rear=NULL;free(p);,看一下这三个操作:1.调用初始化后(调用一次 queueinitail)得;2.调用一次ingueue(1)得;再调用一次ingueue(2)得;再调用一次 ingueue(3)得;3.调用一次 outgueue(再调用一次 outgueue(&a)得。,1,2,3,NULL
13、,NULL,链表linkage table,实际上前边讲的栈,队列都是单向链表,但是栈和队列只是单向链表的两种特殊应用操作只在头尾进行。下边介绍单向链表的一般操作:创建单向链表:创建单向链表,是指用一项一项的数据逐步建立、形成一个链表。可以分成向链头加入数据和向链尾加入数据两种方式。创建单向链表可以分成向链头加入数据和向链尾加入数据两种方式。新项加入链头就是压栈的算法;新项加入链尾就是队列中入队的算法。只要反复调用那里的函数或将函数体放在循环语句中即可,这里不赘述。,遍历单向链表:遍历是指从头到尾将链表上数据全部加工一遍。可用如下左端的算法。但实用中,经常用如下右端 的算法来遍历单向链表。,p
14、=base;p0=NULL;while(p!=NULL)p=base;加工 p-while(p!=NULL)p=p-next;加工 p-p0=p;p=p-next;,在单向链表上检索:检索是指在单向链表上查找关键字等于某给定值的节点,若找到则带回相应节点的指针;否则带回NULL。设关键字域域名为key;欲检索的关键字值为key0.如下算法实现检索:p0=NULL;p=base;while(p!=NULL,向单向链表插入一项:设有下图的链表,现在要把r所指示的数据项插入到 p0、p 所指两项之间。操作是:r-next=p;p0-next=r;p0=r/*使 p0 仍为 p 的前一项*/,p0:,
15、p:,1,2,3,4,.,.,从单向链表上删除一项:设有下图的链表,现在要删除p所指项。删除算法是:q=p;p=p-next;p0-next=p;free(q),p0:,1,2,4,.,.,p:,交换单向链表上两项:设有如图所示链表。现在要把 p 所指的项与 q 所指的项交换 为了表示操作方便,我们把该链表分两段画出。,/*交换 p-next、q-next*/g=p-next;/*1*/p-next=q-next;/*2*/q-next=g;/*3*/*交换 p0-next、q0-next*/p0-next=q;/*4*/q0-next=p;/*5*/*交换 p、q*/p=p0-next;/*
16、6*/q=q0-next;/*7*/,树tree,两叉树,两叉树的每个数据项附带两个指针,分别指向它的两个分支。两叉树的定义:空是树;一个结点连接两个不相交的树,仍为树;所有结点具有相同的数据类型。,(a+b/c)*(d e*f),设 ti 为二叉树的一个结点,一般 ti 由两部分组成:基本数据部分-保存本结点上的基本数据;指针部分连-接本结点以下的其它结点。结点 ti 的指针连接的结点称为 ti 的子结点,相应 ti 称为其子结点的父结点。,ti的指针部分有两个指针:左指针、右指针。称 ti 的左指针连接的部分为 ti 的左子树,ti 的右指针连接的部分为 ti 的右子树。若左、右子树均空,
17、则称 ti 为叶结点。,ti,为了检索操作方便,一般把两叉树组织成两叉检索树。两叉检索树的定义是:设树中每个结点的数据部分有一个数据项 key 是有序的,称该数据项为关键字。一个两叉树称为检索树,如果对每个结点 ti,它的左子树中所有结点的 key 值都小于 ti 的 key 值;ti 的右子树中所有结点的 key 值都大于 ti 的 key 值。,二叉检索树的操作有:遍历检索插入一个结点删除一个结点 由于树是递归定义的,所以树的操作用递归算法十分简洁。,设有说明部分:typedef.keytype;typedef.datatype;typedef struct tree keytype ke
18、y;datatype data;struct tree*left;struct tree*right;treetype;typedef treetype*treepointer;treepointer root;,遍历遍历二叉树是指按一定规律走遍树的每个结点,使每一结点被访问一次,而且只被访问一次。在访问一个结点时,可以做任何信息加工工作。下例打印结点的data域,并设该域为char型的。遍历算法可分为前序,中序,后序遍历三种。前序遍历:对任意一个结点 ti 来讲,先访问及处理该结点的数据域;然后遍历左子树;最后遍历右子树。中序遍历:对任意一个结点 ti 来讲,先遍历左子树;然后访问及处理该结
19、点的数据域;最后遍历右子树。后序遍历:对任意一个结点 ti 来讲,先遍历左子树;然后遍历右子树;最后访问及处理该结点的数据域。,【例12-1】设有下图所示树,这是由表达式(a+b/c)*(d-e*f)生成的树,这棵树反映了表达式的结构,同时也反映了表达式的运算次序。前序遍历过程是:得到表达式的波兰表示式(运算符在两个运算分量前)。前序遍历算法是:void preorder(treepointer p)if(p!=NULL)printf(“%c”,p-data);preorder(p-left);preorder(p-right),a,/,b,c,-,d,*,e,f,中序遍历过程是:得到表达式的
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 教学课件 教学 课件 第十二 章动 数据结构
链接地址:https://www.31ppt.com/p-5664291.html