53
4 4 第 第第第第 第 第第第第 第第第第第第 第第 第第第第第第 第第 1 1 理理理理理理理理理理理理理理理理 、; 理理理理理理理理理理理理理理理理 、; 2 2 理理理理理理理理理理理理理理理理理理理理理理理理 、; 理理理理理理理理理理理理理理理理理理理理理理理理 、; 3 3 理理理理理理理理理理理理 、。 理理理理理理理理理理理理 、。 第第第第第第第第第第 【、】 第第第第第第第第第第 【、】 1 1 理理理理理理理理理理理理理理 、; 理理理理理理理理理理理理理理 、; 2 2 理理理理理理理理理理理理理理 、。 理理理理理理理理理理理理理理 、。

 第 4 章 栈和队列

  • Upload
    dunn

  • View
    128

  • Download
    0

Embed Size (px)

DESCRIPTION

 第 4 章 栈和队列. 【 本章教学目的、要求 】 1 、理解栈和队列的定义及其基本操作; 2 、理解栈和队列的线性存储结构和链式存储结构的特点; 3 、理解常见实例的编程思想。 【 本章教学重点、难点 】 1 、栈和队列的定义及其基本操作; 2 、线性存储结构和链式存储结构。. 主要内容. 4.0 前言 4.1 栈 4.2 队列 4.3 应用举例及分析 习题. 4.0 前言. - PowerPoint PPT Presentation

Citation preview

Page 1:  第 4 章 栈和队列

 第 第 44 章 栈和队列章 栈和队列• 【本章教学目的、要求】【本章教学目的、要求】

• 11 、理解栈和队列的定义及其基本操作;、理解栈和队列的定义及其基本操作;• 22 、理解栈和队列的线性存储结构和链式存储结构的、理解栈和队列的线性存储结构和链式存储结构的

特点;特点;• 33 、理解常见实例的编程思想。、理解常见实例的编程思想。

• 【本章教学重点、难点】【本章教学重点、难点】• 11 、栈和队列的定义及其基本操作;、栈和队列的定义及其基本操作;• 22 、线性存储结构和链式存储结构。、线性存储结构和链式存储结构。

Page 2:  第 4 章 栈和队列

主要内容主要内容

4.0 4.0 前言前言

4.1 4.1 栈栈

4.2 4.2 队列队列

4.3 4.3 应用举例及分析应用举例及分析

习题习题

Page 3:  第 4 章 栈和队列

4.0 4.0 前言前言 栈和队列是两种特殊的线性结构。栈和队列是两种特殊的线性结构。从数据的逻辑结构角从数据的逻辑结构角

度看它们是线性表,从操作角度看它们是操作受限制的度看它们是线性表,从操作角度看它们是操作受限制的线性表。线性表。栈和队列在操作系统、编译原理、大型应用软栈和队列在操作系统、编译原理、大型应用软件系统中得到了广泛应用。件系统中得到了广泛应用。

Page 4:  第 4 章 栈和队列

4.1 4.1 栈栈 4.1.1 4.1.1 栈的定义及基本操作栈的定义及基本操作

4.1.2 4.1.2 栈的顺序存储结构栈的顺序存储结构

4.1.3 4.1.3 栈的链式存储结构栈的链式存储结构

Page 5:  第 4 章 栈和队列

4.1.1 4.1.1 栈的定义及基本操作栈的定义及基本操作(( 11 ))

11 、栈的定义:、栈的定义: 栈栈 (stack)(stack) 是限定在表的一端进行插入或删除操作的线性表。是限定在表的一端进行插入或删除操作的线性表。 插入元素又叫插入元素又叫入栈入栈,删除元素又叫,删除元素又叫出栈出栈。通常将允许进行插入。通常将允许进行插入

或删除操作的一端称为或删除操作的一端称为栈顶栈顶 (top)(top) ,另一端称为,另一端称为栈底栈底 (bottom)(bottom) 。。不含元素的栈称为不含元素的栈称为空栈空栈。。

举个例子来说明栈的结构和操作:举个例子来说明栈的结构和操作:将乒乓球放入一圆柱状的玻将乒乓球放入一圆柱状的玻璃瓶中,柱状玻璃瓶的直径只比乒乓球的直径大一点点,这样,璃瓶中,柱状玻璃瓶的直径只比乒乓球的直径大一点点,这样,要想取出先放入瓶中的乒乓球只能将后放入的球先取出来要想取出先放入瓶中的乒乓球只能将后放入的球先取出来。这。这是因为瓶子只有一个瓶口,进球出球都在这个瓶口操作。这样是因为瓶子只有一个瓶口,进球出球都在这个瓶口操作。这样的操作可以看成是栈的操作。的操作可以看成是栈的操作。

Page 6:  第 4 章 栈和队列

4.1.1 4.1.1 栈的定义及基本操作栈的定义及基本操作(( 22 ))

22 、栈的结构、栈的结构 假设有一个栈假设有一个栈 S = (aS = (a11, a, a22, …, a, …, ann)) ,, aa11 先进栈,先进栈, aann 最后进栈。最后进栈。

因为进栈和出栈元素都只能在栈顶一端进行,所以每次出栈的因为进栈和出栈元素都只能在栈顶一端进行,所以每次出栈的元素总是当前栈中栈顶所在的元素,它是最后进栈的元素,而元素总是当前栈中栈顶所在的元素,它是最后进栈的元素,而最先进栈的元素要到最后才能出栈。最先进栈的元素要到最后才能出栈。

因此,栈又称为因此,栈又称为后进先出(后进先出( last in first outlast in first out ))的线性表,简称的线性表,简称LIFOLIFO 表表。。

图图4.14.1是栈结构的示意图。是栈结构的示意图。

Page 7:  第 4 章 栈和队列

4.1.1 4.1.1 栈的定义及基本操作栈的定义及基本操作(( 33 ))

33 、栈的七种基本操作、栈的七种基本操作 (( 11 )) INITSTACK (S)INITSTACK (S) 初始化空栈。初始化空栈。 (( 22 ) ) EMPTY (S)EMPTY (S) 判空栈函数,若判空栈函数,若 SS 为空栈,则函数值为为空栈,则函数值为

“真”或为“真”或为 11 ,否则为“假”或为,否则为“假”或为 00 。。 (( 33 ) ) PUSH (SPUSH (S ,, x)x) 进栈操作,在进栈操作,在 SS 栈顶插入一个元素栈顶插入一个元素 xx ,进,进

栈操作又称插入、压栈。栈操作又称插入、压栈。 (( 44 ) ) POP (S) POP (S) 出栈操作,在出栈操作,在 SS 栈顶删除一个元素,出栈操作栈顶删除一个元素,出栈操作

又称删除、弹栈。又称删除、弹栈。 (( 55 ) ) GETTOP (S)GETTOP (S) 取栈顶元素操作。取栈顶元素操作。 (( 66 ) ) CLEAR (S)CLEAR (S) 栈置空操作。栈置空操作。 (( 77 ) ) CURRENT_SIZE (S)CURRENT_SIZE (S) 求当前栈中元素个数的函数。求当前栈中元素个数的函数。 以上基本操作中,(以上基本操作中,( 11 ),(),( 33 ),(),( 44 ),(),( 66 )是加工型操)是加工型操

作,其他都是引用型操作。作,其他都是引用型操作。

Page 8:  第 4 章 栈和队列

图图 4.1 4.1 栈结构的示意图栈结构的示意图

Page 9:  第 4 章 栈和队列

4.1.2 4.1.2 栈的顺序存储结构栈的顺序存储结构(( 11 ))

11 、顺序存储结构的栈又称顺序栈,可用向量来实现。、顺序存储结构的栈又称顺序栈,可用向量来实现。 即利用一组地址连续的存储单元依次存放从栈底到栈顶的数据元素,即利用一组地址连续的存储单元依次存放从栈底到栈顶的数据元素,

栈底位置固定不变。可将栈底设在向量低下标的一端,因栈顶位置随栈底位置固定不变。可将栈底设在向量低下标的一端,因栈顶位置随着元素的进栈和出栈操作而变化,通常设一个指针着元素的进栈和出栈操作而变化,通常设一个指针 toptop 指向栈顶元素指向栈顶元素所在的位置。所在的位置。

注意,注意, toptop 不是指针类型的变量,而是整型变量,不是指针类型的变量,而是整型变量, toptop 总是指向栈顶总是指向栈顶元素在栈中的位置(序号)。元素在栈中的位置(序号)。

用用 CC 语言描述顺序栈的数据类型如下:语言描述顺序栈的数据类型如下:#define DATATYPE1 int#define DATATYPE1 int

#define MAXSIZE 100#define MAXSIZE 100

typedef structtypedef struct

{DATATYPE1 data{DATATYPE1 data [[ MAXSIZEMAXSIZE ]] ;;

int top;int top;

}SEQSTACK;}SEQSTACK;

Page 10:  第 4 章 栈和队列

4.1.2 4.1.2 栈的顺序存储结构栈的顺序存储结构(( 22 ))

22 、举例来说明:、举例来说明: 设栈底位置固定在向量的低端,通常习惯用设栈底位置固定在向量的低端,通常习惯用 top = 0top = 0 表示栈空,所表示栈空,所

以用以用 CC 语言描述顺序栈时,可约定向量中的语言描述顺序栈时,可约定向量中的 00 单元空闲不用。单元空闲不用。 假设假设 MAXSIZEMAXSIZE 为为 66 ,栈中可放入的最多元素个数是,栈中可放入的最多元素个数是 55 个,即个,即 s->s->

datadata [[ 11 ]至]至 s->datas->data [[ 55 ]。栈顶指针]。栈顶指针 s->tops->top 在元素进栈时作在元素进栈时作加加 11 运算;元素出栈时作减运算;元素出栈时作减 11 运算。运算。 toptop 指针是在栈上进行插入指针是在栈上进行插入或删除操作的依据。或删除操作的依据。

s->top = 0s->top = 0 表示表示栈空栈空,, s->top = MAXSIZE - 1 s->top = MAXSIZE - 1 表示表示栈满栈满,这是在,这是在顺序栈的基本操作中必须考虑到的两个重要条件。顺序栈的基本操作中必须考虑到的两个重要条件。

图图4.24.2说明了在顺序栈上进栈和出栈操作时,栈中元素和栈顶指针说明了在顺序栈上进栈和出栈操作时,栈中元素和栈顶指针的关系。的关系。 4.2(a)4.2(a) 是空栈状态;是空栈状态; 4.2(b)4.2(b) 是一个元素是一个元素 AA 入栈后的状态;入栈后的状态;4.2(c)4.2(c) 是栈满状态;是栈满状态; 4.2(d)4.2(d) 是在是在 (c)(c) 基础上删除一个元素后的状态。基础上删除一个元素后的状态。

Page 11:  第 4 章 栈和队列

4.1.2 4.1.2 栈的顺序存储结构栈的顺序存储结构(( 33 ))

33 、下面是在顺序栈上实现栈的几个基本操作所对应的算法。、下面是在顺序栈上实现栈的几个基本操作所对应的算法。 (( 11 ) 初始化空栈) 初始化空栈

void initstack(SEQSTACK *s)void initstack(SEQSTACK *s)

{ {

s->top = 0;s->top = 0;

}} (( 2)2) )判栈空)判栈空

int empty(SEQSTACK *s)int empty(SEQSTACK *s)

{ {

if(s->top == 0)if(s->top == 0)

return 1;return 1;

else else

return 0;return 0;

}}

Page 12:  第 4 章 栈和队列

4.1.2 4.1.2 栈的顺序存储结构栈的顺序存储结构(( 44 ))

(( 33 ) 进栈) 进栈int push(SEQSTACK *s, DATATYPE1 x)int push(SEQSTACK *s, DATATYPE1 x)

{ {

if(s->top == MAXSIZE - 1)if(s->top == MAXSIZE - 1)

{ {

printf(″Overflowprintf(″Overflow \\ n″);n″);

return 0;return 0;

}}

elseelse

{ {

s->top++;s->top++;

(s->data)(s->data) [[ s->tops->top ] ] = x;= x;

return 1;return 1;

}}

}}

Page 13:  第 4 章 栈和队列

4.1.2 4.1.2 栈的顺序存储结构栈的顺序存储结构(( 55 ))

(( 44 ) 出栈) 出栈DATATYPE1 pop(SEQSTACK *s)DATATYPE1 pop(SEQSTACK *s)

{{

DATATYPE1 x;DATATYPE1 x;

if(empty(s))if(empty(s))

{ printf(″Underflow{ printf(″Underflow \\ n″);n″);

x = NULL;}x = NULL;}

elseelse

{ x = (s->data){ x = (s->data) [[ s->tops->top ]] ;;

s->top--;}s->top--;}

return x;return x;

}}

Page 14:  第 4 章 栈和队列

4.1.2 4.1.2 栈的顺序存储结构栈的顺序存储结构(( 66 ))

(( 55 )取栈顶元素)取栈顶元素DATATYPE1 gettop(SEQSTACK *s)DATATYPE1 gettop(SEQSTACK *s)

{{

DATATYPE1 x;DATATYPE1 x;

if(empty(s))if(empty(s))

{printf(″Stack is empty.{printf(″Stack is empty. \\ n″);n″);

x = NULL;}x = NULL;}

elseelse

x = (s->data)x = (s->data) [[ s->tops->top ]] ;;

return x;return x;

}}

Page 15:  第 4 章 栈和队列

图图 4.2 4.2 顺序栈上进栈、出栈操作示意图顺序栈上进栈、出栈操作示意图

Page 16:  第 4 章 栈和队列

4.1.3 4.1.3 栈的链式存储结构栈的链式存储结构(( 11 ))

11 、栈也可以用单链表作为存储结构。用单链表作为、栈也可以用单链表作为存储结构。用单链表作为存储结构的栈称为存储结构的栈称为链栈链栈。链栈的数据类型说明如下: 。链栈的数据类型说明如下: #define DATATYPE2 char; #define DATATYPE2 char;

typedef struct snodetypedef struct snode

{DATATYPE2 data;{DATATYPE2 data;

struct snode *next;struct snode *next;

}LINKSTACK; }LINKSTACK;

toptop 是栈顶指针,它是指针类型变量,是栈顶指针,它是指针类型变量, toptop 惟一地确定一惟一地确定一个链栈。当个链栈。当 top = NULLtop = NULL 时,该链栈为空栈,链栈没有栈时,该链栈为空栈,链栈没有栈满的问题。链栈的示意图如满的问题。链栈的示意图如图图4.34.3所示。所示。

Page 17:  第 4 章 栈和队列

4.1.3 4.1.3 栈的链式存储结构栈的链式存储结构(( 22 ))

22 、链栈上元素进栈和出栈的算法如下、链栈上元素进栈和出栈的算法如下 (( 进栈和出栈都是基于栈顶指进栈和出栈都是基于栈顶指针针 toptop 的操作的操作 )) :: (( 11 ) 进栈) 进栈

LINKSTACK *pushstack(LINKSTACK *top, DATATYPE2 x)LINKSTACK *pushstack(LINKSTACK *top, DATATYPE2 x)

{ {

LINKSTACK *p;LINKSTACK *p;

p = malloc(sizeof(LINKSTACK));p = malloc(sizeof(LINKSTACK));

p->data = x;p->data = x;

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

top = p;top = p;

return p;return p;

}}

Page 18:  第 4 章 栈和队列

4.1.3 4.1.3 栈的链式存储结构栈的链式存储结构(( 33 ))

(( 22 )出栈 )出栈 LINKSTACK *popstack(LINKSTACK *top, DATATYPE2 *v)LINKSTACK *popstack(LINKSTACK *top, DATATYPE2 *v)

{{

LINKSTACK *p;LINKSTACK *p;

if(top == NULL)if(top == NULL)

printf(″Underflowprintf(″Underflow \\ n″);n″);

elseelse

{*v = top->data;{*v = top->data;

p = top;p = top;

top = top->next;top = top->next;

free(p);}free(p);}

return top;return top;

}}

Page 19:  第 4 章 栈和队列

图图 4.3 4.3 链栈示意图链栈示意图

Page 20:  第 4 章 栈和队列

4.2 4.2 队列队列 4.2.1 4.2.1 队列的定义及基本操作队列的定义及基本操作

4.2.2 4.2.2 队列的顺序存储结构队列的顺序存储结构

4.2.3 4.2.3 队列的链式存储结构队列的链式存储结构

Page 21:  第 4 章 栈和队列

4.2.1 4.2.1 队列的定义及基本操作队列的定义及基本操作(( 11 ))

11 、队列的定义、队列的定义 队列队列 (queue)(queue) 也是一种特殊的线性表。它也是一种特殊的线性表。它所有的插入操作所有的插入操作

均限定在表的一端进行,而均限定在表的一端进行,而所有的删除所有的删除操作则限定在表操作则限定在表的另一端进行。允许删除操作的一端称为的另一端进行。允许删除操作的一端称为队头队头 (front)(front) ,,允许插入操作的一端称为允许插入操作的一端称为队尾队尾 (rear)(rear) 。。

上述规定决定了先进队列的元素先出队列。就如我们平上述规定决定了先进队列的元素先出队列。就如我们平时排队买东西一样。因此队列又称作时排队买东西一样。因此队列又称作先进先出(先进先出( first in fifirst in fi

rst outrst out )的线性表)的线性表,,简称简称 FIFOFIFO 表表。。

Page 22:  第 4 章 栈和队列

4.2.1 4.2.1 队列的定义及基本操作队列的定义及基本操作(( 22 ))

22 、队列的结构、队列的结构 假设有队列假设有队列 Q =Q = (( aa11, a, a22, ...,a, ...,ann ),则队列中的元),则队列中的元

素是按素是按 aa11, a, a22, ...,a, ...,ann 的次序进队,而第一个出队列的元素的次序进队,而第一个出队列的元素是是 aa11 ,第二个出队列的是,第二个出队列的是 aa22 ,只有在,只有在 aai-1i-1 出队列后,出队列后, aaii

才可以出队列(才可以出队列( 1 ≤ i ≤ n1 ≤ i ≤ n )。)。 当队列中没有元素时称为空队列。队列的示意图如当队列中没有元素时称为空队列。队列的示意图如 4.44.4所所

示。示。 33 、队列的七种基本操作、队列的七种基本操作

(( 11 ) ) INITQUEUEINITQUEUE (( QQ )初始化空队列。)初始化空队列。

Page 23:  第 4 章 栈和队列

4.2.1 4.2.1 队列的定义及基本操作队列的定义及基本操作(( 33 ))

(( 22 ) ) EMPTYEMPTY (( QQ )判队空函数,若)判队空函数,若 QQ 为空栈,则函数返为空栈,则函数返回值为“真”或为回值为“真”或为 11 ,否则为“假”或为,否则为“假”或为 00 。。

(( 33 )) ADDQADDQ (( QQ ,, xx )入队列操作,在队尾插入一个元素)入队列操作,在队尾插入一个元素 xx 。。 (( 44 )) DELQDELQ (( QQ )出队列操作,在队头删除一个元素。)出队列操作,在队头删除一个元素。 (( 55 ) ) GETFRONTGETFRONT (( QQ )取队头元素操作。)取队头元素操作。 (( 66 ) ) CLEARCLEAR (( QQ )队列置空操作。)队列置空操作。 (( 77 ) ) CURRENT_SIZECURRENT_SIZE (( QQ )求队列中元素的个数的函数。)求队列中元素的个数的函数。 以上基本操作中,(以上基本操作中,( 11 ),(),( 33 ),(),( 44 ),(),( 66 )是加工型)是加工型

操作,其他都是引用型操作。操作,其他都是引用型操作。

Page 24:  第 4 章 栈和队列

图图 4.4 4.4 队列结构示意图队列结构示意图

Page 25:  第 4 章 栈和队列

4.2.2 4.2.2 队列的顺序存储结构队列的顺序存储结构(( 11 ))

11 、顺序存储结构的队列称为顺序队列。通常用一个向量、顺序存储结构的队列称为顺序队列。通常用一个向量空间来存放顺序队列的元素。空间来存放顺序队列的元素。 由于队列的队头和队尾的位置是在动态变化的,因此要设两个由于队列的队头和队尾的位置是在动态变化的,因此要设两个

指针分别指向当前队头元素和队尾元素在向量中的位置。这两指针分别指向当前队头元素和队尾元素在向量中的位置。这两个指针分别为队头指针个指针分别为队头指针 frontfront 和队尾指针和队尾指针 rearrear 。这两个指针都。这两个指针都是整型变量。用是整型变量。用 CC 语言描述顺序队列的数据类型说明如下:语言描述顺序队列的数据类型说明如下:

#define DATATYPE1 int#define DATATYPE1 int

#define MAXSIZE 100#define MAXSIZE 100

typedef structtypedef struct

{DATATYPE1 data{DATATYPE1 data [[ MAXSIZEMAXSIZE ]] ;;

int front, rear;int front, rear;

}SEQQUEUE;}SEQQUEUE;

Page 26:  第 4 章 栈和队列

4.2.2 4.2.2 队列的顺序存储结构队列的顺序存储结构(( 22 ))

为实现基本操作,我们约定头指针为实现基本操作,我们约定头指针 frontfront 总是指向队列中第一个元素总是指向队列中第一个元素的前一个单元位置,而尾指针的前一个单元位置,而尾指针 rearrear 总是指向队列最后一个元素的所在总是指向队列最后一个元素的所在位置。位置。假设假设 MAXSIZEMAXSIZE 为为 66 ,队列中可放入的最多元素个数是,队列中可放入的最多元素个数是 66 个,个,即即 q->dataq->data [[ 00 ]至]至 q->dataq->data [[ 55 ]。]。初始化时,头指针和尾指针都初始化时,头指针和尾指针都指向向量空间下界的下一个位置:指向向量空间下界的下一个位置:

q->rear = q->front = -1q->rear = q->front = -1 。当。当 q->rear = MAXSIZE - 1q->rear = MAXSIZE - 1 时,新元素就不时,新元素就不能再加入队列了;当能再加入队列了;当 q->rear = q->frontq->rear = q->front 时,表示队列中没有元素,时,表示队列中没有元素,为队空。为队空。

把上面的各种情况汇总为下面的几个条件,这是实现顺序队列基本操把上面的各种情况汇总为下面的几个条件,这是实现顺序队列基本操作的重要原则:作的重要原则:

队列的初始化条件:队列的初始化条件: q->rear = q->front = -1q->rear = q->front = -1

队满条件:队满条件: q->rear = MAXSIZE - 1q->rear = MAXSIZE - 1

队空条件:队空条件: q->front = q->rearq->front = q->rear

Page 27:  第 4 章 栈和队列

4.2.2 4.2.2 队列的顺序存储结构队列的顺序存储结构(( 33 ))

假设假设 MAXSIZEMAXSIZE 为为 66 ,队列中可放入的元素最多,队列中可放入的元素最多 66 个,图个,图 4.54.5

说明了在顺序队列中进行元素出队和元素入队操作时,队列中说明了在顺序队列中进行元素出队和元素入队操作时,队列中的元素及队列头指针和尾指针的变化情况。图的元素及队列头指针和尾指针的变化情况。图 4.5(a)4.5(a) 表示该队表示该队列的初始状态为空。列的初始状态为空。

图图 4.5(b)4.5(b) 表示有三个元素表示有三个元素 AA ,, BB ,, CC相继加入队列,队尾指相继加入队列,队尾指针发生变化,队头指针不变。图针发生变化,队头指针不变。图 4.5(c)4.5(c) 表示表示 AA ,, BB ,, CC 元素元素先后出队列,队头指针发生变化,队尾指针不变。队头队尾指先后出队列,队头指针发生变化,队尾指针不变。队头队尾指针碰上时,此队列中无元素。图针碰上时,此队列中无元素。图 4.5(d)4.5(d) 表示表示 DD ,, EE ,, FF 元素元素相继加入队列,此时如有新元素要入队列则因队列已满,即相继加入队列,此时如有新元素要入队列则因队列已满,即 q-q-

>rear = MAXSIZE - 1>rear = MAXSIZE - 1 条件出现,而不能再进行插入元素的操条件出现,而不能再进行插入元素的操作了。显然,当前队列中的元素的个数是作了。显然,当前队列中的元素的个数是 (q->rear) - (q->front)(q->rear) - (q->front) 。。

Page 28:  第 4 章 栈和队列

4.2.2 4.2.2 队列的顺序存储结构队列的顺序存储结构(( 44 ))

按上面的说明,可将队列中插入元素的操作描述为:按上面的说明,可将队列中插入元素的操作描述为:if (q->rear == MAXSIZE - 1) if (q->rear == MAXSIZE - 1)

printf(″queue full″);printf(″queue full″);

else else

{ q->rear ++; q->data{ q->rear ++; q->data [[ q->rearq->rear ] ] = x ; }= x ; } 队列中删除元素的操作描述为:队列中删除元素的操作描述为:

if (q->front == q-> rear)if (q->front == q-> rear)

printf (″queue empty″);printf (″queue empty″);

else q->front++;else q->front++; 上面的顺序队列在操作中存在着如下问题:例如当上面的顺序队列在操作中存在着如下问题:例如当 q->front = q->front =

q->rear = 4q->rear = 4 时,队列为空,但插入新元素只能从当前时,队列为空,但插入新元素只能从当前 rearrear 指指针指向的位置开始插入,前面的单元无法利用。针指向的位置开始插入,前面的单元无法利用。

Page 29:  第 4 章 栈和队列

4.2.2 4.2.2 队列的顺序存储结构队列的顺序存储结构(( 55 ))

更有甚者,当更有甚者,当 q->rear = MAXSIZE - 1q->rear = MAXSIZE - 1 时,队满条件出现,不时,队满条件出现,不能再插入新元素,但当前队列可能并不满,而是假满现象。又能再插入新元素,但当前队列可能并不满,而是假满现象。又如当如当 q->front = q->rear = MAXSIZE - 1 q->front = q->rear = MAXSIZE - 1 时,虽为队空,但不时,虽为队空,但不能再插入新元素,要做一次队列置空操作后才能工作。能再插入新元素,要做一次队列置空操作后才能工作。

产生这些现象的原因是被删除元素的空间在该元素删除以后就产生这些现象的原因是被删除元素的空间在该元素删除以后就永远使用不到了。解决这一问题的常用方法是:将向量永远使用不到了。解决这一问题的常用方法是:将向量 q->datq->dat

aa 从逻辑上构筑成一个头尾相接的圆环,即从逻辑上构筑成一个头尾相接的圆环,即 q->dataq->data [[ 00 ]接]接在在 q->dataq->data [[ MAXSIZE - 1MAXSIZE - 1 ]之后,并将这种逻辑含义下的向]之后,并将这种逻辑含义下的向量称为循环向量,此队列称为循环队列,如量称为循环向量,此队列称为循环队列,如图图4.64.6所示。循环队所示。循环队列仍然是顺序队列结构,只是逻辑上和前面的顺序队列有所不列仍然是顺序队列结构,只是逻辑上和前面的顺序队列有所不同。同。

Page 30:  第 4 章 栈和队列

4.2.2 4.2.2 队列的顺序存储结构队列的顺序存储结构(( 66 ))

循环队列上的操作还是基于头指针循环队列上的操作还是基于头指针 q->rear q->rear 和尾指针和尾指针 q->frontq->front ,,当在当在 q->rear = MAXSIZE - 1 q->rear = MAXSIZE - 1 情况下做插入操作时,就能用到情况下做插入操作时,就能用到前面已被删除的元素空间了,克服了假满的现象。在循环队列前面已被删除的元素空间了,克服了假满的现象。在循环队列中做插入元素操作时,前面算法段中 中做插入元素操作时,前面算法段中 q->rear++ q->rear++ 语句应改成:语句应改成:

if (q->rear+1 == MAXSIZE) if (q->rear+1 == MAXSIZE)

q->rear = 0;q->rear = 0;

else q->rear++;else q->rear++;

或改成更简洁的描述为:或改成更简洁的描述为: q->rear = (q->rear + 1) % MAXSIZE ;q->rear = (q->rear + 1) % MAXSIZE ;

同样,在循环队列中删除元素操作时前面算法段中同样,在循环队列中删除元素操作时前面算法段中 q->front++ q->front++

语句应改成:语句应改成:

Page 31:  第 4 章 栈和队列

4.2.2 4.2.2 队列的顺序存储结构队列的顺序存储结构(( 77 ))

if (q->front+1 == MAXSIZE)if (q->front+1 == MAXSIZE)

q->front = 0;q->front = 0;

else q->front++;else q->front++;

或改成更简洁的描述为:或改成更简洁的描述为: q->front = (q->front + 1) % MAXSIZE ;q->front = (q->front + 1) % MAXSIZE ;

上述方法解决了顺序队列假满的问题,但新的问题出现了:队满条件上述方法解决了顺序队列假满的问题,但新的问题出现了:队满条件不再是不再是 q->rear = MAXSIZE - 1q->rear = MAXSIZE - 1 ,而变成,而变成 q->front = q->rearq->front = q->rear 和队空条和队空条件一样了。从不断插入元素的角度看尾指针件一样了。从不断插入元素的角度看尾指针 q->rearq->rear 不断加不断加 11 ,当尾,当尾指针和头指针相遇而相等时,则队列满;从不断删除元素的角度看头指针和头指针相遇而相等时,则队列满;从不断删除元素的角度看头指针指针 q->frontq->front 不断加不断加 11 ,当头指针和尾指针相遇而相等时,则队列空;,当头指针和尾指针相遇而相等时,则队列空;所以队满条件和队空条件在循环队列中是一样的:所以队满条件和队空条件在循环队列中是一样的: q->rear = q->frontq->rear = q->front ,,而算法是无法分清该等式是表示队满还是表示队空。而算法是无法分清该等式是表示队满还是表示队空。

Page 32:  第 4 章 栈和队列

4.2.2 4.2.2 队列的顺序存储结构队列的顺序存储结构(( 88 ))

图图4.74.7给出了循环队列插入、删除元素的操作过程,也说明了新问给出了循环队列插入、删除元素的操作过程,也说明了新问题发生的过程。图题发生的过程。图 4.7(a)4.7(a) 是循环队列的一般情况。图是循环队列的一般情况。图 4.7(b)4.7(b) 是在图是在图4.7 (a)4.7 (a) 情况下不断有元素入队列,当情况下不断有元素入队列,当 q->rear = q->frontq->rear = q->front 时,则队时,则队满情况出现。图满情况出现。图 4.7(c) 4.7(c) 是在图是在图 4.7 (a)4.7 (a) 情况下不断有元素出队列,情况下不断有元素出队列,当当 q->front = q->rearq->front = q->rear 时,则队空情况出现。上述队满和队空情况时,则队空情况出现。上述队满和队空情况出现的过程不同,但队满和队空情况的结果相同。出现的过程不同,但队满和队空情况的结果相同。

解决新问题的方法有很多种。一种简单的解决方法是:损失一个单解决新问题的方法有很多种。一种简单的解决方法是:损失一个单元不用,即当循环队列中元素的个数是元不用,即当循环队列中元素的个数是 MAXSIZE - 1MAXSIZE - 1 时就认为队时就认为队满了,这样一来,队满的条件就变成:(满了,这样一来,队满的条件就变成:( q->rear + 1q->rear + 1 )) % MAXSI% MAXSI

ZE = q->frontZE = q->front 。队空条件不变,仍是:。队空条件不变,仍是: q->front = q->rearq->front = q->rear 。。

Page 33:  第 4 章 栈和队列

4.2.2 4.2.2 队列的顺序存储结构队列的顺序存储结构(( 99 ))

现把上面约定的方法汇总为循环队列基本操作的重要原则:现把上面约定的方法汇总为循环队列基本操作的重要原则: 循环队列的初始化条件:循环队列的初始化条件: q->rear == q->front == 0q->rear == q->front == 0 循环队满条件:循环队满条件: (q->rear + 1) % MAXSIZE == q->front(q->rear + 1) % MAXSIZE == q->front 循环队空条件:循环队空条件: q->front == q->rearq->front == q->rear

下面是在上述约定下循环队列上几个基本操作的算法:下面是在上述约定下循环队列上几个基本操作的算法: (( 11 )判队列空)判队列空

int empty(SEQQUEUE *q)int empty(SEQQUEUE *q)

{{

if(q->rear == q->front)if(q->rear == q->front)

return 1;return 1;

elseelse

return 0;return 0;

}}

Page 34:  第 4 章 栈和队列

4.2.2 4.2.2 队列的顺序存储结构(队列的顺序存储结构( 1100 ))

(( 22 ) 取队头元素) 取队头元素DATATYPE1 getfront(SEQQUEUE *q)DATATYPE1 getfront(SEQQUEUE *q)

{{

DATATYPE1 v;DATATYPE1 v;

if (empty(q))if (empty(q))

{ printf(″ Queue is empty. { printf(″ Queue is empty. \\ n ″);n ″);

v = NULL;}v = NULL;}

elseelse

v = q->data [(q->front + 1) % MAXSIZE];v = q->data [(q->front + 1) % MAXSIZE];

return v;return v;

}}

Page 35:  第 4 章 栈和队列

4.2.2 4.2.2 队列的顺序存储结构(队列的顺序存储结构( 1111 ))

(( 33 )队尾插入元素)队尾插入元素int enqueue(SEQQUEUE *qint enqueue(SEQQUEUE *q ,, DATATYPE1 x)DATATYPE1 x)

{{

int r;int r;

if(q->front == (q->rear + 1) % MAXSIZE)if(q->front == (q->rear + 1) % MAXSIZE)

{ printf(″ Queue is full. { printf(″ Queue is full. \\ n ″);n ″);

r = 0;}r = 0;}

elseelse

{ q->rear = (q->rear + 1) % MAXSIZE;{ q->rear = (q->rear + 1) % MAXSIZE;

(q->data)(q->data) [[ q->rearq->rear ] ] = x;= x;

r = 1;}r = 1;}

return r;return r;

}}

Page 36:  第 4 章 栈和队列

4.2.2 4.2.2 队列的顺序存储结构(队列的顺序存储结构( 1122 ))

(( 44 )队头删除元素)队头删除元素DATATYPE1 dequeue(SEQQUEUE *q)DATATYPE1 dequeue(SEQQUEUE *q)

{{

DATATYPE1 v;DATATYPE1 v;

if(empty(q))if(empty(q))

{printf(″ Queue is empty. {printf(″ Queue is empty. \\ n ″);n ″);

v = NULL;}v = NULL;}

elseelse

{ q->front = (q->front + 1) % MAXSIZE;{ q->front = (q->front + 1) % MAXSIZE;

v = (q->data)v = (q->data) [[ q->frontq->front ]] ; }; }

return v;return v;

}}

Page 37:  第 4 章 栈和队列

图图 4.5 4.5 顺序队列上元素入队列、出队列操作示意图顺序队列上元素入队列、出队列操作示意图

55

44

33

22

11

00

55

44

33

CC 22

BB 11

AA 00

FF 55

EE 44

DD 33

22

11

00q->rear

q->front

q->rearq->front

55

44

33

22

11

00

q->rear

q->front

q->frontq->rear

(a) (b) (c) (d)

Page 38:  第 4 章 栈和队列

图图 4.6 4.6 循环队列示意图循环队列示意图

Page 39:  第 4 章 栈和队列

图图 4.7 4.7 循环队列元素插入、删除过程示意图循环队列元素插入、删除过程示意图

Page 40:  第 4 章 栈和队列

4.2.3 4.2.3 队列的链式存储结构队列的链式存储结构(( 11 ))

可用链式存储结构表示队列,简称为可用链式存储结构表示队列,简称为链队列链队列。一个链队列需要一个头。一个链队列需要一个头指针和一个尾指针才能惟一确定。队列中元素的结构和前面单链表中指针和一个尾指针才能惟一确定。队列中元素的结构和前面单链表中结点的结构一样。链队列的数据类型说明如下:结点的结构一样。链队列的数据类型说明如下:#define DATATYPE1 int;#define DATATYPE1 int;

typedef struct qnodetypedef struct qnode

{ DATATYPE1 data;{ DATATYPE1 data;

struct qnode *next;struct qnode *next;

}LINKQLIST;}LINKQLIST;

typedef struct typedef struct

{{

LINKQLIST *front, *rear;LINKQLIST *front, *rear;

}LINKQUEUE;}LINKQUEUE;

Page 41:  第 4 章 栈和队列

4.2.3 4.2.3 队列的链式存储结构队列的链式存储结构(( 22 ))

LINKQUEUELINKQUEUE 类型说明中的两个分量均为指针变量,分别为类型说明中的两个分量均为指针变量,分别为链队列的链队列的头指针和尾指针头指针和尾指针。为了操作方便,。为了操作方便,在队首元素前附加一个头结点,队在队首元素前附加一个头结点,队列的头指针就指向头结点列的头指针就指向头结点。。图图4.84.8是一个链队列的示意图,图是一个链队列的示意图,图 4.94.9 给出给出了在链队列上插入元素和删除元素的示意图。链队列无队满问题。了在链队列上插入元素和删除元素的示意图。链队列无队满问题。

在链队列上的几个基本操作的算法如下:在链队列上的几个基本操作的算法如下: (( 11 )链队列初始化)链队列初始化

void initlinkqueue(LINKQUEUE *q)void initlinkqueue(LINKQUEUE *q)

{{

q->front = malloc(sizeof(LINKQLIST));q->front = malloc(sizeof(LINKQLIST));

(q->front)->next = NULL;(q->front)->next = NULL;

q->rear = q->front;q->rear = q->front;

}}

Page 42:  第 4 章 栈和队列

4.2.3 4.2.3 队列的链式存储结构队列的链式存储结构(( 33 ))

(( 22 ) 判链队列空) 判链队列空int emptylinkqueue(LINKQUEUE *q)int emptylinkqueue(LINKQUEUE *q)

{{

int v;int v;

if(q->front == q->rear)if(q->front == q->rear)

v = 1;v = 1;

elseelse

v = 0;v = 0;

return v;return v;

}}

Page 43:  第 4 章 栈和队列

4.2.3 4.2.3 队列的链式存储结构队列的链式存储结构(( 44 ))

(( 33 )读链队列队首元素)读链队列队首元素DATATYPE1 getlinkfront(LINKQUEUE *q)DATATYPE1 getlinkfront(LINKQUEUE *q)

{{

DATATYPE1 v;DATATYPE1 v;

if(emptylinkqueue(q))if(emptylinkqueue(q))

v = NULL;v = NULL;

elseelse

v = (q->front)->next->data;v = (q->front)->next->data;

return v;return v;

}}

Page 44:  第 4 章 栈和队列

4.2.3 4.2.3 队列的链式存储结构队列的链式存储结构(( 55 ))

(( 44 )元素插入链队列)元素插入链队列void enlinkqueue(LINKQUEUE *qvoid enlinkqueue(LINKQUEUE *q ,, DATATYPE1 x)DATATYPE1 x)

{{

(q->rear)->next = malloc(sizeof(LINKQLIST));(q->rear)->next = malloc(sizeof(LINKQLIST));

q->rear = (q->rear)->next;q->rear = (q->rear)->next;

(q->rear)->data = x;(q->rear)->data = x;

(q->rear)->next = NULL;(q->rear)->next = NULL;

}}

Page 45:  第 4 章 栈和队列

4.2.3 4.2.3 队列的链式存储结构队列的链式存储结构(( 66 ))

(( 55 ) 从链队列中删除元素) 从链队列中删除元素 若当前链队列的长度大于若当前链队列的长度大于 11 ,则删除队首元素的操作只要修改,则删除队首元素的操作只要修改

头结点的指针即可。若当前链队列的长度等于头结点的指针即可。若当前链队列的长度等于 11 ,则删除队首,则删除队首元素时,除修改头结点的指针域外,还应该修改尾指针,因为元素时,除修改头结点的指针域外,还应该修改尾指针,因为队尾元素已被删除。图队尾元素已被删除。图 4.104.10 是当链队列中只有一个元素时删是当链队列中只有一个元素时删除该元素时指针变化的示意图。除该元素时指针变化的示意图。

把一般情况和特殊情况考虑在一起,得到下面的算法:把一般情况和特殊情况考虑在一起,得到下面的算法:

Page 46:  第 4 章 栈和队列

4.2.3 4.2.3 队列的链式存储结构队列的链式存储结构(( 77 ))

DATATYPE1 dellinkqueue(LINKQUEUE *q)DATATYPE1 dellinkqueue(LINKQUEUE *q)

{{

LINKQLIST *p;LINKQLIST *p;

DATATYPE1 v;DATATYPE1 v;

if(emptylinkqueue(q))if(emptylinkqueue(q))

{ printf(″Queue is empty.{ printf(″Queue is empty. \\ n″);n″);

v = NULL;}v = NULL;}

elseelse

{ p = (q->front)->next;{ p = (q->front)->next;

(q->front)->next = p->next;(q->front)->next = p->next;

if(p->next == NULL)if(p->next == NULL)

q->rear = q->front;q->rear = q->front;

v = p->data;v = p->data;

free(p); }free(p); }

return v;return v;

}}

Page 47:  第 4 章 栈和队列

图图 4.8 4.8 链队列示意链队列示意

Page 48:  第 4 章 栈和队列

图图 4.9 4.9 链队列元素插入、删除过程示意图链队列元素插入、删除过程示意图

Page 49:  第 4 章 栈和队列

图图 4-10 4-10 链队列长度为链队列长度为 11 时元素出队列过程示意图时元素出队列过程示意图

Page 50:  第 4 章 栈和队列

4.3 4.3 应用举例及分析应用举例及分析(( 11 ))

例例 4.14.1 队列管理的模拟算法队列管理的模拟算法 (( 队列采用带头结点的链表结队列采用带头结点的链表结构构 )) 。。采用如下管理模式:采用如下管理模式:队列初始化为空队列;队列初始化为空队列;键盘键入奇数时:奇数从队尾入队列;键盘键入奇数时:奇数从队尾入队列;键盘键入偶数时:队头指针指向的奇数出队列;键盘键入偶数时:队头指针指向的奇数出队列;键盘键入键盘键入 00 时: 退出算法;时: 退出算法;每键入一整数,显示操作后队列中的值。每键入一整数,显示操作后队列中的值。

Page 51:  第 4 章 栈和队列

4.3 4.3 应用举例及分析应用举例及分析(( 22 ))

void outlinkqueue(LINKQUEUE *q)void outlinkqueue(LINKQUEUE *q)

// // 显示队列中的值显示队列中的值{{ LINKQLIST *p;LINKQLIST *p;

p = q->front;p = q->front;

printf(″Queue : ″);printf(″Queue : ″);

while(p != q->rear)while(p != q->rear)

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

printf(″%d ″,p->data);}printf(″%d ″,p->data);}

printf(″printf(″ \\ n″);n″);

}}

main( )main( )

{{

LINKQUEUE lq, *p;LINKQUEUE lq, *p;

int j; int j;

Page 52:  第 4 章 栈和队列

4.3 4.3 应用举例及分析应用举例及分析(( 33 ))

p = &lq;p = &lq;

initlinkqueue(p);initlinkqueue(p);

printf(″Input a integer : ″);printf(″Input a integer : ″);

scanf(″%d″, &j);scanf(″%d″, &j);

while(j != 0)while(j != 0)

{if(j % 2 == 1){if(j % 2 == 1)

enlinkqueue(p, j);// enlinkqueue(p, j);// 输入奇数:奇数入队列输入奇数:奇数入队列 elseelse

j = dellinkqueue(p); // j = dellinkqueue(p); // 输入偶数:队头奇数出队列输入偶数:队头奇数出队列 outlinkqueue(p);outlinkqueue(p);

printf(″printf(″ \\ nInput a integer : ″);nInput a integer : ″);

scanf(″%d″, &j);scanf(″%d″, &j);

}}

}}

Page 53:  第 4 章 栈和队列

习 题习 题 4.14.1  简述栈和线性表的区别。 简述栈和线性表的区别。 4.24.2  简述栈和队列这两种数据结构的相同点和不同点。 简述栈和队列这两种数据结构的相同点和不同点。 4.34.3  如果进栈的元素序列为 如果进栈的元素序列为 AA ,, BB ,, CC ,, DD ,则可能,则可能

得到的出栈序列有多少种?写出全部的可能序列。得到的出栈序列有多少种?写出全部的可能序列。 4.44.4  如果进栈的元素序列为 如果进栈的元素序列为 11 ,, 22 ,, 33 ,, 44 ,, 55 ,, 66 ,,

能否得到能否得到 44 ,, 33 ,, 55 ,, 66 ,, 11 ,, 22 和和 11 ,, 33 ,, 55 ,, 44 ,, 22 ,,66 的出栈序列?并说明为什么不能得到或如何得到。的出栈序列?并说明为什么不能得到或如何得到。

4.54.5  写一算法将一顺序栈中的元素依次取出,并打印元 写一算法将一顺序栈中的元素依次取出,并打印元素值。素值。