2 线性表 (2)

38
数数数数 --- 数数数 2 2 数数数 数数数 (2) (2)

Upload: doria

Post on 21-Jan-2016

97 views

Category:

Documents


0 download

DESCRIPTION

2 线性表 (2). 教学目标. 1. 了解线性结构的特点 2. 掌握顺序表的定义、查找、插入和删除 3. 掌握链表的定义、查找、插入和删除 4. 能够从时间和空间复杂度的角度比较两种存储结构的不同特点及其适用场合 5. 应用线性结构解决基本问题 6. 了解 STL 中的 vector,list 的基本使用方法. 教学内容. 2.1 线性表的定义 2.2 线性表的顺序表示和实现 2.3 线性表的链式表示和实现 2.4 线性表与 STL (标准模板库). 2.3 线性表的链式表示和实现. 一、单链表 二、结点和单链表的 C++ 语言描述 - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 2  线性表 (2)

数据结构 --- 线性表

2 2 线性表线性表(2)(2)

Page 2: 2  线性表 (2)

数据结构 --- 线性表

1.了解线性结构的特点2.掌握顺序表的定义、查找、插入和删除 3.掌握链表的定义、查找、插入和删除 4.能够从时间和空间复杂度的角度比较两种

存储结构的不同特点及其适用场合5.应用线性结构解决基本问题6.了解 STL中的 vector,list的基本使

用方法

1.了解线性结构的特点2.掌握顺序表的定义、查找、插入和删除 3.掌握链表的定义、查找、插入和删除 4.能够从时间和空间复杂度的角度比较两种

存储结构的不同特点及其适用场合5.应用线性结构解决基本问题6.了解 STL中的 vector,list的基本使

用方法

教学目标

Page 3: 2  线性表 (2)

数据结构 --- 线性表

2.1 线性表的定义

2.2 线性表的顺序表示和实现

2.3 线性表的链式表示和实现

2.4 线性表与 STL(标准模板库)

2.1 线性表的定义

2.2 线性表的顺序表示和实现

2.3 线性表的链式表示和实现

2.4 线性表与 STL(标准模板库)

教学内容

Page 4: 2  线性表 (2)

数据结构 --- 线性表

2.3 线性表的链式表示和实现

Page 5: 2  线性表 (2)

数据结构 --- 线性表

一、单链表二、结点和单链表的 C++ 语言描述三、线性表的操作在单链表中的实现四、其它形式的链表五、编程实现带头结点的单链表

Page 6: 2  线性表 (2)

数据结构 --- 线性表

用一组地址任意的存储单元存放线性表中的数据元素。

一、单链表

以元素 ( 数据元素的存储 )

+ 指针 ( 指示后继元素存储位置 )

= 结点以“结点的序列”表示线性表 称作链表

Page 7: 2  线性表 (2)

数据结构 --- 线性表

以线性表中第一个数据元素 的存储地址作为线性表的地址,称作线性表的头指针。

1a

头结点 a1 a2 … ... an ^

头指针头指针

有时为了操作方便,在第一个结点之前虚加一个“头结点”,以指向头结点的指针为链表的头指针。 ( 这样,头指针永不为空 )

空指针线性表为空表时,头结点的指针域为空

Page 8: 2  线性表 (2)

数据结构 --- 线性表

struct LinkList{ LNode *head; // 头指针 ( 带头结点 ) //LNode *tail , *cur;// 尾指针 , 当前指针 //int length; // 链表长度 bool Init(); // 初始化 void Clear(); // 清除线性表 void Create(int n); // 建立含 n 个结点的单链表 int Locate(ElemType e); // 查找 bool InsertBefore(int i, ElemType e);// 插入元素 bool Delete(int i); // 删除元素 void Traverse(); // 遍历,并输出内容 bool Empty(); // 判断空表};

二、 线性表链式映像的 C++ 语言描述struct LNode { ElemType data; // 数据域 Lnode *next; // 指针域} ;

Page 9: 2  线性表 (2)

数据结构 --- 线性表

ai-1

InsertBefore(i, e) 的实现: 有序对 <ai-1, ai> 改变为 <ai-1, e> 和 <e, ai>

e

aiai-1

因此,在单链表中第 i 个结点之前进行插入的基本操作为 : 找到线性表中第 i-1个结点,然后修改其指向后继的指针。

三、线性表的操作在单链表中的实现

Page 10: 2  线性表 (2)

数据结构 --- 线性表

void InsertBefore(int i, int e) {

// 本算法在链表第 i 个结点之前插入新的元素 e

} // InsertBefore算法的时间复杂度为 : O(n)

p = head;

while (p && i>1) { p=p->next; i--; }//找第 i-1个结点if (p==NULL || i <1) return; // i 大于表长或者小于 1 s = new LNode; // 生成新结点s->data = e;

s->next = p->next; p->next = s; // 插入

Page 11: 2  线性表 (2)

数据结构 --- 线性表

Page 12: 2  线性表 (2)

数据结构 --- 线性表

核心代码:s = new LNode; // 生成新结点s->data = e;

s->next = p->next; p->next = s; // 插入

e

ai-1 aiai-1

s

p

Page 13: 2  线性表 (2)

数据结构 --- 线性表

在单链表中删除第 i 个结点的基本操作为 :找到线性表中第 i-1个结点,修改其指向后继的指针。核心代码如下:

ai-1 ai ai+1ai-1

q = p->next; p->next = q->next;

e = q->data; delete q;p q

Delete (i, &e) 的实现 :

有序对 <ai-1, ai> 和 <ai, ai+1> 改变为 <ai-1,

ai+1>

Page 14: 2  线性表 (2)

数据结构 --- 线性表

void Delete (int i, int &e) {

// 删除单链表中第 i 个结点

} // Delete算法的时间复杂度为 : O(n)

p = head; j = 0;while (p->next && j < i-1) { p = p->next; ++j; } // 寻找第 i 个结点,并令 p 指向其前趋if (!(p->next) || j > i-1) return; // 删除位置不合理

q = p->next; p->next = q->next; // 删除并释放结点e = q->data; delete q;

Page 15: 2  线性表 (2)

数据结构 --- 线性表

Page 16: 2  线性表 (2)

数据结构 --- 线性表

Clear( ) 的实现 :

void Clear( ) {

// 将单链表重新置为一个空表 while (head->next) {

p=head->next;

head->next=p->next;

}

} // Clear

delete p;

算法时间复杂度:O(n)

Page 17: 2  线性表 (2)

数据结构 --- 线性表

如何从线性表得到单链表?

链表是一个动态的结构,它不需要预先分配空间,因此生成链表的过程是一个结点“逐个插入”的过程。

Page 18: 2  线性表 (2)

数据结构 --- 线性表

例如:逆位序输入 n 个数据元素的值, 建立带头结点的单链表。操作步骤:一、建立一个“空表”;二、输入数据元素 an , 建立结点并插入;三、输入数据元素 an-1 , 建立结点并插入;

an

an

an-1

四、依次类推,直至输入 a1 为止。

Page 19: 2  线性表 (2)

数据结构 --- 线性表

void Create(int n) { // 逆序输入 n 个数据元素,建立带头结点的单链表

} // Create算法的时间复杂度为 : O(n)

head = new LNode;head->next =NULL; // 先建立一个带头结点的单链表

for (i = n; i > 0; i--) { p = new LNode; cin>>p->data; // 输入元素值 p->next = head->next; head->next = p; // 插入}

Page 20: 2  线性表 (2)

数据结构 --- 线性表

回顾 前面例 2.1, 2.2, 2.3 三个例子的算法,看一下当线性表分别以顺序表和链表实现时,它们的时间复杂度为多少?

2-1 两集合用线性表 LA 和 LB 表示,求新集合 A = A B∪ 。 void union(List &La, List Lb)

2-2 非纯集合 B 净化为纯集合 A 。void purge(List &La, List Lb)

2-3 void MergeList(List La, List Lb, List &Lc)

Page 21: 2  线性表 (2)

数据结构 --- 线性表

void union(List &La, List Lb) { La_len = ListLength(La); Lb_len =ListLength(Lb); for (i = 1; i <= Lb_len; i++) { GetElem(Lb, i, e); if (!Locate(La, e) InsertBefore (La, ++La_len, e); }//for} // union

控制结构:基本操作:

for 循环GetElem, Locate 和 InsertBefore

当以顺序映像实现抽象数据类型线性表时为 : O( LenA×LenB )

当以链式映像实现抽象数据类型线性表时为 : O( LenA×LenB )

例 2-1

算法时间复杂度算法时间复杂度

Page 22: 2  线性表 (2)

数据结构 --- 线性表

void purge(List &La, List Lb) { // Lb 元素有序 InitList(LA); La_len = ListLength(La); Lb_len =ListLength(Lb); for (i = 1; i <= Lb_len; i++) { GetElem(Lb, i, e); if (ListEmpty(La) || en!=e) { InsertBefore(La, ++La_len, e); en = e; } }//for} // purge

控制结构:基本操作:

for 循环GetElem 和 InsertBefore

当以顺序映像实现抽象数据类型线性表时为 :O(LenB)当以链式映像实现抽象数据类型线性表时为 :O(LenB2)

例 2-2

算法时间复杂度算法时间复杂度

Page 23: 2  线性表 (2)

数据结构 --- 线性表

void MergeList(List La, List Lb, List &Lc) { InitList(Lc); i = j = 1; k = 0; La_len = ListLength(La); Lb_len = ListLength(Lb); while ((i <= La_len) && (j <= Lb_len)) { GetElem(La, i, ai); GetElem(Lb, j, bj); if (ai <= bj) { InsertBefore (Lc, ++k, ai); ++i; } else { InsertBefore (Lc, ++k, bj); ++j; } } … …

控制结构:基本操作:

三个并列的 while 循环GetElem, InsertBefore

当以顺序映像实现抽象数据类型线性表时为 : O( LenA+LenB)当以链式映像实现抽象数据类型线性表时为 : O( (LenA+LenB)2 )

例 2-3

算法时间复杂度算法时间复杂度

Page 24: 2  线性表 (2)

数据结构 --- 线性表

用上述定义的单链表实现线性表的操作时,存在的问题:

改进链表的设置:

1.单链表的表长是一个隐含的值;2.在单链表的最后一个元素之后插入元素时,

需遍历整个链表;3.在链表中,元素的“位序”概念淡化,结点

的“位置”概念加强。

1.增加“表长”、“表尾指针” 和 “当前位置的指针” 三个数据域;

2.将基本操作中的“位序 i ” 改变为“指针 p ” 。

Page 25: 2  线性表 (2)

数据结构 --- 线性表

void InsAfter(int e ) { // 将数据元素 e 插入在链表当前指针所指结点之后

} // InsAfter

s=new LNode; s->data=e; s->next = curr->next; curr->next = s; if (tail = curr) tail = s; curr = s; // 注意

Page 26: 2  线性表 (2)

数据结构 --- 线性表

void DelAfter(int & e ) {

// 删除当前指针所指结点之后的结点 , 操作失败时 e=NULL

} //DelAfter

q = curr->next;curr->next = q->next;if (tail = q) tail = curr;e=q->data; delete q;

Page 27: 2  线性表 (2)

数据结构 --- 线性表

void MergeList(LinkList &Lc, LinkList &La,LinkList &Lb) { Lc.Init(); // 初始化 La.curr = La.head->next; Lb.curr = Lb.head->next; // 当前指针指向头结点 while (! La.Empty( ) || !Lb.Empty( )) { // La或 Lb 非空

a=La.GetCurElem(); b=Lb.GetCurElem(); If(a<b)

La.DelAfter( La, e ) ; // 当前指针后移else

Lb.DelAfter( Lb, e ) ;Lc.InsertAfter(e);

} La.destory(); Lb. destory(); // 销毁链表 La 和 Lb} // MergeList

例:归并有序表 La 和 Lb , 生成新有序表 Lc 之后销毁 La 和 Lb

O( LenA+LenB)算法时间复杂度 :算法时间复杂度 :

Page 28: 2  线性表 (2)

数据结构 --- 线性表

1. 双向链表四、其它形式的链表

struct DuLNode {

ElemType data; // 数据域DuLNode *prior; // 指向前驱的指针DuLNode *next; // 指向后继的指针

};操作特点:“查询”和单链表相同。“插入”和“删除”需要同时修改两个方向上的指针。

Page 29: 2  线性表 (2)

数据结构 --- 线性表

最后一个结点的指针域的指针又指回第一个结点的链表

a1 a2 … ... an

2. 循环链表

和单链表的差别仅在于,判别链表中最后一个结点的条件不再是“后继是否为空”,而是“后继是否为头结点”。

Page 30: 2  线性表 (2)

数据结构 --- 线性表

ai-1 ai

e

s->next = p->next; p->next = s;

s->next->prior = s; s->prior = p;

p

s

ai-1 ai

双向链表插入

Page 31: 2  线性表 (2)

数据结构 --- 线性表

ai-1

双向链表删除

ai ai+1

p->next = p->next->next;

p->next->prior = p;

p

ai-1

为简单起见,这里没有释放被删除节点的空间

Page 32: 2  线性表 (2)

数据结构 --- 线性表

五、编程实现带头结点的单链表struct LinkList{

LNode *head; // 头结点int size; // 链表长度

LinkList(); // 构造函数 , 初始化单链表void create(int a[],int n); // 根据数组 A 中的 n 个元素,建立单链表void clear(); // 释放除头结点外的其他结点bool insert(int i, int e); // 在单链表的第 i个位置前插入 ebool erase(int i); // 在单链表中删除第 i个元素 ,并用 e返回其值void traverse(); // 遍历链表,并输出依序内容LNode* locate(int e); // 查找首个值等于 e的位置 ( 指针 ),否则返回 NULL bool getElem(int i, int &e) // 在单链表中取得第 i个元素void reverse(); // 逆置链表

};

struct LNode{int data;LNode *next;

};

Page 33: 2  线性表 (2)

数据结构 --- 线性表

LinkList() { // 构造函数 , 初始化单链表head=new LNode; // 生成头结点head->next=NULL;size=0;

}

void clear(){ // 释放除头结点外的其他结点LNode *p;while(head->next!=NULL){

p=head->next;head->next=p->next;delete p;

}size=0;

}

Page 34: 2  线性表 (2)

数据结构 --- 线性表

bool erase(int i) { // 在单链表中删除第 i个元素if(i<1 || i>size) return false;

LNode *p=head;while(p->next!=NULL && i>1){

p=p->next;i--;

}

LNode *q=p->next;p->next=q->next;delete q;size--;return true;

}

Page 35: 2  线性表 (2)

数据结构 --- 线性表

bool getElem(int i, int &e){ // 在单链表中取得第 i个元素

if(i<1 || i>size) return false;

LNode *p=head->next;//指向第一个节点

while(p!=NULL && i>1){i--;p=p->next;

}

e=p->data;return true;

}

Page 36: 2  线性表 (2)

数据结构 --- 线性表

int main(){LinkList lst;int a[6]={11,12,13,14,15,16};lst.create(a,6); lst.traverse();lst.erase(3); lst.traverse();lst.insert(6,99); lst.traverse();lst.reverse(); lst.traverse();int e; lst.getElem(1,e); cout<<e<<endl;

LNode *p=lst.locate(13);if(p!=NULL) cout<<p->explain<<endl;else cout<<"No found!"<<endl;

lst.clear();lst.create(a,3); lst.traverse();return 0;

}

单链表使用举例单链表使用举例

Page 37: 2  线性表 (2)

数据结构 --- 线性表

小结1. 熟练掌握链表中的头结点、头指针和首元结

点的区别。2. 熟练掌握链表的查找、插入和删除算法3. 能够从时间和空间复杂度的角度比较两种存

储结构的不同特点及其适用场合

1. 熟练掌握链表中的头结点、头指针和首元结点的区别。

2. 熟练掌握链表的查找、插入和删除算法3. 能够从时间和空间复杂度的角度比较两种存

储结构的不同特点及其适用场合

Page 38: 2  线性表 (2)

数据结构 --- 线性表

课后建议:

HLoj 8801-8808,8812-8824 HDoj 1002, 1715,1753 (大整数加

法)