关于单链表掌握以下知识: 为什么需要单链表...

23
22/6/8 1 关关关关关关关关关关关关关关关关关关关 关关关关关关关关关 关关关关关关关关关关 关关关关关关关关关关关关 : 关关 关关关 关关关 关关关 关关关 、、、、、 关关关关关关

Upload: wilma-beard

Post on 30-Dec-2015

156 views

Category:

Documents


8 download

DESCRIPTION

链表及其应用. 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、. 9.10.2 链表概念的引入. 处理一个班的学生,一般使用结构体数组, 需预留足够大的数组空间. 带来两个问题 : 1. 多余的元素占用的空间浪费,可动态申请数组空间解决, 2. 但有时系统不能满足过大的连续存储空间的要求。. 解决: 使用链表结构 , 一次只需要一个结构体的连续空间, 就像现实生活中的 链子 ,由若干 环节 组成, - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

23/4/19 1

• 关于单链表掌握以下知识:– 为什么需要单链表– 如何通过指针和结构体的定义实现单链表结

构– 单链表常用操作的实现方法 : 建立、遍历、

插入、删除、查找、

链表及其应用

Page 2: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

处理一个班的学生,一般使用结构体数组, 需预留足够大的数组空间

带来两个问题: 1. 多余的元素占用的空间浪费,可动态申请数组空间解决, 2. 但有时系统不能满足过大的连续存储空间的要求。

解决:使用链表结构 , 一次只需要一个结构体的连续空间, 就像现实生活中的链子,由若干环节组成, 在 C++ 语言中用结构体实现链表环节,

对于一个班,有多少个学生,就动态地生成多少个结构体, 如何来连接它们呢?用指针 将它们连接成一个链表结构, 画图表示,单向链表 --->

9.10.2 链表概念的引入

Page 3: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

student stu[10]= { {10101, "Li Nin",18,'M',88}, {...}, {...}, ...... }; student *p;

p = stu; for(i=0; i<10; i++, p++) cout << p->num << p->name << p->score << endl ;

2. 指向结构体数组元素的指针

10101“Li Lin”

18‘M’

10102“Zhang Fun”

19‘M’

10104“Wang Min”

20‘F’

stu[1]

100

99

88

stu[0]

stu[2]

p, stu

p+1

p+2

…...

Page 4: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

(环节)(节点) 结构体

…... …... …... …...…...head

0

为形成链表,结构体要增加一个成员,即指针,指向下一个结构体节点。如上图是一个单向链表。

结尾标志NULL空指针

Page 5: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

优点:解决了上述两个问题

1. 需要多少结构体,就动态申请多少个结构体空间, 比数组实现节约空间。

2. 每个结点未必连续存放,通过指针将各个结点连接起来, 这样对大片连续存储区的要求降低。

3. 另外,对链表的操作如插入、删除等, 也变得比较简单了。(不需要挪动数组元素空间了)

struct student // 定义节点结构体类型 { int num; /* 学号 */ char name[20]; /* 姓名 */ int age; /* 年龄 */ char sex; /* 性别 */ int score; /* 成绩 */ student *next; };

Page 6: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

① 创建链表(有序链表、无序链表)② 遍历链表(依次访问链表的每一个结点) 查找结点,释放链表各节点的空间③ 删除结点④ 插入结点

9.10.3 链表的常用算法

链表上的数据结点为:struct node { int data;

node *next;};

Page 7: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

[ 例 ] 动态申请,建立链表,输出各结点数据,最后循环依次释放各结点空间。#include <iostream.h>struct node{ int data; node *next; };void main( ){ node *head, *p1, *p2; head = new node; p1 = new node; p2 = new node; head->data = 1000; head->next = p1; p1->data = 1001; p1->next = p2; p2->data = 1002; p2->next = NULL; while(head!=NULL) { cout << head->data << endl;

p1=head; head = head->next; delete p1; }}

head

p1 p2

NULL

输出: 1000 1001 1002

Page 8: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

1. 创建无序链表:循环输入数据,若数值不为- 1 ,新建一结点并连接到链表尾部。

head 指向链表首结点p2 指向建立过程中的链表尾结点p1 指向新开辟的结点

head

p2 p1

程序

加入结点时 , 分三种情况① 首结点的建立② 中间结点的加入③ 尾结点的处理

[ 例 ] 链表常用算法介绍

Page 9: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

node *Create( ) // 返回值 : 链表的首指针{ node *p1, *p2, *head; int a; head = NULL; cout <<" 正在创建一条无序链表 ...\n"; cout <<" 请输入一个整数 , 以 - 1 结束 : "; cin >> a; while( a != -1 ) // 循环输入数据,建立链表 { p1 = new node; p1->data = a;

if(head==0 ) // ① 建立首结点 { head=p1; p2=p1; }

else // ② 处理中间结点 { p2->next=p1; p2=p1; } cout <<" 请输入一个整数 , 以 - 1 结束 : ";

cin >> a; } if(head != 0) p2->next=0;//③ 处理尾结点 , 可能第 1 次就输入 -1 return(head); // 返回创建链表的首指针}

解释

Page 10: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

23/4/19 10

• 无论是哪一种建立方法 , 共同的操作是 :– 最主要的是指向第一个结点的头指针 , 有时需要设一个指向

最后一个结点的尾指针以方便操作 , 注意头指针该如何修改– 逐个申请结点空间– 对每个结点的数据域赋值– 每个结点的指针域如何赋值取决于建立方法– 将新结点加入到链表中 , 即修改链表中某一个结点的指针域

• 后插法的关键是 :– 新结点的指针域值一定为空 , 因为它是新的最后一个结点– 头指针只要修改一次 , 即在空链状态下插入第一个点时修改– 保证 tail 指针始终指在当前链表的最后一个结点处 , 即新结

点 p 插入链表之后 , 要做赋值 : tail=p; 以后通过 tail->next=p; 就可以实现在尾部插入新结点

单链表的建立:

Page 11: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

23/4/19 11

单链表的遍历:从头指针开始,顺着各个指针,依次访问链表中的各个结点 • 关键是确定遍历的条件 ,即何时循环 ,何时终止• 根据单链表的最后一个指针域为空 ,可以让一个工作指针指向当前

结点 ,不断后移 ,如果该指针为空 , 则链表遍历结束• 关键代码 :• for(p = head; p; p = p->next) • printNode(p->data);

单链表的查找:在单链表中搜索是否存在这样的结点,其数据域或数据域的某一项等于给定待搜索的值。 • 查找返回 : 若存在则返回指向该结点的指针 , 否则返回空指针• 关键代码 :• while(p&&!equal(p->data,data,1)) • p=p->next;

该函数是根据结点的数据域类型定义的一个输出函数 动

态 演示 过程

动态 演示 过

该函数是根据结点的数据域类型定义的一个判断元素值是否相等的函数

2. 遍历链表

Page 12: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

2. 遍历链表(输出链表上各个结点的值)

void Print( const node *head ) // 不允许通过 head 修改{ // 链表中结点的值

const node *p;p=head;cout << " 输出链表中各结点数据: ";while( p!=NULL ){

cout <<setw(4) << (p->data) ;p=p->next;

}cout << endl;

}

Page 13: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

2. 遍历链表(续)(查找结点 )

node * Search( const node *head, int x ) // 不允许通过 head 修改链表中结点的值{ const node *p; // 不允许通过 p 修改它指向的结点的值 p=head; while( p!=NULL ) { if(p - >data == x) return p; // 若找到,则返回该结点指针

p = p - >next; } return NULL; // 若找不到,则返回空指针}

Page 14: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

3. 删除一个结点 删除链表上具有指定值的第一个结点

• 首先要用遍历的思想查找该值的结点是否在单链表存在• 一定要有一个指向待删除结点前趋结点位置的指针 (p2)

以便删除

删除结点时 , 分三种情况①原始链表为空链表 // 无结点可删②待删除的结点是原始链表的首结点 // head 指针变化③ 删除其他结点

删除 演示 过

Page 15: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

// 函数功能:删除第 1 个值为 num 的结点,返回新链表的首指针。node *Delete_one_node( node *head, int num ){ node *p1, *p2; if( head == NULL) // 链表为空,处理情况① { cout << " 链表为空,无结点可删 !\n"; return(NULL); }

p1 = head; while(p1 - >data != num && p1 - >next != NULL ) { // 循环查找待删除结点

p2 = p1; p1 = p1 - >next; // p2 指向的结点在 p1 指向的结点之

前 }

Page 16: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

if(p1 - >data == num) // 找到待删除结点,由 p1 指向 {

if(p1==head) // 若找到的结点是首结点,处理情况② head=p1 - >next;

else // 找到的结点不是首结点 , 处理情况③ p2 - >next = p1 - >next;

delete p1;cout << " 删除了一个结点 !\n";

} else // 未找到待删除结点 cout<<num<<" 链表上没有找到要删除的结点 !\n";

return(head);}

Page 17: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

4. 释放链表

void Delete_chain( node *head // 释放已创建的链表结点空间 ) { node *p;

while( head){

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

}}

Page 18: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

5. 插入结点 把一个结点插入链表,使链表结点数据保持升序

插入结点时 , 分四种情况①原链表为空链表;②插入在链表首结点之前;③ 插入在链表中间;④插在链表尾结点之后。

Page 19: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

// 函数功能:将 p 指向的结点插入链表 , 结果链表保持有序。 返回值是新链表的首指针。node *Insert(node *head, node *p) //{ node *p1, *p2; if(head == NULL) // 原链表为空链表,对应情况① { head = p;

p - >next = NULL;return(head);

} p1 = head; while( (p - >data) > (p1 - >data) && p1 - >next != NULL ) { // 寻找待插入位置

p2 = p1; p1 = p1 - >next; // p2 指向的结点在 p1 指向的结点之前

}

Page 20: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

if( (p - >data) <= (p1 - >data) ) // 插在 p1 之前 { p - >next = p1; if(head == p1)

head = p; // 插在链表首部,对应情况② else

p2 - >next = p; // 插在链表中间,对应情况③ } else // 插在链表尾结点之后,对应情况④ { p1 - >next = p; p - >next = NULL; } return(head);}

序插 演示 过

Page 21: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

6. 创建有序链表node *Create_sort(void){ node *p1, *head=0; int a; cout << "产生一条排序链表 , 请输入数据 , 以 -1 结束 : "; cin >> a; while( a!=-1 ) { p1=new node;

p1->data = a;head=Insert(head,p1);cin >> a;

} return(head);}

Page 22: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

7. 主函数,测试上述函数#include <iostream.h>#include "node_def.h" #include "create.h"#include "print_search.h"#include "delete.h"#include "insert.h"#include "deletechain.h"#include "create_sort.h"void main( ){ node *head;

int num;head=Create( ); // 建立一条无序链表Print(head); // 输出链表

Page 23: 关于单链表掌握以下知识: 为什么需要单链表 如何通过指针和结构体的定义实现单链表结构 单链表常用操作的实现方法 : 建立、遍历、插入、删除、查找、

续:cout << " 输入待删除结点上的整数: \n";cin >> num;head=Delete_one_node(head, num);Print(head); // 删除一个结点后,输出链表cout << " 输入要查找的整数: \n";cin >> num;if(Search(head, num)!=NULL)

cout<<num<<" 在链表中 \n";else

cout<<num<<" 不在链表中 \n";Deletechain(head); // 释放上述链表空间head=Create_sort( ); // 建立一条有序链表Print(head); // 输出链表Deletechain(head); // 释放有序链表空间

}