第3章 栈和队列.

Slides:



Advertisements
Similar presentations
阻塞操作. 在 linux 里,一个等待队列由一个 wait_queue_head_t 类型的结构来描述 等待队列的初始化: static wait_queue_head_t testqueue; init_waitqueue_head(&testqueue);
Advertisements

一、算法的基本概念 二、数据结构 三、栈和队列 四、排序方法 五、二叉树
问题的提出: 例1 把十进制整数转换为二至十六之间的任一进制数输出。
第二章 线性表 £2.4 线性表的应用 £2.1 线性表的类型定义 £2.2 线性表的顺序存储结构 £2.3 线性表的链式存储结构
实用数据结构基础 第3章 栈.
第五讲 队列.
数据结构——树和二叉树 1/96.
Linked List Operations
第3章 栈和队列 3.1 栈 3.2 队列 3.3 应用.
第6章 佇列(Queues) 6-1 佇列的基礎 6-2 佇列的表示法 6-3 環狀佇列 6-4 雙佇列.
主要内容: 1.第一部分 概述 2.第二部分 线性表、栈、队列
第三章 栈和队列 Stack and Queue
第二章 线性表 线性表 顺序表 链表 顺序表与链表的比较.
第三章栈和队列 栈 队列 递归.
佇列 (Queue).
資料結構 第5章 佇列.
数据结构.
第十章 基本数据结构 链表结构 栈结构 队列结构 主讲:李祥 时间:2015年10月.
Ch.3 栈和队列 黄刘生 中国科学技术大学计算机系 国家高性能计算中心(合肥)
线性表小结 元素之间的线性关系 顺序表 顺序表:元素相邻存储 单链表:后继指针链接 一维数组 给定下标随机存取
本 章 说 明 3.1 栈 3.2 栈的应用举例 3.3 栈与递归的实现 3.4 队 列 本 章 小 结 返回主目录.
cn/~dongeliu/dsa.html 刘 东 信息学院6系 中国科学技术大学
第3章 栈和队列(二) 1/.
第三章 栈和队列 栈和队列是两种重要的数据结构。
第3章 栈和队列 丽水学院工学院.
1、栈及其实现 2、栈的应用 3、队列及其实现 4、优先级队列 5、链式队列 6、队列应用
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
第2章 线性表 2.1 线性表的类型定义 2.2 线性表的顺序表示和实现 2.3 线性表的链式表示和实现 2.4 一元多项式的表示.
线性表是一种最简单的线性结构 线性结构的基本特征为: 线性结构 是 一个数据元素的有序(次序)集 1.集合中必存在唯一的一个“第一元素”;
第3章 堆栈和队列 堆栈 堆栈应用 队列 队列应用 优先级队列 主要知识点.
Chapter 6 队列(QUEUES).
第三章 栈与队列 £3.1 栈 £3.3 队列 £3.2 栈的应用举例 £3.1.1 栈的定义 £3.1.2 栈的顺序存储结构
第三章 栈和队列 3.1 栈 3.2 栈的应用举例 3.3 栈与递归的实现 3.4 队列.
第3章 栈和队列(一).
第三章 栈和队列.
数据结构 Data Structure 中南大学 主讲人:王国军,郑瑾 中南大学信息院计科系
資料結構與C++程式設計進階 堆疊與佇列(Stack & Queue) 講師:林業峻 CSIE, NTU 6/ 21, 2010.
陈海明 副教授 信息学院 计算机系 电子信息类非计算机专业选修课 程序设计实践 陈海明 副教授 信息学院 计算机系
(知识点三) 2.3 线性表的链式表示和实现 本节将介绍线性表的另一存储结构—— 链式存储及其对应的操作。
教 师:曾晓东 电 话: E_mail: 计算机软件技术基础 教 师:曾晓东 电 话: E_mail:
第3章 栈和队列 3.1 栈 本章主题:栈和队列的应用 教学目的:掌握栈和队列的应用方法,理解栈的重要作用
数 据 结 构 刘家芬 Sept 2012.
线性表练习.
第三章 栈和队列.
第3章 栈和队列 ——操作受限的线性表 3.1 栈 3.1.1抽象数据类型栈的定义 3.1.2栈的表示与实现
資料結構 第4章 堆疊.
严蔚敏、吴伟民编著 清华大学出版社 学习网站:
顺序表的插入.
数据结构概论 第3章 栈和队列 董黎刚 浙江工商大学信电学院 2019年2月25日.
第四章 栈和队列 4.1栈 4.2栈的应用 4.3队列 4.3队列应用.
计算机软件技术基础 数据结构与算法(3).
第三章 栈和队列 第二部分 队列(Queue) 2019/4/8.
顺序表的删除.
Linked Lists Prof. Michael Tsai 2013/3/12.
队列及其实现.
单链表的基本概念.
第 四 讲 线性表(二).
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
第三章 数据组织与处理.
第 六 讲 栈和队列(一).
本章的基本内容是: ⑴栈和队列的定义及操作特性; 第3章 特殊线性表—栈、队列和串 ⑵栈和队列的两种存储方法和基本运算的实现;
实验目的:掌握数据的顺序存储结构及它们在计算机中的操作。 实验内容:
第1章 数据结构基础概论 本章主要介绍以下内容 数据结构研究的主要内容 数据结构中涉及的基本概念 算法的概念、描述方法以及评价标准.
第七讲 栈和队列(二) 1/.
第3章 栈和队列 3.1 栈 3.2 队列.
Chapter 2 Entity-Relationship Model
第三章 栈和队列.
昆山爱达人信息技术有限公司 QQ: 本节内容 1.栈的链式存储结构及实现 2.栈的应用.
第二章 线性表 东南大学计算机学院 方效林 本课件借鉴了清华大学殷人昆老师 和哈尔滨工业大学张岩老师的课件.
Presentation transcript:

第3章 栈和队列

2 3.1 栈 栈的逻辑结构 栈的顺序存储结构 栈的链式存储结构

3 3.1.1 栈的逻辑结构 1. 栈的定义 栈 (stack) 是限定仅在表尾进行插入或删除操作的线性表,因此,对于栈来说,表尾端有其特殊的含义。 在栈中,插入元素叫作入栈,删除元素叫作出栈。通常将允许进行插入或删除操作的一端称为栈顶 (top),栈顶将随着栈中的数据元素的增减而浮动,通过栈顶指针指明当前元素的位置;另一端称为栈底 (bottom),栈底指针并不随着栈中的元素的增减而移动,栈底是固定的。不含元素的空表称为空栈。

5 出栈 进栈 an a2 a1 栈顶 栈底 (a) 非空栈 (b) 空栈

2. 栈的基本操作  InitStack (S ) 操作结果:构造一个空栈 S。  Push (S, x) 初始条件:栈 S 已经存在。 6 2. 栈的基本操作  InitStack (S ) 操作结果:构造一个空栈 S。  Push (S, x) 初始条件:栈 S 已经存在。 操作结果:插入元素 x为新的栈顶元素。  Pop (S,x) 初始条件:栈 S 已经存在且非空。 操作结果:删除栈 S 栈顶元素,并用 x返回其值。

7 3.1.2 栈的顺序存储结构 栈的顺序存储结构(简称顺序栈)是利用一组连续的存储单元依次存放自栈底到栈顶的数据元素,同时附设指针 top 指示栈顶元素在顺序栈中的位置。

1. 栈的顺序存储表示 # define STACK_SIZE 50; /* 存储空间初始分配量*/ 8 1. 栈的顺序存储表示 # define STACK_SIZE 50; /* 存储空间初始分配量*/ typedef struct /* 顺序栈存储结构定义*/ { StackElementType elem[Stack_Size]; /* 用来存放栈中元素的 一维数组*/ int top; /* 栈顶指针,top为-1表示空栈*/ } SeqStack ; /* 顺序栈的类型名*/

9 2. 栈的初始化操作 S->top 称为栈顶指针,初值为-1, 可作为栈空的标记。假定 1 个元素占 1 个存储单元:当插入新元素时,先使指针 S->top 增 1再插入;当删除元素时,先删除元素再使指针 S->top 减 1 ,因此,非空栈中的栈顶指针top始终指向栈顶元素。 栈顶指针的初值也可以为0。此时:当插入新元素时,先插入再使指针S->top增1;删除元素时,先使指针S->top减1,再删除。此时,栈中的栈顶指针始终指向当前栈顶元素的下一个位置。

3. 基本操作在顺序栈上的实现 (1) 初始化 void InitStack ( SeqStack *S ) /* 构造一个空栈 S*/ 10 3. 基本操作在顺序栈上的实现 (1) 初始化 void InitStack ( SeqStack *S ) /* 构造一个空栈 S*/ { S.top = -1; return OK; } /* InitStack*/ InitStack 算法的时间复杂性为 O(1)。

11 (2) 取栈顶元素 Status GetTop ( SeqStack *S, StackElementType *x) /* 如果栈空,则返回 FALSE*//* 如果栈不空,则用 x返回 S 的栈顶元素,并返回 TRUE。*/ { if ( S->top = = -1) return FALSE; /* 如果栈 S 为空,则返回FALSE*/ else { *x= S->elem[ S->top]; /*栈顶指针所指向的单元内的值赋给x指向的单元中*/ return (TRUE); } } /* GetTop*/ GetTop 算法的时间复杂性为 O(1)

12 (3) 将元素弹出栈 int Pop ( SeqStack *S, StackElementType *x ) /*若栈空,则返回 FASLE*/;/* 若栈不空,将栈S的栈顶元素弹出,放到x所指的存储空间中*/ { if ( S->top ==-1) return FALSE; /* 如果栈 S 为空,则返回FALSE*/ else { *x=S->elem[S->top]; /*将栈顶指针所指单元内的值赋给 e*/ S->top--; /* 栈顶指针减 1*/ } return OK; }/* Pop*/ Pop算法的时间复杂性为 O(1)

Int Push ( SeqStack *S; StackElementType x) 13 Int Push ( SeqStack *S; StackElementType x) /* 插入元素 x为新的栈顶元素*/ { if ( S->top ==Stack_Size-1) return(FALSE); /* 栈满*/ S->top++; /*栈顶指针加 1*/ S->elem[S->top]=x; /* x送入栈顶指针指向的单元*/ return(TRUE); }/* Push*/ Push 算法的时间复杂性为 O(1)

栈的应用(一)表达式求值问题 int ExpEvaluation( ) InitStack(&OVS); InitStack(&OPTR); Push(&OPTR,’#’); printf(“\n\n请输入一个表达式串(以#结尾):”); ch=getchar( ); while(ch!=‘ #’||GetTop(OPTR)!=‘#’) { if(!In(ch,OPSet)) { n=GetNumber(&ch); /*读入的数ch拼接后转化为十进制*/ push(&OVS,n); } else { switch (Compare(GetTop(OPTR),ch))

{ case ‘<‘: /*栈顶运算符的优先级低于刚读入运算符的优先级*/ Push(&OPTR,ch); ch=getchar( ); break; 15 case’>=‘: Pop(&OPTR,&op); Pop(&OVS,&b); Pop(&OVS,&a); v=Execute(a,op,b); Push(&OVS, v); break; } } /*end of while*/ V=GetTop(OVS); return(v); } /* ExpEvaluation*/

16 3.1.3 栈的链式存储结构 栈的链式存储结构(简称链式栈)是指栈中各数据元素独立存储,依靠指针链接建立相邻的逻辑关系。这里和线性表的单链表一样,为了操作方便起见,我们也给链式栈添加一个头结点,并令头指针指向头结点。 栈顶指针 top 惟一地确定一个链式栈,top->next 指向栈顶元素。当 top->next = NULL 时,该链式栈为空栈。链式栈没有栈满的问题。 在应用中,如果同时需要两个以上的栈,则最好采用链式栈。

由于栈操作是线性表操作的特例(入栈相当于线性表的插入,出栈相当于线性表的删除),则链式栈的操作易于实现, 17 1. 栈的链式存储结构 typedef struct node { StackElementType data; /* 数据域*/ struct node *next; /* 指针域*/ } LinkStackNode ,*LinkStack; // 链式栈的类型名 由于栈操作是线性表操作的特例(入栈相当于线性表的插入,出栈相当于线性表的删除),则链式栈的操作易于实现,

18 2. 基本操作在链式栈上的实现 (1) 取栈顶元素 int GetTop1 ( LinkStack top, StackElementType *e ) /* 如果栈 S 空,则返回 FALSE;*//*如果栈 S 不空,则用 e 返回 S 的栈顶元素,并返回 TRUE。*/ { if ( ! top->next ) /* 如果栈 S 为空,则返回FALSE*/ return(FALSE); else { /* 如果栈 S 不空,则返回栈顶元素*/ e = top->next->data; return(FALSE); } /* else */ } // GetTop1 GetTop1算法的时间复杂性为 O(1)

19 (2) 将元素压入栈 int Push1 ( LinkStack &top; SElemType e ) /* 将元素 e 插入到栈 S 中,成为新的栈顶元素*/ { q = ( LinkStack ) malloc ( sizeof ( SNode ) ); if ( ! q ) return (FALSE); // 存储分配失败 q->data = e; // 将数据 e 写入新结点 q->next = top->next; // 将新结点插入栈顶 top->next = q; // 修改栈顶指针 return OK; } /* Push1*/ Push1 算法的时间复杂性为 O(1)

(3) 将元素弹出栈 Status Pop1 ( LinkStack &top, SElemType &e ) 20 (3) 将元素弹出栈 Status Pop1 ( LinkStack &top, SElemType &e ) /*若栈 S 空,则返回 ERROR ; 若栈 S 不空,则删除 S 栈顶元素,用 e 返回其值,并返回 OK */ { if ( ! top->next ) return ERROR; // 如果栈 S 为空,则返回 ERROR e = top->next->data; // 取出栈顶元素的值 q = top->next; // q 指向栈顶元素 top->next = q->next; // 删除栈顶元素 free (q); // 释放栈顶元素所占的空间 return OK; } /*Pop1*/ Pop1 算法的时间复杂性为 O(1)

21 3.2 队 列 队列的逻辑结构 队列的顺序存储结构 队列的链式存储结构

3.2.1 队列的逻辑结构 1. 队列的定义 队列 (queue) 是限定只能在表的一端进行插入,而在表的另一端进行删除操作的线性表。 22 3.2.1 队列的逻辑结构 1. 队列的定义 队列 (queue) 是限定只能在表的一端进行插入,而在表的另一端进行删除操作的线性表。 在队列中,我们把允许插入的一端称为队尾 (rear),通过队尾指针指明队尾的位置;把允许删除的一端称为队头 (front),通过队头指针指明队头的位置。队头和队尾指针将随着队列的动态变化而移动。

23 假设有队列 Q = ( a1, a2, a3, …, an-2, an-1, an ),则 a1 就是队头元素,an 就是队尾元素。队列中的元素是按照 a1, a2, a3, …, an-2, an-1, an 的顺序进队的,而第一个出队的元素是 a1,第二个出队的元素是 a2,只有在 ai-1 出队后 ai 才可以出队(1≤i≤n)。 当队列中没有元素时称为空队列。

操作结果:插入元素 e 为队列 Q 的新队尾元素。  DeleteQueue ( &Q, &x ) 初始条件:队列 S 已经存在且非空。 24 (2) 队列的抽象数据类型定义  InitQueue ( &Q ) 操作结果:构造一个空队列 Q。  EnterQueue ( &Q, x) 初始条件:队列 Q 已经存在。 操作结果:插入元素 e 为队列 Q 的新队尾元素。  DeleteQueue ( &Q, &x ) 初始条件:队列 S 已经存在且非空。 操作结果:删除队列 Q 队头元素,用 x返回其值。

25 3.2.2 队列的顺序存储结构 和顺序栈相类似,在队列的顺序存储结构中,除了利用一组连续的存储单元依次存放从队列头到队列尾的数据元素之外,还需要附设两个指针:队头指针 front 和队尾指针 rear,分别指示队列头元素和队列尾元素的位置。

1. 队列的顺序存储表示 # define MAXSIZE 50; //最大队列长度 typedef struct { 26 1. 队列的顺序存储表示 # define MAXSIZE 50; //最大队列长度 typedef struct { QueueElementType element[MAXSIZE]; //队列的元素空间 int front; // 队头指针 int rear; //队尾指针 } SeqQueue ; //顺序队列的类型名

27 2. 队列的初始化操作 初始化构建空队列时,令 Q->front = Q->rear = 0。每当插入新的队列尾元素的时候,先将新元素插入队尾指针所指位置,再使队尾指针 Q->rear 加 1;每当删除旧的队列头元素的时候,先使队头指针所指位置的元素取出,再使队头指针 Q->front 加 1。因此,在非空队列中,队头指针始终指向队列头元素,而队尾指针始终指向队列尾元素的下一个位置。

对下面给出的队列进行插入和删除操作的示意过程: 28 3. 循环队列 对下面给出的队列进行插入和删除操作的示意过程: 从队列头删除旧元素 从队列尾插入新元素 J1 J2 J3 J4 Q->front Q->rear J5 J6 J7 J8 Q->front Q->front Q->front Q->front Q->rear Q->rear Q->rear Q->rear 在队列中,由于不断插入新元素,队尾很快会超出数组 Q[ ] 的边界;由于不断删除旧元素,数组 Q[ ] 的开始处又会出现很多空位。

① 当发生这样的情况时,把队列中的数据元素移到数组 Q[ ] 的前端,并且修改头指针和尾指针。 29 为了调剂余缺,解决办法有下面两种: ① 当发生这样的情况时,把队列中的数据元素移到数组 Q[ ] 的前端,并且修改头指针和尾指针。 ② 将数组 Q[ ] 的 “尾” 与 Q[ ] 的 “头” 逻辑上接起来,形成循环队列,这是一种较巧妙的解决办法,也是通常采用的解决办法。

4. 基本操作在循环队列上的实现 (1) 构造空队列 30 4. 基本操作在循环队列上的实现 (1) 构造空队列 void InitQueue ( SeqQueue *Q ) //构造一个空循环队列 Q { Q->front = Q->rear = 0; } /* InitQueue*/ InitQueue 算法的时间复杂性为 O(1)

31 (2) 在队列尾插入新元素 int EnterQueue ( SeqQueue *Q, QueueElemmentType x) /* 如果队列满,则返回 FALSE,如果队列不满,则插入元素x 为 Q 新的队尾元素。*/ { if ( ( Q->rear + 1 ) % MAXSIZE = = Q->front ) return (FALSE) // 如果队列满,则无法插入,报错 Q->element [Q->rear] =x; //插入 Q->rear = ( Q->rear + 1 ) % MAXSIZE; //移动队尾指针 return (TRUE); } /*EnterQueue*/ EnterQueue算法的时间复杂性为 O(1)

(4)删除队列头元素 int DeleteQueue ( SeqQueue *Q, QueueElemmentType*x) { 32 (4)删除队列头元素 int DeleteQueue ( SeqQueue *Q, QueueElemmentType*x) /*如果队列空,则返回 FALSE, 如果队列不空,则删除 Q 的队列头元素,用 x返回其值, 并返回 TRUE。*/ { if ( Q->front = = Q->rear ) return (FALSE); // 若队列空,则无法删除,报错 *x= Q->element [Q->front]; //带回被删除元素 Q->front = ( Q->front + 1 ) % MAXQSIZE; //移动队头指针 return (TRUE); } /* DeleteQueue*/ DeleteQueue 算法的时间复杂性为 O(1)

队列的应用:打印杨辉三角形前n行算法 void YangHuiTrianble( ) { SeqQueue Q; InitQueue(&Q); //初始化队列 EnterQueue(&Q,1) //第一行元素入队列 for( n=2;n<=N; n++) //产生第n行元素并入队,同时打印第n-1行的元素 { EnterQueue(&Q,1); //杨辉三角的每行的第一个元素是1   for( i=1;i<=n-2;i++) { DeleteQueue (&Q, &temp); printf (“%d”, temp); GetHead (Q, &x); temp=temp+x; EnterQueue (&Q, temp); }   

3.2.3 队列的链式存储结构 当用户无法予估计所用队列的最大长度时,宜采用链式存储结构。 34 3.2.3 队列的链式存储结构 当用户无法予估计所用队列的最大长度时,宜采用链式存储结构。 队列的链式存储结构(简称链队列)是利用单链表表示的队列。一个链队列显然需要两个分别指向队列头和队列尾的指针(分别称为头指针和尾指针)才能唯一确定。这里,和线性表的单链表结构一样,为了操作方便起见,我们也给链式队列添加一个头结点,并令头指针指向头结点。由此,空的链队列的判决条件为头指针和尾指针均指向头结点。

1. 队列的链式存储结构 typedef struct Node // 结点定义 35 1. 队列的链式存储结构 typedef struct Node // 结点定义 { QueueElementType data ; // 数据域 struct Node *next ; // 指针域 }LinkQueueNode; typedef struct // 队列定义 { LinkQueueNode; *front; // 队列头指针 LinkQueueNode; *rear; // 队列尾指针 } LinkQueue ; // 链式队列的类型名

3. 基本操作在链式队列上的实现 (1) 构造空队列 36 3. 基本操作在链式队列上的实现 (1) 构造空队列 int InitQueue ( LinkQueue *Q ) // 构造一个空链式队列 Q { Q->front = (LinkQueueNode*) malloc ( sizeof (LinkQueueNode ) ); if (Q->front!=NULL ) { Q->rear= Q->front; Q->front->next=NULL; return (TRUE); } else return (FALSE) // 存储分配失败 InitQueue 算法的时间复杂性为 O(1)

(2) 销毁队列 int DestroyQueue ( LinkQueue *Q ) // 销毁队列 Q 37 (2) 销毁队列 int DestroyQueue ( LinkQueue *Q ) // 销毁队列 Q { while ( Q->front ) { Q->rear = Q->front->next; free ( Q->front ); Q->front = Q->rear; } // while return OK; } // DestroyQueue DestroyQueue算法的时间复杂性为 O(n) Q->front x y ∧ z Q->rear

38 (3) 在队列尾插入新元素 int EnterQueue ( LinkQueue *Q; QueueElementType x) // 插入元素 x为 Q 的新的队列尾元素 {LinkQueueNode *NewNode; NewNode= (LinkQueueNode *) malloc ( sizeof (LinkQueueNode)); if (NewNode!=NULL) { NewNode->data =x; NewNode->next = NULL; Q->rear -> next =NewNode; Q->rear =NewNode; return(TRUE); } else return (FALSE) // 存储分配失败 } /* EnterQueue*/ EnterQueue 算法的时间复杂性为 O(1)

{if ( Q->front = = Q>rear ) return(FALSE); 39 int DeleteQueue ( LinkQueue *Q, QueueElemmentType *x ) /* 如果队列空,则返回 FALSE;如果队列不空,则删除 Q 的 队列头元素,用 x返回其值,并返回 TRUE。*/ {if ( Q->front = = Q>rear ) return(FALSE); p = Q->front->next; // p 指向队列的头元素结点 Q->front->next = p->next; // 删除 p 指向的结点 if ( Q->rear = = p ) Q->rear = Q->front; // 若删除的是队列最后一个元素,则令队尾指针等于队头指针 *x= p->data; // 取出队列头元素结点的数据 free ( p ); return (TRUE); } // DeleteQueue DeQueue 算法的时间复杂性为 O(1)

队列应用─模拟患者医院看病过程 void SeeDoctor( ) { InitQueue(Q); flag=1; while(flag){ 40 while(flag){ printf(“\n请输入命令:”);  ch=getchar( ); switch(ch){ case ‘a’: printf(“\n病历号:”); scanf(“%d”,&n); EnterQueue(&Q, n); break;

case ‘n’: if (! IsEmpty(Q)) { DeleteQueue (&Q, &n); printf(“\n病历号为%d的病人就诊”,n); } else printf(“\n无病人等候就诊”);     break; case ‘q’: printf(“\n今天停止挂号,下列病人依次就诊:”);      while (!IsEmpty(Q)) { DeleteQueue(&Q,&n); printf(“%d”,n); } Flag=0; break; default: printf(“\n非法命令!”);}}}