39

Click here to load reader

第三章 栈和队列

  • Upload
    siusan

  • View
    111

  • Download
    4

Embed Size (px)

DESCRIPTION

第三章 栈和队列. 2007.9. Stack and Queue. 生活中的栈与队列 计算机中的栈与队列 栈:表达式的求值、函数的递归调用 队列:打印机任务处理、 CPU 的进程处理 栈与队列的特征 LIFO(Last In First Out) FIFO(First In First Out)/FIFS(First In First Service). 第 1 节 栈. 栈的定义 栈的特征 栈的基本运算 顺序栈的实现 链式栈的实现. 1. 栈( Stack ) 的定义. - PowerPoint PPT Presentation

Citation preview

Page 1: 第三章   栈和队列

2007.9

第三章 栈和队列Stack and Queue

Page 2: 第三章   栈和队列

生活中的栈与队列

计算机中的栈与队列栈:表达式的求值、函数的递归调用队列:打印机任务处理、 CPU 的进程处理

栈与队列的特征LIFO(Last In First Out)FIFO(First In First Out)/FIFS(First In First

Service)

Page 3: 第三章   栈和队列

栈的定义栈的特征栈的基本运算顺序栈的实现链式栈的实现

第 1 节 栈

Page 4: 第三章   栈和队列

1. 栈( Stack )的定义 栈 (Stack) 是限制在表的一端进行插入和删除运算的线性表。 栈顶 (Top) 为可进行插入、删除的一端,另一端为栈底

(Bottom) 。 空栈:表中没有元素。 假设栈 S=(a1 , a2 , a3 ,… an) ,则 a1 称为栈底元素,

an 为栈顶元素。栈中元素按 a1 , a2 , a3 ,… an 的次序进栈,退栈的第一个元素应为栈顶元素。换句话说,栈的修改是按后进先出的原则进行的。因此,栈称为后进先出表( LIFO )。

Page 5: 第三章   栈和队列

3. 线性表的基本运算1.InitList(L) : 初始化 , 置表 L 为空2.ClearList(L): 将表 L 清空3.ListLength(L) : 求表 L 的长度4.Insert(L,i,Item) : 插入,在给定的线性表 L 中,将数据 Item 插入到第 i 个结点的位置。表 L 长度加 1 。5.Delete(L,i) :删除,在给定的线性表 L 中,删除第 i 个元素。表 L 的长度减 1 。6.Get(L,i) :获取表 L 中第 i 个结点的值。7.Locate(L,x) :查找定位。若线性表 L 中存在一个值为x 的结点,则返回该结点的位置,否则返回空。8.GetNext ( L , Item , p):取 Item的后继结点9.GetPrior ( L , Item , p ):取 Item   的前驱结点

Page 6: 第三章   栈和队列

顺序表的存储结构顺序表存储结构的 C 语言描述基于顺序表的运算的实现

第 2 节 线性表的顺序存储—顺序表

Page 7: 第三章   栈和队列

1. 顺序表的存储结构顺序表:把线性表的结点按逻辑顺序依次存放在一组地址连续的存储单元里。用这种方法存储的线性表简称顺序表。由于 C 语言中的一维数组也是采用顺序存储表示,故可以用数组类型来描述顺序表。

a1

a2

an

内存0

ListSize -1

备用空间

有效结点

Page 8: 第三章   栈和队列

2. 顺序表存储结构的 C 语言描述 # define ListSize 100 typedef int DataType; typedef struc{ DataType

data[ListSize]; int length; } SequenList;

/* 声明变量 */SequenList *sqlist;

a1

a2

an

01

n-1length

内存 数组下标

ListSize -1

备用空间

Page 9: 第三章   栈和队列

一个具体的例子 # define ClassSize 100

typedef struct stu{ char* num; char* name; int english; int c_languge; int data_stru;}StuType;typedef struc{ StuType

data[ClassSize]; int class_len; } class_list;class_list *soft_1;

学号 姓名 大学英语 C 语言 数据结构 …A07001 王萍 90 85 95 …A07002 马玲 80 85 90 …A07003 张兰 95 91 99 …A07004 李建 70 84 86 …A07005 黄勇 82 76 78 …

: : : : : :

学生成绩表

Page 10: 第三章   栈和队列

顺序表的地址关系 假设线性表的每个元素需占用 m 个存储单元,并以所占的第一个单元的存储地址作为数据元素的存储位置。则线性表中第 i+1 个数据元素的存储位

置 LOC( a i+1) 和第 i 个数据元素的存储位置LOC(a I ) 之间满足下列关系:

LOC(a i+1)=LOC(a i)+m

线性表的第 i 个数据元素 ai 的存储位置为: LOC(ai)=LOC(a1)+(i-1)*m

Page 11: 第三章   栈和队列

3. 基于顺序表的运算的实现1.InitList(L) : 初始化 , 置表 L 为空2.ClearList(L): 将表 L 清空3.ListLength(L) : 求表 L 的长度4.Insert(L,i,Item) : 插入,在给定的线性表 L中,将数据 Item 插入到第 i 个结点的位置。表 L长度加 1 。5.Delete(L,i) :删除,在给定的线性表 L 中,删除第 i 个元素。表 L 的长度减 1 。

Page 12: 第三章   栈和队列

6.Get(L,i) :获取表 L 中第 i 个结点的值。7.Locate(L,x) :查找定位。若线性表 L 中存在一个值为 x 的结点,则返回该结点的位置,否则返回空。8.GetNext ( L , Item , p):取 Item的后继结点9.GetPrior ( L , Item , p ):取 Item

  的前驱结点

Page 13: 第三章   栈和队列

4. 顺序表插入算法的时间复杂度分析Void InsertList(Sequenlist *sqlist , int i, DataType

Item) { int j; if (i<1 || i>sqlist.length+1){ printf(“Position error”); return 0 ; } if( sqlist.length >= ListSize ) { printf(“overflow”); return 0; } for( j=sqlist.length-1; j>=i-1;

j-- )sqlist.data[ j+1]=sqlist.data[ j ]; sqlist.data[ i-1]=x; sqlist.length++;}

Page 14: 第三章   栈和队列

顺序表插入算法的时间复杂度分析时间主要耗费在: for( j=sqlist.length-1; j>=i-1; j-- ) sqlist.data[ j+1]=sqlist.data[ j ];所需移动结点的次数不仅依赖于表的长度,而且还与插入位置有关。最好:当 i=n 时,结点不需要后移,其时间复杂度O ( 1 );最坏:当 i=0 时,结点后移语句将循环执行 n 次,需移动表中所有结点,其时间复杂度 O ( n )

Page 15: 第三章   栈和队列

平均复杂度:由于插入可能在表中任何位置上进行,因此需分析算法的平均复杂度 在长度为 n 的线性表中第 i 个位置上插入一个结点,

令 Eis(n) 表示移动结点的期望值(即移动的平均次数),则在第 i 个位置上插入一个结点的移动次数为 n-I+1 。故 不失一般性,假设在表中任何位置 (1≦i≦n+1) 上插入结点的机会是均等的,则在等概率插入的情况下,

1

1

)1(n

ii inPEis

nO

ninn

Eis

nP

n

i

i

平均时间复杂度为:因此

,

2)1(

11

11:

1

1

Page 16: 第三章   栈和队列

也就是说,在顺序表上做插入运算,平均要移动表上一半结点。当表长 n 较大时,算法的效率相当低。虽然 Eis(n) 中 n 的的系数较小,但就数量级而言,它仍然是线性阶的。因此算法的平均时间复杂度为 O(n) 。问题:请你分析一下删除算法的时间复杂度。

Page 17: 第三章   栈和队列

5. 顺序表存储结构特点基本特点:逻辑上相邻的元素,物理上也是相邻的。优点

可以直接实现元素的定位,或者说可以随机存取其中的任意元素因为数据在存储结构中是连续存储的,任意元素的存储地址可由公式直接算出。

高级程序设计语言提供的数组类型可以直接定义顺序存储结构的线性表,使程序实现十分方便。

Page 18: 第三章   栈和队列

顺序存储结构的不方便之处:数据元素最大个数需预先确定,使得高级语言编译系统需预先分配相应的存储空间。插入与删除运算的效率很低。

为了保持线性表中的数据元素的顺序,在插入操作和删除操作时需移动大量数据。顺序存储结构的线性表的存储空间不便于扩充。

当一个线性表分配顺序存储空间后,如果线性表的存储空间已满,但还需要插入新的元素,则会发生“上溢”错误,会导致运算的失败或中断。

Page 19: 第三章   栈和队列

Link List

2.2 单链表

Page 20: 第三章   栈和队列

复习:关于指针的概念指针 地址即

指针 与 指针所指的变量指针相关的函数

•malloc•free

Page 21: 第三章   栈和队列

1. 单链表的存储结构线性表的逻辑结构

单链表的逻辑结构Data_1

Data_2

Data_n

^…

单链表的结点结构数据域 指针域

单链表的存储结构

……… ……

Data_2 200……. ……

Data_1 110…. ……

Data_n Null

135…… ……

…… ……

头指针 head : 165

110135

165

200

head

Page 22: 第三章   栈和队列

A07001

王萍90

85

95

A07002

马玲80

85

90

A07003

张兰95

91

99

A07004

李建70

84

86

A07005

黄勇82

76

78

NULL

一个具体的例子 学号 姓名 大学英语 C 语言 数据结构 …A07001 王萍 90 85 95 …A07002 马玲 80 85 90 …A07003 张兰 95 91 99 …A07004 李建 70 84 86 …A07005 黄勇 82 76 78 …

: : : : : :

head

Page 23: 第三章   栈和队列

一个具体的例子……… ……

A07002 马玲…

200

……. ……

A07001 王萍…

110

…. ……

A070046 … Null

135…… ……

…… ……

头指针 head : 165

110

135

165

200

学号 姓名 大学英语 C 语言 数据结构 …A07001 王萍 90 85 95 …A07002 马玲 80 85 90 …A07003 张兰 95 91 99 …A07004 李建 70 84 86 …A07005 黄勇 82 76 78 …

: : : : : :

Page 24: 第三章   栈和队列

2. 单链表存储结构的 C 语言描述顺序表的描述# define ListSize 100 typedef int DataType; typedef struc{ DataType

data[ListSize]; int length; } SequenList;

/* 声明变量 */SequenList *sqlist;

Data_1

Data_2

Data_n

^…head

typedef int DataType;

typedef struc node{

DataType data; struc node *next; } LinkList;

/* 声明变量 */LinkList *head;

Page 25: 第三章   栈和队列

一个具体的例子 # define ClassSize 100

typedef struct stu{ char* num; char* name; int english; int c_languge; int data_stru;}StuType;typedef struc{ StuType

data[ClassSize]; int class_len; } class_list;class_list *soft_1;

学号 姓名 大学英语 C 语言 数据结构 …A07001 王萍 90 85 95 …A07002 马玲 80 85 90 …A07003 张兰 95 91 99 …A07004 李建 70 84 86 …A07005 黄勇 82 76 78 …

: : : : : :

学生成绩表typedef struct stu{ char* num; char* name; int english; int c_languge; int data_stru;}StuType;typedef struc node{ StuType data; struc node *next ;} class_list;class_list

*soft_1_head;

Page 26: 第三章   栈和队列

基于单链表的运算的实现1.InitList(L) : 初始化 , 置表 L 为空2.ClearList(L): 将表 L 清空3.ListLength(L) : 求表 L 的长度4.Insert(L,i,Item) : 插入,在给定的线性表 L中,将数据 Item 插入到第 i 个结点的位置。表 L长度加 1 。5.Delete(L,i) :删除,在给定的线性表 L 中,删除第 i 个元素。表 L 的长度减 1 。

Page 27: 第三章   栈和队列

6.Get(L,i) :获取表 L 中第 i 个结点的值。7.Locate(L,x) :查找定位。若线性表 L 中存在一个值为 x 的结点,则返回该结点的位置,否则返回空。8.GetNext ( L , Item , p):取 Item的后继结点9.GetPrior ( L , Item , p ):取 Item

  的前驱结点

Page 28: 第三章   栈和队列

单链表算法的时间复杂度O ( 1 ) O ( n )O ( 1 ) O ( n )

InitList(L) ClearList(L)Insert(L,i,Item) ListLength(L)Delete(L,i) Get(L,i) GetNext(L , Item , p)

Locate(L,x)

GetPrior( L , Item ,p)

Page 29: 第三章   栈和队列

单链表的建立void build( int n ) { linklist *q; char ch; int i; printf("\n input %2d data: \t",n); head=(struct link *)malloc(sizeof(struct

link)); q=head; scanf("%d",&q->data); for(i=2;i<=n;i++){ q->next=(struct link

*)malloc(sizeof(struct link)); q=q->next; scanf("%d",&q->data); } q->next=NULL; }

Page 30: 第三章   栈和队列

单链表存储结构特点基本特点:逻辑上相邻的元素,物理上不一定相邻的。优缺点

是一种动态结构,整个存储空间为多个链表共用不需预先分配空间指针占用额外存储空间不能随机存取,查找速度慢

Page 31: 第三章   栈和队列

2.3 其他形式的链表

Page 32: 第三章   栈和队列

2.3.1 循环链表循环链表是表中最后一个结点的指针指向头结点,使链表构成环状。特点:从表中任一结点出发均可找到表中其他结点,提高查找效率。

单链表的逻辑结构Data_1

Data_2

Data_n

^…

循环链表的逻辑结构Data_1

Data_2

Data_n

循环链表的空表head

Page 33: 第三章   栈和队列

循环链表的操作循环链表的操作与单链表基本一致 , 循环条件不同

单链表 : p->next==NULL循环链表 : p->next==head

Page 34: 第三章   栈和队列

循环链表的建立void build( int n ) { linklist *q; char ch; int i; printf("\n input %2d data: \t",n); head=(struct link *)malloc(sizeof(struct

link)); q=head; scanf("%d",&q->data); for(i=2;i<=n;i++){ q->next=(struct link

*)malloc(sizeof(struct link)); q=q->next; scanf("%d",&q->data); } q->next=head; }

Page 35: 第三章   栈和队列

讨论:如何将两个链表合并?利用单链表如何实现? P47 EX4

Data_1

Data_2

Data_n

^…

Data_1

Data_2

Data_m

^…

ha

hb

Page 36: 第三章   栈和队列

利用循环链表如何实现?表头指针与表尾指针的使用

Data_1

Data_2

Data_n

Data_1

Data_2

Data_m

ha

hb

ra

rb

Page 37: 第三章   栈和队列

2.3.2 双向循环链表

特点:方便查找任一结点的后继和前驱。

双向循环链表的逻辑结构

双向循环链表的空表head

双向链表的结点结构 prior

data

next

head

Page 38: 第三章   栈和队列

双向循环链表的 C 语言描述typedef int DataType; typedef struc Dnode{ DataType data; struc Dnode *prior,

next; } DoubleLinkList;

/* 声明变量 */DoubleLinkList *head;

prior

data

next

Page 39: 第三章   栈和队列

双向循环链表中的插入与删除void dinsertbefor(dlistnode

*p , datatype x) { dlistnode

*q=malloc(sizeof(dlistnode));

q—>data=x; q—>prior=p—>prior; q—>next=p; p—>prior—>next=q; p—>prior=q; }

void ddeletenode(dlistnode *p)

{ p–>prior–>next=p–

>next; p–>next–>prior=p–

>prior; free(p); }•与单链表的插入和删除操作不同的是,在双链表中插入和删除必须同时修改两个方向上的指针。上述两个算是法的时间复杂度均为 O(1) 。