41
第3第 第第第第

第 3 章 栈和队列

  • Upload
    myrna

  • View
    144

  • Download
    0

Embed Size (px)

DESCRIPTION

第 3 章 栈和队列. 2. 3.1 栈. 栈的逻辑结构. 栈的顺序存储结构. 栈的链式存储结构. 3. 3.1.1 栈的逻辑结构. 1. 栈的定义. 栈 (stack) 是限定仅在 表尾 进行插入或删除操作的线性表,因此,对于栈来说,表尾端有其特殊的含义。. - PowerPoint PPT Presentation

Citation preview

Page 1: 第 3 章  栈和队列

第 3 章 栈和队列

Page 2: 第 3 章  栈和队列

栈的逻辑结构栈的逻辑结构

2

栈的顺序存储结构栈的顺序存储结构栈的链式存储结构栈的链式存储结构

3.1 3.1 栈栈

Page 3: 第 3 章  栈和队列

栈 栈 (stack) (stack) 是限定仅在是限定仅在表尾表尾进行插入或删除操作的线性进行插入或删除操作的线性表,因此,对于栈来说,表尾端有其特殊的含义。表,因此,对于栈来说,表尾端有其特殊的含义。

在栈中,插入元素叫作在栈中,插入元素叫作入栈入栈,删除元素叫作,删除元素叫作出栈出栈。通常。通常将允许进行插入或删除操作的一端称为将允许进行插入或删除操作的一端称为栈顶 栈顶 (top)(top) ,栈顶将随着,栈顶将随着栈中的数据元素的增减而浮动,通过栈顶指针指明当前元素的位栈中的数据元素的增减而浮动,通过栈顶指针指明当前元素的位置;另一端称为置;另一端称为栈底 栈底 (bottom)(bottom) ,栈底指针并不随着栈中的元素,栈底指针并不随着栈中的元素的增减而移动,栈底是固定的。不含元素的空表称为空栈。的增减而移动,栈底是固定的。不含元素的空表称为空栈。

3

3.1.1 3.1.1 栈的逻辑结构栈的逻辑结构 1. 1. 栈的定义栈的定义

Page 4: 第 3 章  栈和队列
Page 5: 第 3 章  栈和队列

aann

aa22

aa11

栈顶栈顶

栈底栈底

出栈出栈 进栈进栈

(a) (a) 非空栈非空栈 (b) (b) 空栈空栈

5

Page 6: 第 3 章  栈和队列

6

2. 2. 栈的基本操作栈的基本操作 InitStack (S )InitStack (S )

操作结果:构造一个空栈 操作结果:构造一个空栈 SS 。。

Push (S, x)Push (S, x)

初始条件:栈 初始条件:栈 SS 已经存在。已经存在。操作结果:插入元素 操作结果:插入元素 xx 为新的栈顶元素。为新的栈顶元素。

Pop (S,x)Pop (S,x)

初始条件:栈 初始条件:栈 SS 已经存在且非空。已经存在且非空。操作结果:删除栈操作结果:删除栈 SS 栈顶元素,并用栈顶元素,并用 xx 返回其值。返回其值。

Page 7: 第 3 章  栈和队列

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

指示栈顶元素在顺序栈中的位置。指示栈顶元素在顺序栈中的位置。

7

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

Page 8: 第 3 章  栈和队列

1. 1. 栈的顺序存储表示栈的顺序存储表示

# define STACK_SIZE 50; # define STACK_SIZE 50; /* /* 存储空间初始分配量存储空间初始分配量 */*/

typedef struct typedef struct /* /* 顺序栈存储结构定义顺序栈存储结构定义 */*/

{{

StackElementType elem[Stack_Size];StackElementType elem[Stack_Size]; /* /* 用来存放栈中元素的 一维数组用来存放栈中元素的 一维数组 **

//

int top; int top; /* /* 栈顶指针栈顶指针 ,top,top 为为 -1-1 表示空表示空栈栈 */*/

} SeqStack ;} SeqStack ; /* /* 顺序栈的类型名顺序栈的类型名 */*/

8

Page 9: 第 3 章  栈和队列

S->S->top top 称为栈顶指针,初值为称为栈顶指针,初值为 -1-1, , 可作为栈空的标记。可作为栈空的标记。假定 假定 1 1 个元素占 个元素占 1 1 个存储单元:当插入新元素时,个存储单元:当插入新元素时,先先使指针 使指针 S->S->top top 增 增 11 再再插入;当删除元素时,先插入;当删除元素时,先删删除元素再除元素再使指针 使指针 S->S->top top 减 减 1 1 ,因此,非空栈中的栈,因此,非空栈中的栈顶指针顶指针 toptop 始终指向栈顶元素。 始终指向栈顶元素。 栈顶指针的初值也可栈顶指针的初值也可以为以为 00 。此时:当插入新元素时,先插入再使指针。此时:当插入新元素时,先插入再使指针 S->S->

toptop 增增 11 ;删除元素时,先使指针;删除元素时,先使指针 S->topS->top 减减 11 ,再删,再删除。此时,栈中的栈顶指针始终指向当前栈顶元素的除。此时,栈中的栈顶指针始终指向当前栈顶元素的下一个位置。下一个位置。

2. 2. 栈的初始化操作栈的初始化操作

9

Page 10: 第 3 章  栈和队列

3. 3. 基本操作在顺序栈上的实现基本操作在顺序栈上的实现

void InitStack ( SeqStack *S ) /* void InitStack ( SeqStack *S ) /* 构造一个空栈 构造一个空栈 S*/S*/

{{ S.top = -1 S.top = -1 ;;

return OK;return OK;

} /* InitStack*/} /* InitStack*/

10

(1) (1) 初始化初始化

InitStack InitStack 算法的时间复杂性为 算法的时间复杂性为 OO(1)(1) 。。

Page 11: 第 3 章  栈和队列

Status GetTop ( SeqStack *S, StackElementType *x) Status GetTop ( SeqStack *S, StackElementType *x) /* /* 如果栈空,如果栈空,则返回 则返回 FALSE*//* FALSE*//* 如果栈不空,则用 如果栈不空,则用 xx 返回 返回 SS 的栈顶元素,并返回 的栈顶元素,并返回 TRUETRUE 。。 */*/

(2) (2) 取栈顶元素取栈顶元素

{{ if ( S->top = = -1) return FALSE; if ( S->top = = -1) return FALSE; /* /* 如果栈 如果栈 SS 为空,则返回为空,则返回 FAFA

LSE*/LSE*/

11

else else

{ *x= S->elem[ S->top];{ *x= S->elem[ S->top];

/*/* 栈顶指针所指向的单元内的值赋给栈顶指针所指向的单元内的值赋给 xx 指向的单元中指向的单元中 */*/

return (TRUE);return (TRUE);

}}

} /* GetTop*/} /* GetTop*/

GetTop GetTop 算法的时间复杂性为 算法的时间复杂性为 OO(1)(1)

Page 12: 第 3 章  栈和队列

(3) (3) 将元素弹出栈将元素弹出栈int Pop ( SeqStack *S, StackElementType *x ) int Pop ( SeqStack *S, StackElementType *x ) /*/* 若栈空,则返回 若栈空,则返回 FF

ASLE*/ASLE*/ ;; /* /* 若栈不空,将栈若栈不空,将栈 SS 的栈顶元素弹出,放到的栈顶元素弹出,放到 xx 所指的存储空间中所指的存储空间中 */*/

{{ if ( S if ( S ->-> top ==-1top ==-1 ) ) return FALSE;return FALSE;

/* /* 如果栈 如果栈 SS 为空,则返回为空,则返回 FALSE*/FALSE*/

else else

{{

*x=S->elem[S->top]; *x=S->elem[S->top]; /*/* 将栈顶指针所指单元内的值赋给 将栈顶指针所指单元内的值赋给 e*/e*/

S->top--; S->top--; /* /* 栈顶指针减 栈顶指针减 1*/1*/

}} return OK;return OK;

}/* Pop*/}/* Pop*/

12

PopPop 算法的时间复杂性为 算法的时间复杂性为 OO(1)(1)

Page 13: 第 3 章  栈和队列

Int Push ( SeqStack *S; StackElementType x)Int Push ( SeqStack *S; StackElementType x)/* /* 插入元素 插入元素 xx 为新的栈顶元素为新的栈顶元素 */*/

{{

if ( S->top ==Stack_Size-1) return(FALSE);if ( S->top ==Stack_Size-1) return(FALSE); /* /* 栈满栈满 */*/

S->top++; S->top++; /*/* 栈顶指针加 栈顶指针加 1*/1*/

S->elem[S->top]=x; S->elem[S->top]=x; /* x/* x 送入栈顶指针指向的单元送入栈顶指针指向的单元 */*/

return(TRUE);return(TRUE);

}/* }/* Push*/Push*/

13

Push Push 算法的时间复杂性为 算法的时间复杂性为 OO(1)(1)

Page 14: 第 3 章  栈和队列

栈的应用(一)表达式求值问题栈的应用(一)表达式求值问题

InitStack(&OVS); InitStack(&OPTR);InitStack(&OVS); InitStack(&OPTR);

Push(&OPTR,’#’);Push(&OPTR,’#’);

printf(“\n\nprintf(“\n\n 请输入一个表达式串(以请输入一个表达式串(以 ## 结尾)结尾) :”);:”);

ch=getchar( );ch=getchar( );while(ch!=‘ #’||GetTop(OPTR)!=‘#’)while(ch!=‘ #’||GetTop(OPTR)!=‘#’)

{{

if(!In(ch,OPSet))if(!In(ch,OPSet))

{ n=GetNumber(&ch); /*{ n=GetNumber(&ch); /* 读入的数读入的数 chch 拼接后转化为十进制拼接后转化为十进制 **

// push(&OVS,n);push(&OVS,n);

}}

else else

{ switch (Compare(GetTop(OPTR),ch)){ switch (Compare(GetTop(OPTR),ch))

int ExpEvaluation( )int ExpEvaluation( )

Page 15: 第 3 章  栈和队列

{ case ‘<‘: /*{ case ‘<‘: /* 栈顶运算符的优先级低于刚读入运算符的优先级栈顶运算符的优先级低于刚读入运算符的优先级 */*/

Push(&OPTR,ch);Push(&OPTR,ch);

ch=getchar( );ch=getchar( );

break;break; case’>=‘:case’>=‘:

Pop(&OPTR,&op);Pop(&OPTR,&op);

Pop(&OVS,&b);Pop(&OVS,&b);

Pop(&OVS,&a);Pop(&OVS,&a);

v=Execute(a,op,b);v=Execute(a,op,b);

Push(&OVS, v); Push(&OVS, v);

break;break;

}}

} /*end of while*/} /*end of while*/

15

V=GetTop(OVS); return(v);V=GetTop(OVS); return(v);

} /* ExpEvaluation*/} /* ExpEvaluation*/

Page 16: 第 3 章  栈和队列

栈的链式存储结构(简称链式栈)是指栈中各栈的链式存储结构(简称链式栈)是指栈中各数据元素独立存储,依靠指针链接建立相邻的逻辑关系。数据元素独立存储,依靠指针链接建立相邻的逻辑关系。这里和线性表的单链表一样,为了操作方便起见,我们这里和线性表的单链表一样,为了操作方便起见,我们也给链式栈添加一个头结点,并令头指针指向头结点。也给链式栈添加一个头结点,并令头指针指向头结点。 栈顶指针 栈顶指针 toptop 惟一地确定一个惟一地确定一个链式栈链式栈,, toptop-->>

next next 指向指向栈顶元素栈顶元素。当 。当 toptop-->next = NULL >next = NULL 时,该时,该链链式栈为空栈。链式栈没有栈满的问题。式栈为空栈。链式栈没有栈满的问题。 在应用中,如果同时需要两个以上的栈,则最在应用中,如果同时需要两个以上的栈,则最好采用链式栈。好采用链式栈。

16

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

Page 17: 第 3 章  栈和队列

1. 1. 栈的链式存储结构栈的链式存储结构

typedef struct node {typedef struct node {

StackElementTypeStackElementType data; data; /* /* 数据域数据域 */*/

struct nodestruct node **next;next; /* /* 指针域指针域 */*/

} LinkStackNode ,} LinkStackNode ,**LinkStack;LinkStack; // // 链式栈的类型名链式栈的类型名

17

由于栈操作是线性表操作的特例(由于栈操作是线性表操作的特例(入栈入栈相当相当于线性表的于线性表的插入插入,,出栈出栈相当于线性表的相当于线性表的删除删除),则链式),则链式栈的操作易于实现,栈的操作易于实现,

Page 18: 第 3 章  栈和队列

(1) (1) 取栈顶元素取栈顶元素int GetTop1 ( LinkStack top, StackElementType *e ) int GetTop1 ( LinkStack top, StackElementType *e ) /* /* 如果栈 如果栈 S S

空,则返回 空,则返回 FALSEFALSE ;; *//**//* 如果栈 如果栈 S S 不空,则用 不空,则用 ee 返回 返回 SS 的栈顶元素,并返回 的栈顶元素,并返回 TRUETRUE 。。 */*/

{{

if ( ! topif ( ! top-->next )>next ) /* /* 如果栈 如果栈 SS 为空,则返回为空,则返回 FALSE*/FALSE*/

return(FALSE);return(FALSE);

else {else { /* /* 如果栈 如果栈 SS 不空,则返回栈顶元素不空,则返回栈顶元素 */*/

e = tope = top-->next>next-->data;>data;

return(FALSE);return(FALSE);

} /* else */} /* else */

} // GetTop1} // GetTop1

18

2. 2. 基本操作在链式栈上的实现基本操作在链式栈上的实现

GetTop1GetTop1 算法的时间复杂性为 算法的时间复杂性为 OO(1)(1)

Page 19: 第 3 章  栈和队列

(2) (2) 将元素压入栈将元素压入栈

int Push1 ( LinkStack &top; SElemType e ) int Push1 ( LinkStack &top; SElemType e ) /* /* 将元素 将元素 ee 插入到栈 插入到栈 SS 中,成为新的栈顶元素中,成为新的栈顶元素 */*/

{{ q = ( LinkStack ) malloc ( sizeof ( SNode ) ); q = ( LinkStack ) malloc ( sizeof ( SNode ) );

if ( ! q ) return (FALSE);if ( ! q ) return (FALSE); // // 存储分配失败存储分配失败 qq-->data = e;>data = e; // // 将数据 将数据 ee 写入新结写入新结点点 qq-->next = top>next = top-->next;>next; // // 将新结点插入栈顶将新结点插入栈顶 toptop-->next = q;>next = q; // // 修改栈顶指针修改栈顶指针 return OK;return OK;

} /* Push1*/} /* Push1*/

19

Push1 Push1 算法的时间复杂性为 算法的时间复杂性为 OO(1)(1)

Page 20: 第 3 章  栈和队列

(3) (3) 将元素弹出栈将元素弹出栈Status Pop1 ( LinkStack &top, SElemType &e )Status Pop1 ( LinkStack &top, SElemType &e )/*/* 若栈 若栈 S S 空,则返回 空,则返回 ERROR ERROR ; 若栈 ; 若栈 S S 不空,则删除 不空,则删除 SS 栈顶元素,用 栈顶元素,用 ee 返回返回

其值,并返回 其值,并返回 OKOK */*/

{{ if ( ! top if ( ! top-->next )>next )

return ERROR;return ERROR; // // 如果栈 如果栈 SS 为空,则返回 为空,则返回 ERRORERROR

e = tope = top-->next>next-->data;>data; // // 取出栈顶元素的值取出栈顶元素的值

q = topq = top-->next;>next; // // qq 指向栈顶元素指向栈顶元素

toptop-->next = q>next = q-->next;>next; // // 删除栈顶元素删除栈顶元素

free (q);free (q); // // 释放栈顶元素所占的空间释放栈顶元素所占的空间

return OK;return OK;

} /*Pop1*/} /*Pop1*/

20

Pop1 Pop1 算法的时间复杂性为 算法的时间复杂性为 OO(1)(1)

Page 21: 第 3 章  栈和队列

队列的逻辑结构队列的逻辑结构

21

3.2 3.2 队 列队 列

队列的顺序存储结构队列的顺序存储结构队列的链式存储结构队列的链式存储结构

Page 22: 第 3 章  栈和队列

队列 队列 (queue)(queue) 是限定只能在表的一端进行插是限定只能在表的一端进行插入,而在表的另一端进行删除操作的线性表。入,而在表的另一端进行删除操作的线性表。

22

在队列中,我们把允许插入的一端称为在队列中,我们把允许插入的一端称为队尾 队尾 (rear)(rear) ,通过队尾指针指明队尾的位置;把允许删除的,通过队尾指针指明队尾的位置;把允许删除的一端称为一端称为队头 队头 (front)(front) ,,通过队头指针指明队头的位置。通过队头指针指明队头的位置。队头和队尾指针将随着队列的动态变化而移动。队头和队尾指针将随着队列的动态变化而移动。

3.2.1 3.2.1 队列的逻辑结构队列的逻辑结构

1. 1. 队列的定义队列的定义

Page 23: 第 3 章  栈和队列

23

假设有队列 假设有队列 QQ = ( = ( aa11, , aa22, , aa33, …, , …, aann-2-2, , aann-1-1, , aann ) ) ,,

则 则 aa11 就是队头元素,就是队头元素, aann 就是队尾元素。队列中的元素就是队尾元素。队列中的元素

是按照 是按照 aa11, , aa22, , aa33, …, , …, aann-2-2, , aann-1-1, , aann 的顺序进队的,而第一的顺序进队的,而第一

个出队的元素是 个出队的元素是 aa11 ,第二个出队的元素是 ,第二个出队的元素是 aa22 ,只有在 ,只有在

aaii-1-1 出队后 出队后 aaii 才可以出队(才可以出队( 11≤≤ii≤≤nn )。)。 当队列中没有元素时称为空队列。当队列中没有元素时称为空队列。

Page 24: 第 3 章  栈和队列

24

(2) (2) 队列的抽象数据类型定义队列的抽象数据类型定义

InitQueue ( &Q )InitQueue ( &Q )

操作结果:构造一个空队列 操作结果:构造一个空队列 QQ 。。

EnterQueue ( &Q, x)EnterQueue ( &Q, x)

初始条件:队列 初始条件:队列 QQ 已经存在。已经存在。操作结果:插入元素 操作结果:插入元素 ee 为队列 为队列 QQ 的新队尾元素。的新队尾元素。

DeleteQueue ( &Q, &x )DeleteQueue ( &Q, &x )

初始条件:队列 初始条件:队列 SS 已经存在且非空。已经存在且非空。操作结果:删除队列 操作结果:删除队列 QQ 队头元素,用 队头元素,用 xx 返回其返回其

值。值。

Page 25: 第 3 章  栈和队列

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

25

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

Page 26: 第 3 章  栈和队列

1. 1. 队列的顺序存储表示队列的顺序存储表示# define MAXSIZE 50;# define MAXSIZE 50; //// 最大队列长度最大队列长度

typedef struct {typedef struct {

QueueElementType element[MAXSIZE]; QueueElementType element[MAXSIZE]; //// 队列的元素空间队列的元素空间

int int front; front; // // 队头指针队头指针

int int rear; rear; //// 队尾指针队尾指针

} SeqQueue ;} SeqQueue ; //// 顺序队列的类型名顺序队列的类型名

26

Page 27: 第 3 章  栈和队列

初始化构建空队列时,令 初始化构建空队列时,令 Q->Q->front = front = Q->Q->rear rear

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

2. 2. 队列的初始化操作队列的初始化操作

27

Page 28: 第 3 章  栈和队列

对下面给出的队列进行插入和删除操作的示意过程:对下面给出的队列进行插入和删除操作的示意过程:

J1 J2 JJ33 JJ44

Q->front Q->rear

28

JJ55 JJ66 JJ77 JJ88

从队列尾插入新元素从队列尾插入新元素从队列头删除旧元素从队列头删除旧元素

Q->rearQ->front Q->rearQ->front Q->rearQ->Q->frontfront QQ->rear->rearQ->Q->frontfront

在队列中,由于不断插入新元素,队在队列中,由于不断插入新元素,队尾很快会超出数组 尾很快会超出数组 Q[ ] Q[ ] 的边界;由于不断删的边界;由于不断删除旧元素,数组 除旧元素,数组 Q[ ] Q[ ] 的开始处又会出现很多的开始处又会出现很多空位。空位。

3. 3. 循环队列循环队列

Page 29: 第 3 章  栈和队列

为了调剂余缺,解决办法有下面两种:为了调剂余缺,解决办法有下面两种:

② ② 将数组 将数组 Q[ ] Q[ ] 的 “尾” 与 的 “尾” 与 Q[ ] Q[ ] 的 的 “头” 逻辑上接起来,形成循环队列,这是一种较巧妙“头” 逻辑上接起来,形成循环队列,这是一种较巧妙的解决办法,也是通常采用的解决办法。的解决办法,也是通常采用的解决办法。

① ① 当发生这样的情况时,把队列中的数据元素当发生这样的情况时,把队列中的数据元素移到数组 移到数组 Q[ ] Q[ ] 的前端,并且修改头指针和尾指针。的前端,并且修改头指针和尾指针。

29

Page 30: 第 3 章  栈和队列

4. 4. 基本操作在循环队列上的实现基本操作在循环队列上的实现

void InitQueue ( SeqQueue *Q ) void InitQueue ( SeqQueue *Q ) //// 构造一个空循环队列 构造一个空循环队列 QQ

{{

Q->front = Q->rear = 0;Q->front = Q->rear = 0;

} /* InitQueue*/} /* InitQueue*/

30

(1) (1) 构造空队列构造空队列

InitQueue InitQueue 算法的时间复杂性为 算法的时间复杂性为 OO(1)(1)

Page 31: 第 3 章  栈和队列

int EnterQueue ( SeqQueue *Q, QueueElemmentType x) int EnterQueue ( SeqQueue *Q, QueueElemmentType x) /* /* 如果队如果队列满,则返回 列满,则返回 FALSE,FALSE, 如果队列不满,则插入元素如果队列不满,则插入元素 x x 为 为 QQ 新的队尾元素。新的队尾元素。 */*/

(2) (2) 在队列尾插入新元素在队列尾插入新元素

{{

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

return (FALSE)return (FALSE) // // 如果队列满,则无法插入,报错如果队列满,则无法插入,报错

Q->element [Q->rear] =x; Q->element [Q->rear] =x; //// 插入插入

Q->rear = ( Q->rear + 1 ) % MAXSIZE;Q->rear = ( Q->rear + 1 ) % MAXSIZE; //// 移动队尾指针移动队尾指针 return (TRUE);return (TRUE);

} } /*EnterQueue*//*EnterQueue*/

31

EnterQueueEnterQueue 算法的时间复杂性为 算法的时间复杂性为 OO(1)(1)

Page 32: 第 3 章  栈和队列

(4)(4) 删除队列头元素删除队列头元素int DeleteQueue ( SeqQueue *Q, QueueElemmentType*x)int DeleteQueue ( SeqQueue *Q, QueueElemmentType*x)/*/* 如果队列空,则返回 如果队列空,则返回 FALSEFALSE , 如果队列不空,则删除 , 如果队列不空,则删除 QQ 的队列头元素,用 的队列头元素,用 xx 返回其值, 返回其值, 并返回 并返回 TRUETRUE 。。 */*/

{{

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

return (FALSE);return (FALSE); //// 若队列空,则无法删除,报错若队列空,则无法删除,报错

*x= Q->element [Q->front];*x= Q->element [Q->front]; ////带回被删除元素带回被删除元素

Q->front = ( Q->front + 1 ) % MAXQSIZE; Q->front = ( Q->front + 1 ) % MAXQSIZE; //// 移动队头指针移动队头指针 return (TRUE);return (TRUE);

} } /* DeleteQueue*//* DeleteQueue*/

32

DeleteQueue DeleteQueue 算法的时间复杂性为 算法的时间复杂性为 OO(1)(1)

Page 33: 第 3 章  栈和队列

队列的应用:打印杨辉三角形前队列的应用:打印杨辉三角形前 nn 行算法行算法 void YangHuiTrianble( )void YangHuiTrianble( )

{{ SeqQueue Q; SeqQueue Q; InitQueue(&Q); InitQueue(&Q); //// 初始化队列初始化队列

EnterQueue(&Q,1) EnterQueue(&Q,1) //// 第一行元素入队列第一行元素入队列

for( n=2;n<=N; n++) for( n=2;n<=N; n++) //// 产生第产生第 nn 行元素并入队,同时行元素并入队,同时打印第打印第 n-1n-1 行的元行的元素素

{ EnterQueue(&Q,1); { EnterQueue(&Q,1); ////杨辉三角的每行的第一个元素是杨辉三角的每行的第一个元素是 1 1

     for( i=1;i<=n-2;i++) for( i=1;i<=n-2;i++)

{ DeleteQueue (&Q, &temp);{ DeleteQueue (&Q, &temp);

printf (“%d”, temp);printf (“%d”, temp);

GetHead (Q, &x);GetHead (Q, &x);

temp=temp+x;temp=temp+x;

EnterQueue (&Q, temp);EnterQueue (&Q, temp);

}}      

Page 34: 第 3 章  栈和队列

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

34

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

Page 35: 第 3 章  栈和队列

1. 1. 队列的链式存储结构队列的链式存储结构

typedef struct Node typedef struct Node // // 结点定义结点定义

{ QueueElementType{ QueueElementType data ;data ; // // 数据域数据域

struct Nodestruct Node **next ;next ; // // 指针域指针域

}LinkQueueNode;}LinkQueueNode;

typedef struct typedef struct // // 队列定义队列定义

{{

LinkQueueNode; *LinkQueueNode; *front; front; // // 队列头指针队列头指针

LinkQueueNode; *LinkQueueNode; *rear;rear; // // 队列尾指针队列尾指针

} LinkQueue ;} LinkQueue ; // // 链式队列的类型名链式队列的类型名

35

Page 36: 第 3 章  栈和队列

(1) (1) 构造空队列构造空队列int InitQueue ( LinkQueue *Q ) int InitQueue ( LinkQueue *Q ) // // 构造一个空链式队列 构造一个空链式队列 QQ

{{

Q->front = (LinkQueueNode*) malloc ( sizeof (LinkQueueNode ) );Q->front = (LinkQueueNode*) malloc ( sizeof (LinkQueueNode ) ); if (Q->front!=NULL ) if (Q->front!=NULL )

{ Q->rear= Q->front;{ Q->rear= Q->front;

Q->front->next=NULL;Q->front->next=NULL;

return (TRUE);return (TRUE);

}}

else return (FALSE) else return (FALSE) // // 存储分配失败存储分配失败}}

36

3. 3. 基本操作在链式队列上的实现基本操作在链式队列上的实现

InitQueue InitQueue 算法的时间复杂性为 算法的时间复杂性为 OO(1)(1)

Page 37: 第 3 章  栈和队列

int DestroyQueue ( LinkQueue *Q ) int DestroyQueue ( LinkQueue *Q ) // // 销毁队列 销毁队列 QQ

(2) (2) 销毁队列销毁队列

{{ while ( Q->front ) while ( Q->front )

{ Q->rear = Q->front{ Q->rear = Q->front-->next;>next;

free ( Q->front );free ( Q->front );

Q->front = Q->rear;Q->front = Q->rear;

} } // while// while

return OK;return OK;

} // DestroyQueue} // DestroyQueue

37

Q->front

Q->rearx y ∧z

DestroyQueueDestroyQueue 算法的时间复杂性为 算法的时间复杂性为 OO((nn))

Page 38: 第 3 章  栈和队列

int EnterQueue ( LinkQueue *Q; QueueElementType x) int EnterQueue ( LinkQueue *Q; QueueElementType x) // // 插入元插入元素 素 xx 为 为 QQ 的新的队列尾元素的新的队列尾元素

(3) (3) 在队列尾插入新元素在队列尾插入新元素

{{LinkQueueNode *NewNode; LinkQueueNode *NewNode;

NewNode= (LinkQueueNode *) malloc ( sizeof (NewNode= (LinkQueueNode *) malloc ( sizeof (LinkQueueNodeLinkQueueNode));));

if (NewNode!=NULL) if (NewNode!=NULL)

38

{ NewNode{ NewNode-->data =x; >data =x;

NewNode-NewNode->next = NULL;>next = NULL;

Q->rear Q->rear --> next =NewNode;> next =NewNode;

Q->rear =NewNode;Q->rear =NewNode;

return(TRUE); }return(TRUE); }

else return (FALSE) else return (FALSE) // // 存储分配失败存储分配失败

} } /* EnterQueue*//* EnterQueue*/EnterQueue EnterQueue 算法的时间复杂性为 算法的时间复杂性为 OO(1)(1)

Page 39: 第 3 章  栈和队列

39

int DeleteQueue ( LinkQueue *Q, QueueElemmentType *x ) int DeleteQueue ( LinkQueue *Q, QueueElemmentType *x ) /* /*

如果队列空,则返回 如果队列空,则返回 FALSEFALSE ;如果队列不空,则删除 ;如果队列不空,则删除 QQ 的 队列头元素,用 的 队列头元素,用 xx 返返回其值,并返回 回其值,并返回 TRUETRUE 。。 */*/

{{if ( Q->front = = Q>rear ) return(FALSE);if ( Q->front = = Q>rear ) return(FALSE);

p = Q->frontp = Q->front-->next;>next; // // pp 指向队列的头元素结点指向队列的头元素结点

free ( p );free ( p );

return (TRUE); return (TRUE);

} // DeleteQueue} // DeleteQueue

Q->frontQ->front-->next = p>next = p-->next;>next; // // 删除 删除 pp 指向的结点指向的结点

if ( Q->rear = = p ) Q->rear = Q->front;if ( Q->rear = = p ) Q->rear = Q->front;

// // 若删除的是队列最后一个元素,则令队尾指针等于队头指针若删除的是队列最后一个元素,则令队尾指针等于队头指针

*x= p->data; *x= p->data; // // 取出队列头元素结点的数据取出队列头元素结点的数据

DeQueue DeQueue 算法的时间复杂性为 算法的时间复杂性为 OO(1)(1)

Page 40: 第 3 章  栈和队列

40队列应用队列应用─模拟患者医院看病过程─模拟患者医院看病过程void SeeDoctor( )void SeeDoctor( )

{ InitQueue(Q);{ InitQueue(Q);

flag=1;flag=1;

while(flag){while(flag){

printf(“\nprintf(“\n 请输入命令:”);请输入命令:”);   ch=getchar( );ch=getchar( );

switch(ch){switch(ch){

case ‘a’:case ‘a’:

printf(“\nprintf(“\n 病历号:”病历号:” ););

scanf(“%d”,&n);scanf(“%d”,&n);

EnterQueue(&Q, n);EnterQueue(&Q, n);

break;break;

Page 41: 第 3 章  栈和队列

case ‘n’: if (! IsEmpty(Q))case ‘n’: if (! IsEmpty(Q))

{ DeleteQueue (&Q, &n);{ DeleteQueue (&Q, &n);

printf(“\nprintf(“\n 病历号为%病历号为% dd 的病人就诊”,的病人就诊”, n);n);

}}

else printf(“\nelse printf(“\n 无病人等候就诊”);无病人等候就诊”);        break;break;

case ‘q’: printf(“\ncase ‘q’: printf(“\n 今天停止挂号,下列病人依次就诊:”);今天停止挂号,下列病人依次就诊:”);           while (!IsEmpty(Q))while (!IsEmpty(Q))

{ DeleteQueue(&Q,&n);{ DeleteQueue(&Q,&n);

printf(“%d”,n);printf(“%d”,n);

}}

Flag=0;Flag=0;

break;break;

default: printf(“\ndefault: printf(“\n 非法命令!”);}}}非法命令!”);}}}