数据结构第二章3
1
教材问题讨论:
教材P28对于线性表的单链 表存储结构描述: Typedef struct Lnode { ElemType data; struct Lnode *next; }Lnode, *LinkList; 请注意:Typedef不可能创造 任何新的数据类型,而仅仅是 在原有的数据类型中命名一个 新名字,其目的是使你的程序 更易阅读和移植。
… am-1 em-1 ^
24
0.0 -1
头结点
2. 如何编程实现两个一元多项式相加?
例: a 3x14 2 x8 1
链表a:
a
3
14
14 10
2
8
6
1
0
^
b 8 x 3x 10 x
链表b:
b
8 14 -3 10
10 6
^
运算规则:两多项式中指数相同的项对应系数相加,
若和不为0,则构成多项式c(=a+b)中的一项;a和b中所 有指数不相同的项均应拷贝到c中。
//插入非空表的剩余段 //释放Lb的头结点 ?是条件运算符(为真则取第1项,见P10条件赋值)
21
注:Lc用的是La的头指针
思
考:
1、不用Lc,直接把La表插到Lb表中;或者 把Lb表插到La表中,怎么修改?
2、重复的数据元素不需要插入,怎么修改?
22
例2:一元多项式的计算 (参见教材P39 – 43) 讨论:
10
Status含 义见P10 思路:要修改第i个数据元素,必须从头指针起一直找到该 结点的指针p,然后才能执行p->data=new_value 。 修改第i个数据元素的操作函数可写为:
Status GetElem_L(LinkList L, int i, ElemType &e) {p=L->next; j=1; //带头结点的链表 while(p&&j<i){p=p->next; ++j;} if( !p || j>i ) return ERROR; p->data =e; //若是读取则为:e=p->data; return OK;}// GetElem_L 缺点:想寻找单链表中第i个元素,只能从头指针开始逐一 查询(顺藤摸瓜),无法随机存取 。
… aP2000(x)= 1+ 3x1000 + a5x2000am-1 a1 a2 0 m-2
但当多项式的次数很高且零系数项很多时,更适于用链表存储。
通常设计两个数据域(系数项和指数项)和一个指针域 expon coef
link
^
链式存储
或
am-1 em-1
am-2 em-2
a0 e0
… a0 e0
{ if(pa->data<=pb->data) {pc->next=pa; pc=pa; pa=pa->next;} else {pc->next=pb; pc=pb; pb=pb->next} } pc->next = pa ? pa: pb ; free(Lb); } //Merg立和输出 例:用单链表结构来存放26个英文字母组成的线 性表(a,b,c,…,z),请写出C语言程序。
实现思路:先开辟头指针,然后陆续为每个结点开辟存储 空间并及时赋值,后继结点的地址要提前送给前面的指针。
先挖“坑”,后种“萝 卜”!
7
将全局变量及函数提前说明: #include<stdio.h> #include<stdlib.h> typedef struct node{ char data; struct node *next; }node; node *p,*q,*head; int n ; //一般需要3个指针变量 // 数据元素的个数
s=(node*)malloc(m); s->data=X ; s->next= ?
12
(4) 单链表的删除
在链表中删除某元素b的示意图如下: p a × b × c
p->next
q
(p->next) -> next
删除动作的核心语句(要借助辅助指针变量q):
q = p->next; //首先保存b的指针,靠它才能找到c; p->next=q->next; //将a、c两结点相连,淘汰b结点; free(q) ; //彻底释放b结点空间
重点是链表
例1:两个链表的归并(教材P31例)
已知:线性表 A和B,分别由单链表 La和Lb 存储,其中 数据元素按值非递减有序排列(即已经有序);
要求:将A和B归并为一个新的线性表C , C的数据元素仍 按值非递减排列。设线性表C由单链表 Lc 存储。
假设:A=(3,5,8,11),B=(2,6,8,9,11) 预测:合并后的C =(2, 3, 5, 6, 8, 8, 9, 11,11)
25
实现思路:
依次比较Pa和Pb所指结点中的指数项,依 Pa->expon =、<或>Pb->expon等情况,再决定 是将两系数域的数值相加(并判其和是否为0), 还是将较高指数项的结点插入到新表c中。 Pa
a 3 14 Pb + 8 14 b 2 8 -3 10 -3 10
1 0
10 6 2 8 1 0
int m=sizeof(node); /*结构类型定义好之后,每个node类型的长 度就固定了,m求一次即可*/
8
void build( ) //字母链表的生成。要一个个慢慢链入 { int i; head=(node*)malloc(m); //m=sizeof(node) 前面已求出 p=head; for( i=1; i<26; i++) //因尾结点要特殊处理,故i≠26 { p->data=i+‘a’-1; // 第一个结点值为字符a p->next=(node*)malloc(m); //为后继结点“挖坑”! p=p->next;} //让指针变量P指向后一个结点 p->data=i+‘a’-1; //最后一个元素要单独处理 p->next=NULL ;} //单链表尾结点的指针域要置空!
新手特别容易忘记!!
9
void display()
/*字母链表的输出*/
{p=head; sum=0; while (p) //当指针不空时循环(仅限于无头结点的情况) {printf("%c",p->data); p=p->next; //让指针不断“顺藤摸瓜” } sum++; }
讨论:要统计链表中数据元素的个数,该如何改写?
② 对于指向结构变量node首地址的指针,可说明为: ② 对于指向结构类型的指针变量,可被说明为:
struct node *p, *q;*p, *q; node //或用 struct test *p ,, *q; *p *q;
×
×
×
×
//注:上面已经定义了node为用户自定义的test类型。 //注:上面已经定义了node为用户自定义的test类型。
16
链表示意图:
La 头结点
3
Lb
5 6
8 8
11 9
/\
2
11
/\
Lc
2 8
8
3
9
5
11
6
11
/\
17
算法设计:
算法主要包括搜索、比较、插入三个操作: 搜索:需要设立三个指针来指向La 、Lb和Lc链表; 比较:比较La和Lb表中结点数据的大小; 插入:将La和Lb表中数据较小的结点插入新链表Lc 。 请注意链表的特点,仅改变指针 便可实现数据的移动,即 “数据不动,指针动”
1. 一元多项式的数学通式? 2. 用抽象数据类型如何描述它的定义?
3. 用C语言如何描述它的定义?
4. 如何编程实现两个一元多项式相加?
23
1. 一元多项式的数学通式?
A( x ) am1 x
em1
a m 2 x
em 2
...a0 x
e0
机内存储方式? 一般用顺序存储——只存系数项(但零系数项也不能遗漏)
从头查找前驱结点,所耗时间复杂度将是 O(n)。
14
练习:
在n个结点的单链表中要删除已知结点*P,需找到它 的
前驱结点的地址/指针
,其时间复杂度为
O(n) 。
空间效率分析 链表中每个结点都要增加一个指针空间,相当于总共增 加了n 个整型变量,空间复杂度为 O(n)。
15
2.4 应用举例
算法要求:
//结点类型
结点的 结构
//链表类型 //分别指向链表头尾结点
//链表中元素个数(长度)
表结构
20
见P31算法2.12 算法实现: Void MergeList_L(LinkList &La,LinkList &Lb,LinkList &Lc) { //已知单链线性表La和Lb的元素按值非递减排列。归并为Lc 后也按值非递减排列。 pa=La->next; pb=Lb->next; Lc=pc=La; //有头结点 while(pa&&pb) //将pa 、pb结点按大小依次插入Lc中
错: 补充结构数据类型的C表示法 正:补充结构数据类型的C表示法 ① 类型定义和变量说明可以合写为: typedef struct test // test是自定义结构类型名称 // test是自定义结构类型名称 test
{ char data; //定义数据域的变量名及其类型 struct test *next; //定义指针域的变量名及其类型 }node,*head; /*node是 test 结构类型的变量, }node,*pointer; //node是test结构类型的类型替代, *head是test结构类型的头指针*/ *pointer是指针型的test结构类型的替代,也是数据类型*/