数据结构第二章3
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
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结点空间
② 对于指向结构变量node首地址的指针,可说明为: ② 对于指向结构类型的指针变量,可被说明为:
struct node *p, *q;*p, *q; node //或用 struct test *p ,, *q; *p *q;
×
×
×
×
//注:上面已经定义了node为用户自定义的test类型。 //注:上面已经定义了node为用户自定义的test类型。
//插入非空表的剩余段 //释放Lb的头结点 ?是条件运算符(为真则取第1项,见P10条件赋值)
21
注:Lc用的是La的头指针
思
考:
1、不用Lc,直接把La表插到Lb表中;或者 把Lb表插到La表中,怎么修改?
2、重复的数据元素不需要插入,怎么修改?
22
例2:一元多项式的计算 (参见教材P39 – 43) 讨论:
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 。 请注意链表的特点,仅改变指针 便可实现数据的移动,即 “数据不动,指针动”
18
Pa、Pb用于搜索La和Lb, Pc指向新链表当前结点。 归并过程示意如下:
La
Pa
3
Pa 5 百度文库b
Pa 8 Pb
Pa
11 ^ Pb
Lc=La Lb
Pb
2
6
8
9
11 ^
19
一个带头结点的线性链表类型定义如下: (用类C语言,见P37):
typedef struct Lnode{ Elemtype data; struct Lnode *next; }*link; typedef struct { link head, tail; int len; } linklist;
3
第2章 线性表
2.1 线性表的逻辑结构 2.2 线性表的顺序表示和实现 2.3 线性表的链式表示和实现 2.4 应用举例
4
2.3 线性表的链式表示和实现
2.3.1 链表的表示
2.3.2 链表的实现
2.3.3 链表的运算效率分析
5
2.3.2 链表的实现
(1) 单链表的建立和输出 (2) 单链表的修改 (3) 单链表的插入
新手特别容易忘记!!
9
void display()
/*字母链表的输出*/
{p=head; sum=0; while (p) //当指针不空时循环(仅限于无头结点的情况) {printf("%c",p->data); p=p->next; //让指针不断“顺藤摸瓜” } sum++; }
讨论:要统计链表中数据元素的个数,该如何改写?
{ 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); } //MergeList_L
思考: 省略free(q)语句行不行?
13
2.3.3 链表的运算效率分析
时间效率分析 (1) 查找
因线性链表只能顺序存取,即在查找时要从头指针找起,
查找的时间复杂度为 O(n)。 (2) 插入和删除
因线性链表不需要移动元素,只要修改指针,一般情况下时
间复杂度为 O(1)。 但是,如果要在单链表中进行前插或删除操作,因为要
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个元素,只能从头指针开始逐一 查询(顺藤摸瓜),无法随机存取 。
… 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中。
^
^ 10 6 ^
26
Pc
c
11 14
运算效率分析:
系数相加 0 加法次数 min(m, n) 其中 m和n分别表示表A和表B的结点数。 (2) 指数比较 极端情况是表A和表B 没有一项指数相 同,比较次数最多为m+n-1 (3) 新结点的创建 极端情况是产生m + n 个新结点 合计时间复杂度为 O(m+n) (1)
//结点类型
结点的 结构
//链表类型 //分别指向链表头尾结点
//链表中元素个数(长度)
表结构
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中
从头查找前驱结点,所耗时间复杂度将是 O(n)。
14
练习:
在n个结点的单链表中要删除已知结点*P,需找到它 的
前驱结点的地址/指针
,其时间复杂度为
O(n) 。
空间效率分析 链表中每个结点都要增加一个指针空间,相当于总共增 加了n 个整型变量,空间复杂度为 O(n)。
15
2.4 应用举例
算法要求:
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
Q1:第一行的Lnode 与最后一行的Lnode是不是一回事? A1:不是。前者Lnode是结构名,后者Lnode是对整个 struct类型的一种“缩写”,是一种“新定义名”,它只 是对现有类型名的补充,而不是取代。
2
Typedef struct Lnode { { Typedef struct student ElemType char name;data; struct Lnode *next; int age; }Lnode, *LinkList; }student,*pointer; Q2: 那为何两处要同名(Lnode和Lnode)?太不严谨了吧? A2:同名是为了表述起来方便。例如,若结构名为student, 其新定义名缩写也最好写成student,因为描述的对象相同, 方便阅读和理解。 Q3:结构体中间的那个struct Lnode是何意? A3:在“缩写” Lnode还没出现之前,只能用原始的 struct Lnode来进行变量说明。此处说明了指针分量的 数据类型是struct Lnode。
错: 补充结构数据类型的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结构类型的替代,也是数据类型*/
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 ;} //单链表尾结点的指针域要置空!
1. 一元多项式的数学通式? 2. 用抽象数据类型如何描述它的定义?
3. 用C语言如何描述它的定义?
4. 如何编程实现两个一元多项式相加?
23
1. 一元多项式的数学通式?
A( x ) am1 x
em1
a m 2 x
em 2
...a0 x
e0
机内存储方式? 一般用顺序存储——只存系数项(但零系数项也不能遗漏)
1
教材问题讨论:
教材P28对于线性表的单链 表存储结构描述: Typedef struct Lnode { ElemType data; struct Lnode *next; }Lnode, *LinkList; 请注意:Typedef不可能创造 任何新的数据类型,而仅仅是 在原有的数据类型中命名一个 新名字,其目的是使你的程序 更易阅读和移植。
11
(2) 单链表的修改(或读取)
(3) 单链表的插入
在链表中插入一个元素X 的示意图如下: p a p
b
插 入 X
a
b
p->next
s
X
s->next
链表插入的核心语句: Step 1:s->next=p->next; Step 2:p->next=s ; 思考:Step1和2能互换么?
X 结点的生成方式:
(4) 单链表的删除
6
(1) 单链表的建立和输出 例:用单链表结构来存放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个指针变量 // 数据元素的个数
重点是链表
例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)
… 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