【关键字】实验
实验一 多项式的链表表示及运算,二叉树(必做)
1. 实验目的:掌握线性链表的保存、运算及应用。利用链表实现一元多项式计算。 2. 实验内容:
1)
2) 3) 4)
编写函数,实现用链表结构建立多项式; 编写函数,实现多项式的加法运算; 编写函数,实现多项式的显示;
测试:编写主函数,它定义并建立两个多项式,显示两个多项式,然后将它们相加并显示结果。变换测试用的多项式,检查程序的执行结果。
选做内容:修改程序,选择实现以下功能:
5) 多项式输入:修改主函数,从终端输入多项式的系数和指数,并构建出多项式
链表。
6) 多项式求值:编写一个函数,根据给定的x值计算并返回多项式f(x)的值。测试
该函数(从终端输入一个x的值,调用该函数并显示返回结果)。 7) 多项式相减:编写一个函数,求两个多项式相减的多项式。 8) 多项式相乘:编写一个函数,求两个多项式的乘积多项式。 3. 算法说明:
1) 多项式的建立、输出和相加算法见教材和讲义。
2) 多项式减法:同次项的系数相减(缺项的系数是0)。例如a(x)=3+2x-5x2,
b(x)=3x-4x3,则a(x)-b(x) = 3-x-5x2+4x3。提示:a(x)-b(x) = a(x)+(-b(x))。
3) 多项式乘法:两个多项式的相乘是“系数相乘,指数相加”。算法思想是用一个多
项式中的各项分别与另一个多项式相乘,形成多个多项式,再将它们累加在一起。例如,a(x)=3+2x-5x2,b(x)=3x-4x3,则a(x)*b(x) = (3x)*(3+2x-5x2) + (-4x3)*(3+2x-5x2) = (9x+6x2-15x3) + (-12x3-8x4+20x5) = 9x+6x2-27x3-8x4+20x5。
4.实验过程
线性表的链式保存结构的特点是用一组任意的保存单元保存线性表中的数据元素,这组保存单元可以是连续的,也可以是不连续的。这样,逻辑上相邻的元素在物理位置上就不一定是相邻的,为了能正确反映元素的逻辑顺序,就必须在保存每个元素ai的同时,保存其直接后继元素的保存位置。这时,存放数据元素的结点至少包括两个域,一个域存放该元素的数据,称为数据域(data);另一个域存放后继结点在保存器中的地址,称为指针域或链域(next)。这种链式分配的保存结构称为链表。数据元素的结点结构如下:
此结构的C语言描述为 struct node{
int data;
文档来源为:从网络收集整理.word版本可编辑.欢迎下载支持.
struct node *next; };
typedef struct node NODE;
要对单链表进行操作,首先要掌握怎样建立单链表。链表是一种动态保存结构,所需的保存空间只有在程序执行malloc之后,才可能申请到一个可用结点空间;free(p)的作用是系统回收一个结点,回收后的空间可以备作再次生成结点时用。整个可用保存空间可为多个链表共同享用,每个链表占用的空间不需预先分配划定,而是由系统应需求即时生成。因此,建立线性表的链式保存结构的过程就是一个动态生成链表的过程。即从“空表”的初始状态起,依次建立各元素结点,并逐个插入链表。
动态建立线性表的链式保存结构有两种基本方法,分别适用于不同的场合。可根据所建链表结点的顺序要求选择采用一种方法。 单链表建立方法一:反向建立链表。
思想:若线性表的元素已顺序存放在一维数组A[N]中,建表方法是从线性表的最后一个元素开始,从后向前依次插入到当前链表的第一个结点之前。 #define N m int A[N];
/*m为链表中数据元素的个数*/
NODE *creatlink1( ) {
s = (NODE *)malloc(sizeof(NODE)); s -> data = A[i]; s -> next = head -> next; head -> next = s; } {
NODE *head, *s; int i;
head = (NODE *)malloc(sizeof(NODE)); head -> next = NULL; for(i=N?1; i>=0; i-?)
return head;
文档来源为:从网络收集整理.word版本可编辑.欢迎下载支持.
}
单链表建立方法二:正向建立单链表。
思想:依次读入线性表的元素,从前往后依次将元素插入到当前链表的最后一个结点之后。 NODE *creatlink2( )
{
NODE *head, *p, *s; int num;
{
head = (NODE *)malloc(sizeof(NODE)); scanf(''%d'', &num); p = head; while (num!=0)
s = (NODE *)malloc(sizeof(NODE)); s -> data = num; p -> next = s; p = s;
scanf(\"%d\}
p -> next = NULL; return head; }
具体实例的题面:
设有多项式:A(x)=7+3x+9x8+3x15 B(x)=5x+6x7-9x8 (1) 用单链表给出A(x)的存储表示; (2)用单链表给出B(x)的存储表示;
(3)以上述两个单链表为基础,通过插入和删除等运算给出A(x)+B(x)的存储表示,
其存储空间覆盖A(x)和B(x)的存储空间
解:首先要解决多项式的表征问题,即在程序中如何表征多项式,如写成: f(x)=p1 xe1 +p2xe2+p3xe3+…+pnxen 很明显,我们可以把多项式看作是一个线性表,线性表中的数据元素是一个二元组(系数,
文档来源为:从网络收集整理.word版本可编辑.欢迎下载支持.
指数)。用线性表((p1,e1),(p2,e2),…(pn,en))唯一代表一个多项式。
其次,是选取顺序表还是链表来存储多项式?如果用户输入的多项式的项数事先无法知道,最好采用链表来存储多项式。
再次,程序的主要操作是多项式相加,相加结果存储到新的链表中还是占用原来的链表?
为了操作方便,先采用将相加结果存放到一个新链表中。 最后,方便相加,表征多项式的链表应该按照指数降序排列。
想一想,降低程序的空间复杂度,两个多项式相加的结果被存放到了一个新的链表中。可以设计一个程序,利用第二个链表存放相加后的结果。
进一步解释几个问题: (1)怎么表征((p1,e1),(p2,e2),…(pn,en))?
为了正确表示结点间的逻辑关系,在存储每个结点值的同时,还必须存储指示其直接后继结点的地址(或位置),称为指针(pointer)或链(link),这两部分组成了链表中的结点结构,如图2-2所示。
链表是通过每个结点的指针域将线性表的n个结点按其逻辑次序链接在一起的。 每一个结只包含一个指针域的链表,称为单链表。
为操作方便,总是在链表的第一个结点之前附设一个头结点(头指针)head指向第一个结点。头结点的数据域可以不存储任何信息(或链表长度等信息)。
结点是通过动态分配和释放来的实现,即需要时分配,不需要时释放。实现时是分别使用C语言提供的标准函数:malloc() ,realloc(),sizeof() ,free() 。
动态分配 p=(LNode*)malloc(sizeof(LNode));
函数malloc分配了一个类型为LNode的结点变量的空间,并将其首地址放入指针变量p中。
动态释放 free(p) ;
系统回收由指针变量p所指向的内存区。P必须是最近一次调用malloc函数时的返回值。
(2)最常用的基本操作及其示意图 (a) 结点的赋值 LNode *p;
p=(LNode*)malloc(sizeof(LNode)); p->data=20; p->next=NULL ; (b)常见的指针操作
实验1.2 二叉树应用(必做)
树是非线性结构。本实验继续突出了数据结构加操作的程序设计观点,根据树结构的非
线性特点,将操作进一步集中在遍历操作上,因为遍历操作是其他众多操作的基础。本次实验目的是熟悉二叉树的二叉链表存储结构,以及在此基础上实现二叉树的应用。
1. 实验目的:掌握二叉树的链式存储结构和常用算法。利用哈夫曼树设计最优压缩编
码。
文档来源为:从网络收集整理.word版本可编辑.欢迎下载支持.
2. 问题描述:建立一棵二叉树(不少于15个结点),并对其进行遍历(先序、中序、
后序),打印输出遍历结果。 3. 基本要求:
1.从键盘接受输入(先序),以二叉链表作为存储结构,建立二叉树(以先序来建立)。
2.采用递归算法对其进行遍历(先序、中序、后序),将遍历结果打印输出。 3.采用非递归实现先序和中序遍历,将遍历结果打印输出。
4.编写算法,交换二叉树上所有结点的左、右子树,并以缩格形式打印出交换
前后的该树上的结点信息。
4. 实验内容:
1) 编写函数,静态或动态创建二叉树。
2) 编写遍历函数,实现二叉树的先序、中序、后序遍历。 3) 编写主函数,调试程序。
选做内容:修改程序,选择实现以下功能:
4) 编写主函数,从终端输入一段英文文本;统计各个字符出现的频率,然后构建
哈夫曼树并求出对应的哈夫曼编码;显示哈夫曼树和哈夫曼编码。 5) 编写函数,实现建立哈夫曼树和生成哈夫曼编码的功能。
6) 编码:用哈夫曼编码对一段英文文本进行压缩编码,显示编码后的文本编码序
列;
7) 统计:计算并显示文本的压缩比例;
8) 解码:将采用哈夫曼编码压缩的文本还原为英文文本。 9) 采用非递归算法实现二叉树后序遍历。 5.测试数据
ABCффDEфGффFффф(其中ф表示空格字符) 则输出结果为 先序:ABCDEGF 中序:CBEGDFA 后序:CGBFDBA 6.算法说明:
1) 二叉树和哈夫曼树的相关算法见教材和讲义。
2) 编码的方法是:从头开始逐个读取文本字符串中的每个字符,查编码表得到它
的编码并输出。重复处理直至文本结束。
3) 解码的方法是:将指针指向哈夫曼树的树根,从头开始逐个读取编码序列中的
每位,若该位为1则向右子树走,为0则向左子树走。当走到叶子节点时,取出节点中的字符并输出。重新将指针放到树根,继续以上过程直至编码序列处理完毕。
压缩比例的计算:编码后的文本长度为编码序列中的0和1的个数,原文本长度为字符数*8。两者之比即为压缩比。 二叉树的三种遍历方式: (1) 先序遍历:
若二叉树为空,则空操作;否则 ① 访问根结点; ② 先序遍历左子树;
文档来源为:从网络收集整理.word版本可编辑.欢迎下载支持.
③ 先序遍历右子树。 2) 中序遍历:
若二叉树为空,则空操作;否则 ① 中序遍历左子树; ② 访问根结点; ③ 中序遍历右子树。 (3) 后序遍历:
若二叉树为空,则空操作;否则 ① 后序遍历左子树; ② 后序遍历右子树。 ③ 访问根结点;
二叉链表的C语言描述如下: struct tnode { int data;
struct tnode *lchild, *rchild; }
typedef struct tnode TNODE;
根据先序遍历的定义,先序遍历二叉树的递归算法如下 void preorder(TNODE *bt) {
if (bt != NULL) {
printf(''%d \\n'', bt->data); preorder(bt->lchild); preorder(bt->rchild); } }
根据中序遍历的定义,中序遍历二叉树的递归算法如下: void inorder(TNODE *bt) {
if (bt != NULL) {
inorder(bt->lchild);
printf(''%d \\n'', bt->data); inorder(bt->rchild); } }
根据后序遍历的定义,后序遍历二叉树的递归算法如下: void postorder(TNODE *bt) {
if (bt != NULL) {
postorder(bt->lchild); postorder(bt->rchild);
文档来源为:从网络收集整理.word版本可编辑.欢迎下载支持.
printf(''%d \\n'', bt->data); }
}
复习递归算法: 什么叫递归?
现象:从前有座山,山里有座庙,庙里有两个和尚,老和尚对小和尚说: 从前有座山,山里有座庙,庙里有两个和尚,老和尚对小和尚说: 从前有座山,山里有座庙,庙里有两个和尚,老和尚对小和尚说:。。。。。 如何让递归中止? CTRL+G或设定判断条件。
在过程中,过程又调用了自己,一直调用下去……这种过程叫做递归过程。
递归调用:一个函数直接或间接地调用自身,便构成了函数的递归调用。前者称直接递归调用,后者称间接递归调用。 递归算法设计的基本思想是:对于一个复杂的问题,把问题分解为若干个相对简单前类同的子问题,继续下去直到子问题简单到能够直接求解,也就是说到了递推的出口,这样原问题就有递推得解。
递归算法:是一种直接或者间接地调用自身的算法。在计算机编写程序中,递归算法对解决一大类问题是十分有效的,它往往使算法的描述简洁而且易于理解。 递归算法的特点:递归过程一般通过函数或子过程来实现。
递归算法:在函数或子过程的内部,直接或者间接地调用自己的算法。 递归算法的实质:是把问题转化为规模缩小了的同类问题的子问题。然后递归调用函数(或过程)来表示问题的解。
递归算法解决问题的特点:
(1) 递归就是在过程或函数里调用自身。
(2) 在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口。 (3) 递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。
(4) 在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。所以一般不提倡用递归算法设计程序。
函数的嵌套调用
总结:1)C语言中,函数可以嵌套调用,不可以嵌套定义; 2)函数递归调用指对函数自身的调用,算法描述为 a.if (递归终止条件) return (条件终止时的值) b.else return 递归公式 7.打印二叉树结构
[实现提示]
按凹入表形式横向打印二叉树结构,即二叉树的根在屏幕的最左边,二叉树的左子树在屏幕的下边,二叉树的右子树在屏幕的上边。 例如: 8、打印树结构
[问题描述]
按凹入表形式打印树形结构。 例如:
文档来源为:从网络收集整理.word版本可编辑.欢迎下载支持.
[测试数据]
由学生自己确定。注意测试边界数据,如空树。
[实现提示]
(1)创建静态二叉树,参考代码: //初始化二叉树并赋值
TNODE *T1,*T2,*T3,*T4,*T5,*T6,*T7,*T8,*T9,*T10,*T11,*T12; T1 =(TNODE*)malloc(sizeof(TNODE)); T2 =(TNODE*)malloc(sizeof(TNODE)); T3 =(TNODE*)malloc(sizeof(TNODE)); T4 =(TNODE*)malloc(sizeof(TNODE)); T1->data =1; T2->data =2; T3->data =3; T4->data =4;
T1->lchild =T2; T1->rchild =T3; T2->lchild =T4; T2->rchild =T5; T3->lchild =T6; T3->rchild =T7; T4->lchild =T8; T4->rchild =T9; T6->rchild =NULL; T7->lchild =NULL; (2)创建动态二叉树,参考代码: node *creat(int *a,int i) {
t *x;
if(a[i]==0 || i>8) return NULL; else {
x=(t *)malloc(sizeof(t)); x->data=a[i];
x->lchild=c(a,2*i); x->rchild=c(a,2*i+1); return x; } }
此文档是由网络收集并进行重新排版整理.word可编辑版本!
因篇幅问题不能全部显示,请点此查看更多更全内容