栈的表示和实现

一、            栈的概念

(stack)是插入删除操作限定在表尾进行的线性表

栈的逻辑表示:S=(a1,a2,…,an)

       表尾元素an称为栈顶(top)

       表头元素a1称为栈底(bottom)

不含元素的表称为空栈

栈的运算特征是后进先出(Last In First Out—LIFO)或者先进后出(FirstIn Last Out—FILO)

 

二、            栈的基本运算

INISTACK(S) 初始化操作,设定一个空栈S

EMPTY(S) 判栈S是否为空函数(true/false)

PUSHSx 入栈操作,在栈S顶部插入元素x,相当于线性表的INSERT(L,n+1,x)

POP(S) 出栈函数,若S不空,则返回栈顶元素,并删除栈顶元素;否则返回空元素NULL,相当于线性表的DELETE(L,n)

GRTTOP(S) 取栈顶元素函数,与POP(S)的差别在不删除栈顶元素,相当于线性表的GET(L,n)

CURRENT-SIZE(S) 求S栈中当前元素个数函数,相当于线性表的LENGTH(L)

CLEAR(S) 置栈空操作

 

三、            栈的表示和实现

1.顺序栈的存储结构描述及出入栈运算

CONST arrmax=栈允许存放元素个数的最大值;

TYPE sqstktp=RECORD

                     elem:ARRAY[1……arrmax] OF elemtp;

                     top:0……arrmax {当前栈的表长}

                     END;

S=sqstktp;

S.elem[i] 表示第i个进栈的元素

S.elem[S.top]表示栈顶元素

当S.top=0表示空栈

当S.top=arrmax表示栈满

 

入栈函数push(S,x)的实现

算法思想:若栈满返回false;否则将x入栈,并返回true

FUNC push_stack(VAR s:sqstktp; x:elemtp):boolean;

       IFs.top=armax

              THENRETURN(false)

              ELSE[s.top:= s.top+1;

s.elem[s.top]:=x;

RETURN(true)]

ENDF;{push_stack}

 

出栈函数pop(S)的实现

算法思想:若栈空则返回空元素NULL;否则返回栈顶元素。

FUNC pop_stack(VAR s:sqstktp):elemtp;

       IFs.top=0

       THENRETURN (NULL)

       ELSE[s.top:=s.top-1

RETURN(s.elem[s.top+1])

]

ENDF; {pop_stack}

 

2.链栈的存储结构描述

TYPE pointer=↑nodetype;

    nodetype =RECORD

                data:elemtp;

                next:pointer

             END;

Linkstktp=pointer;

 

四、            多个栈共享存储空间问题

1.   两个栈共享存储空间

常采用将两个栈底设在可用空间的两端。

仅当两个栈顶相遇时,才产生上溢,即所有可用空间已用完对每个栈可用的最大空间就可能大于整个可用空间的一半m/2

 

2.      n个栈共享存储空间

存储空间的一种分配方法是:

将m个存储单元平均分配给n个栈:

S[i],top=S[i].bot=(i-1)*[m/n]        (1≤i≤n)

S[n+1].bot=m (虚设第n+1个栈的bot为m)

当某个栈发生上溢,而整个空间未被占满时,进行“浮动”再分配,

第i个栈上溢条件为:S[i].top=S[i+1].bot         (1≤i≤n)

“浮动”再分配算法思想:

(1)求n个栈所占空间总和

SUM=( S[n].top- S[n].bot)+( S[n-1].top- S[n-1].bot)+……( S[1].top- S[1].bot)

(2)若SUM=m,则无可用空间,上溢处理

       否则进行搜索

       若存在某个栈f,有S[f].top<S[f+1].bot则移动各个元素,得到一个可用单元

栈的应用——表达式求值

一、            表达式

表达式由操作数、运算符和界限符组成。

操作数(operand):常数或变量

运算符(operator)

算术运算符:+、-、*、/、**等

关系运算符:<、≤、=、≠、≥、>

逻辑运算符:AND、OR、NOT

界限符(delimiter):左右括号、表达式结束符#等

先乘除,后加减;

先括号内,后括号外;

同级按左结合律;

空格为不允许的;

 

二、            算术表达式求值的算符优先算法

1.      使用DS

算符栈OPTR:有效算符

操作数栈OPND:有效操作数,运算结果

2.      算法思想:

(1)      初始化:OPND置为空栈,将#放入OPTR栈底。

(2)      依次读入表达式中的每个字符,若是操作数,则入OPND栈;若是算符,则和OPTR栈顶算符进行优先级比较:

1) 若栈顶算符优先,用pop(S)函数弹出算符,执行相应运算,结果存入OPND栈

2) 若与栈顶算符相等,则作()或# #处理

3) 若栈顶算符低,该运算符入OPTR栈。

(3)      重复(2),直到表达式求值完毕

(读入的是#,且OPTR栈顶元素也为#)

 

例:利用算符优先算法对3*7-2)求值

步骤       OPTR        OPND              输入字符              主要操作

1            #                                        3*7-2#           PUSH(OPND,’3’)

2            #                   3                *7-2#           PUSH(OPTR,’*’)

3            # *                 3                     7-2#           PUSH(OPTR,’(’)

4            # * (                     3                          7-2#           PUSH(OPND,’7’)

5            # * (                     37                        -2#           PUSH(OPTR,’-’)

6            # * ( -            37                         2) #            PUSH(OPND,’2’)

7            # * ( -            37 2                        ) #           operate(7,-,2)

8            # * (                     35                           ) #            POPOPTR

9            # *                 35                              #            operate(3,*,5)

10           #                   15                                #            RETURN(GETTOP(OPND))

 

3.      算法描述

FUNC exp_reduced:operandtype;

       INISTACK(OPTR);PUSH(OPTR,’#’); INISTACK(OPND)

       read(w)

       WHILENOT (w=’#’ AND GETTOP(OPTR)=’#’) DO

              IFw NOT IN op THEN PUSH(OPND,w)

              ELSECASE precede(GETTOP(OPTR),w) OF

                            ‘<’:[PUSH(OPTR,w); read(w)]:

                            ‘=’:[x:=POP(OPTR);read(w)]:

‘>’:[theta:=POP(OPTR); b:= POP(OPND);a:=POP(OPND);

PUSH(OPND, operate(a,theta,b))]

ENDC;

RETURN(GETTOP(OPND))

ENDF;{ exp_reduced }

 

递归过程

递归是栈的另一个重要应用,也是程序设计的一个强有力的工具。

一、            应用递归的原因和领域

1.递归定义

             1                         当n=0

阶乘n!=

             n*(n-1)!         当n>0

                            0         n=0

Fibonacci数列Fib(n)=      1         n=1

                            Fib(n-1)+ Fib(n-2)  n>1

 

2.递归结构

二叉树,广义表结构本身固有递归特性,操作也可递归描述。

3.用递归求解更简单

例:计算两个非负整数a*b的算法

1.       递归方式:a*b=b+a-1*b

FUNC mul1abinteger):integer

       IF a=0 THENRETURN(0)

              ELSERETURN(b+mul1a-1b)

ENDF;{mul1}

 

2.       迭代方式:a*b=ab之和

FUNC mul2abinteger):integer

z=0

FOR i:=1TO a DO

              z:=z+b;

RETURN(z)

ENDF;{mul2}

 

二、            递归过程的特点

是程序设计的一个强有力工具,它具有结构清晰,程序易编、易读、易调试,程序正确性易证明等优点;但运行效率低(函数调用自己(现场保护、现场恢复)比语句调用自己难)

 

三、            基本原理

重复地把问题转化为与原问题相似的新问题,直到问题解决为止。

 

四、            关键点

1.用较简单的新问题来表示较复杂的原问题

2.不能产生自己调用自己的无穷序列,即必须有一个递归调用序列的“出口”,来终止递归调用

 

五、            实现

递归过程都是通过栈来实现的,并且任何递归算法均可通过栈改写为非递归算法。

 

六、            汉诺(Hanoi)塔问题 (世界末日问题)

设:有X、Y、Z三个塔座,在X上按直径大小递减次序依次插有n个直径各不相同的圆盘,各圆盘按直径从小到大编为1—n

要求:将X塔上的n个圆盘按规则移至Z上,并仍按同样顺序叠排

移动规则:

(1)      每次只移动一个圆盘;

(2)      移动的圆盘可以插在任一塔座上,但是在任一时刻都不能将大盘压在小盘上。

算法思想:

当n=1:只需将该圆盘从X移到Z即可;

当n>1:若能将压在n号盘上的n-1个圆盘按规则移至Y,然后把n号盘从X移至Z上;

最后再将Y上的n-1个圆盘按规则移至Z上。

原问题为:hanoi(n, X, Y, Z)

化简为:hanoi(n-1, X, Y, Z)

              move(X, n, Z)把X上的n号盘移到Z上

              hanoi(n-1,Y , X, Z)

PROC hanoi(n:integer; X,Y,Z: char);

IF n=1 THEN move(X, n, Z)

       ELSE [hanoi (n-1, X, Z,Y);

move(X, n, Z)

[hanoi (n-1, Y ,X, Z)]

ENDP; {hanoi}

 

习题:

1.       A,B,C,D四个元素依次入栈,假设栈足够大,并且已入栈的元素可以在任意时刻出栈,试写出所有可能的出栈序列。(求n个元素所有可能出栈序列的计算公式)

2.       设带头结点的线性表中元素值为非零正整数,试写出:

(1)      求线性表中所有元素值之和的递归函数(空表返回0

(2)      求线性表中元素最大值的递归函数(空表返回0

(3)      求线性表中元素个数的递归函数(空表返回0

 

队列的表示与实现

一、            定义

队列(queue是限定仅在一端插入,另一端删除的线性表。

允许插入的一端叫队尾(rear,允许插入的一端叫队头(front

不含元素的空表称为空队列

队列的运算特性是先进先出(First In First Out—FIFO

 

二、            队列的基本运算

INIQUEUE(Q) 初始化操作,设置一个空队列

EMPTY(Q) 判断队列是否为空函数(true/false)

ENQUEUE(Qx) 入队列操作,队尾插入元素x

DEQUEUE(Q) 出队函数

GETHEAD(Q) 取队头元素且不删除

CLEAR(Q) 队列置空操作

CURRENT-SIZE(Q) 求队列Q当前元素个数

 

对队列的操作

1.      实际模型(排队买票)

2.      线性模型

 

三、            队列的链式存储结构——链队列

1.      存储结构

链队列需要队头和队尾两个指针来确定。

TYPE

queueptr=queuenode

queuenode=RECORD

                            data:elemtp;

                            next:queueptr

                       END;

linkedquetp=RECORD

                           front,rear:queueptr

                       END;

给链队列添加个头结点,并令头指针指向头结点。

空链队列有头指针均指向头结点

即q,front=q.rear

2.      出队算法

FUNC dl_linkedque(VAR q:linkedquetp):elemtp;

IF q. front =q. rear THEN RETURN(NULL)

ELSE[s: =q. front↑.next;

q. front↑.next:=s↑.next;

IF s↑.next=NIL THEN q. rear = q. front;

x:= s↑.data;dispose(s);

RETURN(x)]

ENDF:{dl_linkedque}

删除时的三种情形:

(1)       删除前已空;

(2)       删除前只有一个结点,删除后为空队列;

(3)       其他情形(删除前结点数>1)

3.      入队算法

PROC en_linkedque(VAR q:linkedquetp ; x:elemtp );

new(p); p↑.data:=x; p↑.next:=NIL;

q. rear↑.next:=p;q.rear:=p

ENDP;

 

四、            队列的顺序存储结构

1.      一般顺序存储结构

用一个向量来存放队列元素,并用两个指针来指示队头和队尾。并约定:头指针sq.front总是指向队头元素的前一个位置;尾指针sq.rear指向队尾元素。

TYPE sequeuetp =RECORD

              elem:ARRAY[1…maxsize ] OF elemtp;

              front,rear:0…maxsize

       END;

 

五、            循环队列

解决线性模型的假溢出问题

Job未进入时,头尾指针同时指向[0]

指针加1操作:i=(i+1)% max

append :=rear“++”then append job

pop :=remove job then front “++”

队空和队满时头尾指针都指向同一位置

 

判断队列空或者满可能的解决办法:

1.      在队列中预留一个空位(空位不确定,在front指向的位置)

2.      设定一个标志(1bit)(记录下头尾指针相等的原因,例如:入队造成相等(满)标志位为1,出队造成相等(空)标志位为0)

Step1;标志位置初值,标志位为0

Step2:入队操作,标志位置为1,

Step3:出队操作,标志位置为0

判空条件:rear=front标志位为0

判满条件:rear=front标志位为1

3.      设置一个数字计数器

数字计数器为0

数字计数器为maxsize

(浪费空间,需要一个整数,4byte=32bit)

4.      让front和rear等于一个不可能等于的值

rear和front允许的值为0—maxsize-1,扩大rear和front 的定义域为0……maxsize。

队列为空的初始条件:初值rear=0;front=maxsize

(maxsize为不可能的值,front的实际位置为0,rear既表示自己又表示front)

(1)      入队前,先判rear是否= maxsize,是则为队满

(2)      当入队后,使得cq . rear= cq .front,则令cq . rear= maxsize ,表示队满。

(3)      若入队前,cq.front=maxsize(空),入队后队列不空,则:cq.front:=cq . rear-1(恢复front指针恢复,入队只是rear指针+1,即front-1)

(如果cq . rear=0,cq.front:=maxsize-1)

入队操作要考虑的问题:

第一步:队列是否已满;

第二步:入队后队列是否已满;

第三步:入队前队列是否为空。

(1)      出队前,先判front是否=maxsize,是则为对空。

(2)      当出队后,使得cq .front= cq .rear,则令cq .front=maxsize,表示队空。

(3)      若cq.rear=maxsize,则:cq.front:= cq . rear-1

(如果cq . rear=0,cq.front:=maxsize-1)

出队操作要考虑的问题:

第一步:队列是否为空;

第二步:出队后队列是否为空;

第三步:出队前队列是否已满。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值