slide1 n.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
2 线性表 (2) PowerPoint Presentation
Download Presentation
2 线性表 (2)

Loading in 2 Seconds...

play fullscreen
1 / 38

2 线性表 (2) - PowerPoint PPT Presentation


  • 180 Views
  • Uploaded on

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

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about '2 线性表 (2)' - doria


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
slide2

教学目标

1.了解线性结构的特点

2.掌握顺序表的定义、查找、插入和删除

3.掌握链表的定义、查找、插入和删除

4.能够从时间和空间复杂度的角度比较两种存储结构的不同特点及其适用场合

5.应用线性结构解决基本问题

6.了解STL中的vector,list的基本使用方法

slide3

教学内容

2.1 线性表的定义

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

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

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

slide5

一、单链表

二、结点和单链表的 C++ 语言描述

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

四、其它形式的链表

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

slide6

一、单链表

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

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

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

= 结点

以“结点的序列”表示线性表

 称作链表

slide7

a1 a2 … ... an ^

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

头指针

线性表为空表时,

头结点的指针域为空

头指针

空指针

头结点

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

slide8

二、线性表链式映像的 C++ 语言描述

struct LNode {

ElemType data; // 数据域

Lnode*next; // 指针域

} ;

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(); // 判断空表

};

slide9

ai-1

ai

ai-1

e

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

InsertBefore(i, e)的实现:

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

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

slide10

void InsertBefore(int i, int e) {

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

} // InsertBefore

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; // 插入

O(n)

算法的时间复杂度为:

slide12

ai-1

ai

ai-1

e

核心代码:

s = new LNode; // 生成新结点

s->data = e;

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

p

s

slide13

ai-1

ai-1

ai

ai+1

Delete (i, &e)的实现:

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

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

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

e = q->data; delete q;

p

q

slide14

void Delete (int i, int &e) {

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

} // Delete

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;

算法的时间复杂度为:

O(n)

slide16

Clear( ) 的实现:

void Clear( ) {

// 将单链表重新置为一个空表

while (head->next) {

p=head->next;

head->next=p->next;

}

} // Clear

delete p;

算法时间复杂度:

O(n)

slide17

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

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

slide18

例如:逆位序输入 n 个数据元素的值,

建立带头结点的单链表。

操作步骤:

一、建立一个“空表”;

二、输入数据元素an,

建立结点并插入;

an

三、输入数据元素an-1,

建立结点并插入;

an

an-1

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

slide19

void Create(int n) {

// 逆序输入 n 个数据元素,建立带头结点的单链表

} // Create

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; // 插入

}

O(n)

算法的时间复杂度为:

slide20

回顾 前面例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)

slide21

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

例2-1

算法时间复杂度

控制结构:

基本操作:

for 循环

GetElem, Locate 和 InsertBefore

当以顺序映像实现抽象数据类型线性表时为:

O( LenA×LenB )

当以链式映像实现抽象数据类型线性表时为:

O( LenA×LenB )

slide22

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

例2-2

算法时间复杂度

控制结构:

基本操作:

for 循环

GetElem 和 InsertBefore

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

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

slide23

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; }

} … …

例2-3

算法时间复杂度

控制结构:

基本操作:

三个并列的while循环

GetElem, InsertBefore

当以链式映像实现抽象数据类型线性表时为:

O( (LenA+LenB)2 )

当以顺序映像实现抽象数据类型线性表时为:

O( LenA+LenB)

slide24

用上述定义的单链表实现线性表的操作时,

存在的问题:

单链表的表长是一个隐含的值;

在单链表的最后一个元素之后插入元素时,需遍历整个链表;

在链表中,元素的“位序”概念淡化,结点的“位置”概念加强。

改进链表的设置:

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

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

slide25

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; // 注意

slide26

void DelAfter(int & e ) {

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

} //DelAfter

q = curr->next;

curr->next = q->next;

if (tail = q) tail = curr;

e=q->data; delete q;

slide27

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

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

算法时间复杂度:

O( LenA+LenB)

slide28

四、其它形式的链表

1. 双向链表

struct DuLNode {

ElemType data;// 数据域

DuLNode *prior;// 指向前驱的指针

DuLNode *next;// 指向后继的指针

};

操作特点:

“查询”和单链表相同。

“插入”和“删除”需要同时修改两个方向上的指针。

slide29

2. 循环链表

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

a1 a2 … ... an

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

slide30

ai-1

ai

e

ai-1

ai

p

双向链表插入

s

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

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

slide31

ai-1

ai

ai+1

ai-1

p

双向链表删除

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

p->next->prior = p;

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

slide32

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

struct LinkList{

LNode *head; // 头结点

int size; // 链表长度

LinkList(); // 构造函数, 初始化单链表

void create(int a[],int n); // 根据数组A 中的n 个元素,建立单链表

void clear(); // 释放除头结点外的其他结点

bool insert(int i, int e);// 在单链表的第i个位置前插入e

bool 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;

};

slide33

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;

}

slide34

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;

}

slide35

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;

}

slide36

单链表使用举例

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;

}

slide37

小结

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

熟练掌握链表的查找、插入和删除算法

能够从时间和空间复杂度的角度比较两种存储结构的不同特点及其适用场合

slide38

课后建议:

HLoj 8801-8808,8812-8824

HDoj 1002, 1715,1753(大整数加法)