47
第4第 第第第第

第 4 章 栈和队列

  • Upload
    poppy

  • View
    125

  • Download
    0

Embed Size (px)

DESCRIPTION

第 4 章 栈和队列. 第 4 章 栈和队列. 4.1 栈 4.2 队列 4.3 小结 4.4 应用举例及分析 习题. 出 栈 入栈. a n. top. ……. a i. ……. a 1. a 0. bottom. 4.1 栈. 4.1.1 栈的定义及基本操作. 定义: 栈( stack ) 是限定在 表的一端 进行插入或删除操作的线性表。 栈顶 (top) : 插入删除操作的一端称为栈顶 ,另一端称为 栈底 (bottom) 。 入栈: 插入元素 出栈: 删除元素 空栈: 不含元素的栈。. - PowerPoint PPT Presentation

Citation preview

Page 1: 第 4 章  栈和队列

第 4 章 栈和队列第 4 章 栈和队列

Page 2: 第 4 章  栈和队列

上一页 下一页 返 回

第 4 章 栈和队列4.1 栈4.2 队列4.3 小结4.4 应用举例及分析习题

Page 3: 第 4 章  栈和队列

上一页 下一页 返 回

4.14.1 栈栈

定义:栈( stack )是限定在表的一端进行插入或删除操作的线性表。栈顶 (top) :插入删除操作的一端称为栈顶 ,另一端称为栈底 (bottom) 。入栈:插入元素出栈:删除元素空栈:不含元素的栈。

4.1.1 栈的定义及基本操作

a0

a1

……

an

ai

……

出栈 入栈

top

bottom

Page 4: 第 4 章  栈和队列

上一页 下一页 返 回

栈 S=(a1 , a2 , a3 ,… an) ,栈中元素按 a1 , a2 , a3 ,… an 的次序进栈,则 a1 为栈底元素, an 为栈顶元素。出栈的第一个元素应为栈顶元素,即栈的修改是按后进先出的原则进行的。

栈:后进先出( LIFO )的线性表。 a1

a2

……

an

ai

……

出栈 入栈栈S

Page 5: 第 4 章  栈和队列

上一页 下一页 返 回

栈常用的几种基本操作:

( 1 ) INITSTACK(S) 初始化空栈( 2 ) EMPTY(S) 判断空栈 ( 3 ) PUSH(S) 入栈( 4 ) POP(S) 出栈( 5 ) GETTOP(S) 取栈顶元素( 6 ) CLEAR(S) 栈置空 ( 7 ) CURRENT_SIZE(S) 求当前栈中元素个数

Page 6: 第 4 章  栈和队列

上一页 下一页 返 回

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

简称为顺序栈,利用一组地址连续的存储单元依次存放从栈底到栈顶的数据元素。栈底位置固定不变,设定一个指针 top ( int 型)指向栈顶元素所在的位置 。 a0

a1

……

an-1

顺序栈 S

ai

……

an

栈底 bottombottom

栈顶 toptop

Page 7: 第 4 章  栈和队列

上一页 下一页 返 回

顺序栈的数据类型可描述为:#define Datatype1 int

#define maxsize 100

typedef struct

{Datatype1 data [maxsize] ;

int top ; } SEQSTACK

an an-1 …… a1

top

SEQSTACK *s

data

Page 8: 第 4 章  栈和队列

上一页 下一页 返 回

• 设向量表的 0 单元为栈底,约定向量中的0单元空闲不用。若 MAXSIZE 为 n ,栈中可放入最多元素数为 MAXSIZE-1 。

• 若定义 SEQSTACK * s ;s->top= =0 表示栈空s->top= =MAXSIZE-1 表示栈满

• top 指示器是在栈中操作的依据。

Page 9: 第 4 章  栈和队列

上一页 下一页 返 回

void initstack(SEQSTACK *S)

{

S->top = 0;

}

顺序栈基本操作对应算法—— (1) 初始化空栈

S->top=0

5 4 3 2 1 0

空栈

Page 10: 第 4 章  栈和队列

上一页 下一页 返 回

int empty(SEQSTACK *S)

{

if(S->top == 0)

   return 1;

else

   return 0;

}

顺序栈基本操作对应算法—— (2) 判栈空

Page 11: 第 4 章  栈和队列

上一页 下一页 返 回

int push(SEQSTACK *S, DATATYPE1 x) { if(S->top = = MAXSIZE-1)

{ printf(“Overflow \n"); return 0; }else

{S->top ++; (S->data)[S->top] = x; return 1; } }

顺序栈基本操作对应算法——( 3 )入栈

5 4 3 2A 1 0

S->top=1

入栈前

5 4 3x 2A 1 0

S->top=2

入栈后

4

E 5D C 3B 2A 1 0

S->top=5

栈满

Page 12: 第 4 章  栈和队列

上一页 下一页 返 回

DATATYPE1 pop(SEQSTACK *s) {DATATYPE1 x; if(empty(s)) {printf (“underflow\n”); x=NULL;} else {x=(s–>data)[s–> top]; s–>top--;} return(x); }

顺序栈基本操作对应算法——( 4 )出栈

5 D 4 C 3 B 2 A 1 0

S->top=4S->top=3

5 4

C 3 B 2 A 1 0

出栈

S->top=0

5 4 3 2 1 0

空栈

x

D

Page 13: 第 4 章  栈和队列

上一页 下一页 返 回

DATATYPE1 gettop(SEQSTACK *s) {DATATYPE1 x; if(empty(s)) {printf (“Stack is empty.\n”); x=NULL;} else x=(s–>data)[s–> top]; return(x); }

顺序栈基本操作对应算法——( 5 )取栈顶元素

Page 14: 第 4 章  栈和队列

上一页 下一页 返 回

用单链表作为存储结构的栈称为链栈。•top 为栈顶指针,是指针型变量,可唯一确定链栈。•top= =NULL 时,为空栈•链栈无栈满问题

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

data next

栈底 ^

top 栈顶

Page 15: 第 4 章  栈和队列

上一页 下一页 返 回

链栈的数据类型可描述为:#define Datatype2 char

typedef struct snode

{ DATATYPE2 data ;

struct snode * next ;

} LINKSTACK ;

data next

LINKSTACK

X

Page 16: 第 4 章  栈和队列

上一页 下一页 返 回

p

LINKSTACK * pushstack (LINKSTACK * top, DataType2 x)

{LINKSTACK * p;

p = malloc(sizeof(LINKSTACK));

p->data = x;

p->next = top;

top = p;

return p;

}

链栈基本操作对应算法—— ( 1 ) 入栈

top

^

x

Page 17: 第 4 章  栈和队列

上一页 下一页 返 回

LINKSTACK * popstack (LINKSTACK * top, DataType2 v) {LINKSTACK * p; if (top= =NULL) printf(“Underflow.\n”); else {v=top->data ;

p = top; top = top->next;

free(p);} return top;

}

top

链栈基本操作对应算法——( 2 )出栈

^

^

top ^

空栈

px

v

Page 18: 第 4 章  栈和队列

上一页 下一页 返 回

4.2  队列

定义:队列:是一种线性表,其插入操作均限定在表的一端进行,而删除操作限定在表的另一端进行。所以把队列简称为先进先出的线性表( FIFO )。队头:允许删除操作的一端称队头 (front) 。队尾:允许插入操作的一端称为队尾 (rear) 。空队列:当队列中无元素时称为空队列。

4.2.14.2.1 队列的定义及基本操作队列的定义及基本操作

队列结构示意图:出队 入队

队头 队尾

a1   a2  … an

Page 19: 第 4 章  栈和队列

上一页 下一页 返 回

队列的几种基本操作:( 1 ) INITQUEUE(Q) 初始化空队列( 2 ) EMPTY(Q) 判断空队列( 3 ) ADDQ(Q,x) 入队列( 4 ) DELQ(Q) 出队列( 5 ) GETFRONT(Q) 取队头元素( 6 ) CLEAR(Q) 队列置空 ( 7 ) CURRENT_SIZE(S) 求队列中元素个数

Page 20: 第 4 章  栈和队列

上一页 下一页 返 回

顺序队列:定义:顺序存储结构的队列称为顺序队列。方案:队头队尾各设一个位置指针 front 和 rear(两个指针都是整型变量)。队列中最多存放的元素数为 MAXSIZE 。队头指针总是指向队列中第一个元素的前一个位置,队尾总是最后一个元素的位置,队列中元素的个数为 rear-front 。

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

Page 21: 第 4 章  栈和队列

上一页 下一页 返 回

实现顺序队列基本操作的原则如下:队列的初始化条件: q->rear=q->front =-1

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

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

顺序队列的数据类型描述为:#define DATATYPE1 int

#define MAXSIZE 100typedef struct{datatype data [maxsize] ;int front, rear; } SEQQUEUE;

Page 22: 第 4 章  栈和队列

上一页 下一页 返 回

顺序队列插入、删除操作示意图 :

q->front→

CBA

5 4 3

2 1 0

初始状态为空

q->rear→

q->rear→q->front→

ABC 依次进队

CBA

5 4 3

2 1 0

ABC 依次出队

Page 23: 第 4 章  栈和队列

上一页 下一页 返 回

循环队列: 引入:顺序队列存在这样的问题:已删除元素结点的位置无法利用,甚至存在假满现象。 改进方法:把顺序队列首尾相接构成循环队列,克服假满现象。 循环队列基本操作的原则变为:

队满条件: ( q->rear+1 )%MAXSIZE=q->front

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

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

Page 24: 第 4 章  栈和队列

上一页 下一页 返 回

顺序循环队列的基本原理:把顺序队列所使用的存储空间构造成一个逻辑上首尾相连的循环队列。当 rear 和 front达到 MaxQueueSize-1 后,再前进一个位置就自动到0。   顺序队列顺序队列

a3

a2

a1front

rear

0

1

2

3

.

.

N-1

a3 a2

a1

0

1

2

3

N-1

rear

front

循环队列循环队列

Page 25: 第 4 章  栈和队列

上一页 下一页 返 回

循环队列基本操作对应算法——( 1 )判队空 int empty(SEQQUEUE *q){ if(q-> rear == q-> front)

return 1;else return 0;

}

Page 26: 第 4 章  栈和队列

上一页 下一页 返 回

循环队列基本操作对应算法—— ( 2 )取队头元素DATATYPE1 getfront(SEQQUEUE *q, ) { DATATYPE1 v; if(empty(q)) {printf(“queue is empty.\n”); v=NULL;} else v=(q–data)[(q–>front+1)%MAXSIZE]; return v; }

Page 27: 第 4 章  栈和队列

上一页 下一页 返 回

循环队列基本操作对应算法—— ( 3 )入队int enqueue(SEQQUEUE *q, DATATYPE1 x){int r; if(q->front==( q->rear+1) %MAXSIZE)

{ printf(" queue overflow \n");r=0; }

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

(q->data)[Q->rear] = x;r=1; }

return r;}

Page 28: 第 4 章  栈和队列

上一页 下一页 返 回

循环队列基本操作对应算法—— ( 4 )出队DATATYPE1 dequeue(SEQQUEUE *q ) { DATATYPE1 v; if(empty(q)) {printf(“queue is empty.\n”); v=NULL;} else {q–front=(q–>front+1)%MAXSIZE; v= (q->data)[Q->front]; return v; } }

Page 29: 第 4 章  栈和队列

上一页 下一页 返 回

用链式存储结构表示队列,称为链队列。

一个链队列具有头、尾两个指针。一般设立一个头结点,队列的头指针指向头结点,头结点的下一元素为队头元素。

q->front=q->rear 为队空条件。

链队列无队满问题。

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

data next

队尾元素 ^

q->front 头结点

队头元素

q->rear

Page 30: 第 4 章  栈和队列

上一页 下一页 返 回

链队列的数据类型可描述为:#define DATATYPE1 int ;

typedef struct qnode {DATATYPE data ; struct qnode * next }LINKQLIST ;

typedef struct {LINKQLIST * front , * rear ; } LINKQUEUE ;

Page 31: 第 4 章  栈和队列

上一页 下一页 返 回

链队列基本操作对应算法—— ( 1 )队列初始化 void initlinkqueue(linkqueue *q)

{

q–>front=malloc(sizeof(linkqlist));

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

q–>rear= q–>front;

}

Page 32: 第 4 章  栈和队列

上一页 下一页 返 回

链队列基本操作对应算法—— (2) 判断队列空 int emptylinkqueue(linkqueue *q) { int v; if(q–> front= q–>rear) v=1; else v=0; return v; }

Page 33: 第 4 章  栈和队列

上一页 下一页 返 回

链队列基本操作对应算法—— (3) 读队首元素 DATATYPE1 getlinkqueue(linkqueue *q)

{

DATATYPE1 v;

if(emptylinkqueue(q))

v=NULL;

else

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

return v;

}

Page 34: 第 4 章  栈和队列

上一页 下一页 返 回

链队列基本操作对应算法—— (4) 入队列 void enlinkqueue(LINKQUEUE*q,DATATYPE1 x) { (q–>rear) –>next=malloc(sizeof(LINKQLIST)); q–>rear=(q–>rear) –>next; (q–>rear) –>data=x; (q–>rear) –>next=NULL; }

Page 35: 第 4 章  栈和队列

上一页 下一页 返 回

4.3 4.3 小结小结• 栈和队列是两种特殊的线性表,从逻辑结构上分

析,其数据元素仍然是顺序存储的,数据在存储空间的存放地址也是连续的。栈的结构特点是先进后出,数据元素只能在表的一端进行插入和删除。栈指针 Top 始终指向栈顶元素在栈中的序号。栈的基本算法有进栈和出栈两种,在算法实现中必须注意栈满 ( 上溢 ) 和栈空 ( 下溢 ) 的判断。队列是数据元素在表的一端插入,在表的另一端删除的特殊线性表。队列的结构特点是先进先出。队列有队首指针 ( 也称作退队指针 ) 和队尾指针( 也称作进队指针 ) 。

Page 36: 第 4 章  栈和队列

上一页 下一页 返 回

• 队列的基本算法也有进队和出队两种,在算法中也要注意队满和队空的判断。为了解决队列中的假溢出问题,循环队列是一种有效的队列结构形式,队列中存储单元首尾相接的结构特征能充分利用队列中的存储空间。注意,在循环队列中,队满时始终存在一个空单元。

• 在有序线性表的插入和删除算法中,算法执行在时间上的量级是线性阶 O(n)。栈和队列的基本操作算法的时间执行量级是常阶数 O(1)。

• 链栈和链队是单链表的一种特殊形式,它们具有链表的存储结构的特征,也有栈和队列操作上的各自特点。

Page 37: 第 4 章  栈和队列

上一页 下一页 返 回

• 链栈的进栈和退栈的运算都在首部进行,只需调整链栈的入口地址;链队具有进队指针 Rear和退队指针 Front ,进队时调整进队指针 Rear的入口地址,退队时调整 Front 的退队指针。同时,应该注意栈空、队空时的判断。

Page 38: 第 4 章  栈和队列

上一页 下一页 返 回

4.4 4.4 应用举例及分析应用举例及分析例1:将一个非负十进制数转换成八进制数,

分别用非递归算法和递归算法来实现。递归算法如下:Void d_to _o(unsigned x){if(x%8!=0) d_to_or(x/8); Printf(“%d”,x%8);}

Page 39: 第 4 章  栈和队列

上一页 下一页 返 回

非递归算法如下:Void d_to _o(unsigned x){SEQSTACK stack, *s;S=&stack;Initstack(s);Push(s,’#’);Whiel(x!=0) {push(s,x%8); S=x/8;} While(gettop(s)!=‘#’) Printf(“%d”,pop(s));}}

Page 40: 第 4 章  栈和队列

上一页 下一页 返 回

例 2:括号配对的语法处理。 “ “ ” ”如果表达式中包含有括号 ( 和 ) ,并可以嵌套使用,则需检测表达式中的括号输入是否匹配 / “ ” “如果是 (()) 序列或 (()

”()) 序列,为配对正确,如果出现“ ” “ ”(() 序列或 ())( 序列为配对不正确。可以基于栈结构设计一个算法来判断输入的一串括号是否配对正确。算法假设输入

“ “ ” ”的字串中除左括号 ( 和右括号 ) 之外无其他字符,括号串以回车键结束。

Page 41: 第 4 章  栈和队列

上一页 下一页 返 回

算法先分析左括号和右括号配对的规律:左括号和右括号配对一定是先有左括号,后出现右括号。因括号可以嵌套使用,左括号允许单个或连续出现,并等待右括号出现而配对消解。左括号在等待右括号出现的过程中应暂时保存起来,当右括号出现时则一定是和最近出现 的一个左括号配对并消解,当括号出现而找不到有左括号配对时则一定发生了配对不正确的情况。从嵌套的角度考虑,如果左括号连续出现,则后出现的左括号应该与最先来到的右括号配对消解。左括号的这种保存和与右括号的配对消解的过程和栈的后进先出原则是一致的。可以将读到的左括号“(“压入设定的栈中,当读到右括号”)”时就和栈中的左括号配对消解,也就是将栈顶的左括号弹出栈,如果栈顶弹不出左括号,则表示输入括号匹配出错;如果括号串已读完,栈中仍有左括号存在,则也表示输入括号匹配出错。

Page 42: 第 4 章  栈和队列

上一页 下一页 返 回

采用栈结构来检测括号配对是否正确所对应的算法如下: int check(SEQSTACK * s) {int bool;char ch;push(s,’# ‘);ch = getchar();bool = 1;while(ch ! = ‘\n’ && bool) {if(ch = = ‘(‘)) //遇左括号,左括号入栈 push(s,ch); //遇右括号 if(ch = = ’)’) if(gettop(s) = = ‘# ‘) bool = 0; // 无左括号配对,则出错 else pop(s); // 有左括号配对,则去左括号 ch = getchar();} if(gettop(s) ! = ‘# ‘) //左括号数目多于右括号,出错 bool = 0; if(bool) printf(“right”); else printf(“error”); }

Page 43: 第 4 章  栈和队列

上一页 下一页 返 回

main()

{

SEQSTACK st, * s;

s = & st;

initstack(s);

check(s);

}

Page 44: 第 4 章  栈和队列

上一页 下一页 返 回

例 3:队列管理的模拟算法(队列采用带头结点的链表结构)。

采用如下管理模式:队列初始化为空队列键盘键入奇数时:奇数从队尾入队列;键盘键入偶数时:队头指针指向的奇数出队列;

键盘键入 0时:退出算法;每键入一整数,显示操作后队列中的值。

Page 45: 第 4 章  栈和队列

上一页 下一页 返 回

Void outlinkqueue(LINKQUEUE * q) //显示队列中的值 {LINKQLIST * p;p = q->front;printf(“queue :” );While(p ! = q->rear) {p = p->next; printf(%d “ , p->data);}printf(“\n”);}

Page 46: 第 4 章  栈和队列

上一页 下一页 返 回

main() {LINKQUEUE lq, * p;int j;p = &lqinitlinkqueue(p);printf(“input a integer ”: );scanf(“%d”, &j);While(j ! = 0) { if(j % 2 = = 1) enlinkqueue(p,j); //输入奇数:奇数入队列 else j = delinkqueue(p); //输入偶数:队头奇数队列 outlinkqueue(p); printf(“\n input a integer ”: ); }}

Page 47: 第 4 章  栈和队列

上一页 下一页 返 回

习题1、简述栈和线性表的区别.2、简述栈和队列这两种数据结构的相同点

和不同点。3、将一个非负十进制整数转换成二进制,

用非递归和递归算法来实现。