1 / 39

第三章 栈和队列

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

siusan
Download Presentation

第三章 栈和队列

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. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第三章 栈和队列 2007.9 Stackand Queue

  2. 生活中的栈与队列 • 计算机中的栈与队列 • 栈:表达式的求值、函数的递归调用 • 队列:打印机任务处理、CPU的进程处理 • 栈与队列的特征 • LIFO(Last In First Out) • FIFO(First In First Out)/FIFS(First In First Service)

  3. 第1节 栈 栈的定义 栈的特征 栈的基本运算 顺序栈的实现 链式栈的实现

  4. 1.栈(Stack)的定义 栈(Stack)是限制在表的一端进行插入和删除运算的线性表。 • 栈顶(Top)为可进行插入、删除的一端,另一端为栈底(Bottom)。 • 空栈:表中没有元素。 假设栈S=(a1,a2,a3,…an),则a1称为栈底元素,an为栈顶元素。 栈中元素按a1,a2,a3,…an的次序进栈,退栈的第一个元素应为栈顶元素。换句话说,栈的修改是按后进先出的原则进行的。因此,栈称为后进先出表(LIFO)。

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

  6. 第2节 线性表的顺序存储—顺序表 顺序表的存储结构 顺序表存储结构的C语言描述 基于顺序表的运算的实现

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

  8. 2.顺序表存储结构的C语言描述 数组下标 内存 # define ListSize 100 typedef int DataType; typedef struc{ DataType data[ListSize]; int length; } SequenList; /*声明变量*/ SequenList *sqlist; 0 a1 1 a2 length n-1 an 备用空间 ListSize -1

  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;

  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

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

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

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

  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)

  15. 平均复杂度:由于插入可能在表中任何位置上进行,因此需分析算法的平均复杂度平均复杂度:由于插入可能在表中任何位置上进行,因此需分析算法的平均复杂度 在长度为n的线性表中第i个位置上插入一个结点,令Eis(n)表示移动结点的期望值(即移动的平均次数),则在第i个位置上插入一个结点的移动次数为n-I+1。故 不失一般性,假设在表中任何位置(1≦i≦n+1)上插入结点的机会是均等的,则在等概率插入的情况下,

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

  17. 5.顺序表存储结构特点 • 基本特点:逻辑上相邻的元素,物理上也是相邻的。 • 优点 • 可以直接实现元素的定位,或者说可以随机存取其中的任意元素 • 因为数据在存储结构中是连续存储的,任意元素的存储地址可由公式直接算出。 • 高级程序设计语言提供的数组类型可以直接定义顺序存储结构的线性表,使程序实现十分方便。

  18. 顺序存储结构的不方便之处: • 数据元素最大个数需预先确定,使得高级语言编译系统需预先分配相应的存储空间。 • 插入与删除运算的效率很低。 • 为了保持线性表中的数据元素的顺序,在插入操作和删除操作时需移动大量数据。 • 顺序存储结构的线性表的存储空间不便于扩充。 • 当一个线性表分配顺序存储空间后,如果线性表的存储空间已满,但还需要插入新的元素,则会发生“上溢”错误,会导致运算的失败或中断。

  19. 2.2 单链表 Link List

  20. 复习:关于指针的概念 即 指针 地址 指针 与 指针所指的变量 • 指针相关的函数 • malloc • free

  21. 1.单链表的存储结构 线性表的逻辑结构 头指针 head: 165 单链表的结点结构 110 135 单链表的逻辑结构 head … 165 单链表的存储结构 200

  22. 一个具体的例子 head

  23. 一个具体的例子 头指针 head: 165 110 135 165 200

  24. 2.单链表存储结构的C语言描述 顺序表的描述 # define ListSize 100 typedef int DataType; typedef struc{ DataType data[ListSize]; int length; } SequenList; /*声明变量*/ SequenList *sqlist; typedef int DataType; typedef strucnode{ DataType data; strucnode *next; } LinkList; /*声明变量*/ LinkList *head; … head

  25. typedef struct stu { char* num; char* name; int english; int c_languge; int data_stru; }StuType; typedef strucnode{ StuType data; • strucnode *next ; • } class_list; class_list *soft_1_head; 一个具体的例子 学生成绩表 # 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;

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

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

  28. 单链表算法的时间复杂度

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

  30. 单链表存储结构特点 • 基本特点:逻辑上相邻的元素,物理上不一定相邻的。 • 优缺点 • 是一种动态结构,整个存储空间为多个链表共用 • 不需预先分配空间 • 指针占用额外存储空间 • 不能随机存取,查找速度慢

  31. 2.3 其他形式的链表

  32. Data_n ^ Data_1 Data_2 … Data_n Data_1 Data_2 head 2.3.1 循环链表 • 循环链表是表中最后一个结点的指针指向头结点,使链表构成环状。 • 特点:从表中任一结点出发均可找到表中其他结点,提高查找效率。 循环链表的逻辑结构 单链表的逻辑结构 循环链表的空表

  33. 循环链表的操作 • 循环链表的操作与单链表基本一致,循环条件不同 • 单链表: p->next==NULL • 循环链表: p->next==head

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

  35. … Data_n Data_m ^ ^ Data_1 Data_1 Data_2 Data_2 讨论:如何将两个链表合并? • 利用单链表如何实现?P47 EX4 ha hb

  36. … Data_n Data_m Data_1 Data_1 Data_2 Data_2 • 利用循环链表如何实现? • 表头指针与表尾指针的使用 ha ra hb rb

  37. 2.3.2 双向循环链表 双向链表的结点结构 双向循环链表的逻辑结构 head 双向循环链表的空表 head 特点:方便查找任一结点的后继和前驱。

  38. 双向循环链表的C语言描述 typedef int DataType; typedef strucDnode{ DataType data; strucDnode *prior, next; } DoubleLinkList; /*声明变量*/ DoubleLinkList *head;

  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)。

More Related