2.13 检查给定排列可否由栈得到
设计一个算法,检查给定排列可否由栈得到。在回答yes时,算法应同时输出相应的操作序列。在你的算法中,除了read、print、push和pop,你可以利用is - empty(S),测试栈S是否为空
栈
栈(Stack)是先进先出(FIFO - Fisrt In First Out)型的数据结构,具有这样的性质:在栈中某成员之前进栈,且在其之后出栈的元素,在出栈时的顺序必然是进栈时的逆序。举例说明,若有进栈序列1,2,3,显然1,2在3进栈之前进栈。若3先于1和2出栈,则出栈序列中,2必然排在1之前,因为这是它们进栈顺序的倒置。因此进栈序列为1,2,3时,是不可能出现3,1,2这样的出栈序列的。
所谓的进栈和出栈序列,并不是连续的。初学者直觉上可能会理解为,当所有成员进栈以后才会出栈,那么进栈和出栈序列就是固定的。实际上,进栈和出栈时交错进行的。我们所说的“进栈序列”,是指在栈的进出序列之中剔除出栈的信息所排成的序列。例如,有序列1 in, 2 in, 2 out, 3 in, 3 out, 1 out,则进栈序列为1,2,3,出栈序列为2,1,3。
因此,检查给定排列是否可由栈得到,人工方法可做如下检验:对于出栈序列的每一个元素,都需要查看在其之后出栈,且在其之前进栈的成员(很绕),它们的出栈序列必须是进栈序列的倒序。
还是举个例子,若有进栈序列1,2,3,4,5,则出栈序列2,5,4,3,1是可能的。检查如下:
- 查看元素2,在其之前进栈的元素为1,在其之后出栈的元素为5,4,3,1,两者都符合的元素为1,由于只有一个元素,因此满足要求;
- 查看元素5,在其之前进栈的元素为1,2,3,4,在其之后出栈的元素为4,3,1,两者都符合的元素为4,3,1,且这三者的出栈顺序是进栈顺序的倒置,因此满足要求;
- 查看元素4,在其之前进栈的元素为1,2,3,在其之后出栈的元素为3,1,两者都符合的元素为3,1,且这两者的出栈顺序是进栈顺序的倒置,因此满足要求;
- 查看元素3,在其之前进栈的元素为1,2,在其之后出栈的元素为1,两者都符合的元素为1,由于只有一个元素,因此满足要求;
- 查看元素1,在其之前进栈的元素为1,2,3,4,5,在其之后无元素出栈,没有两者都符合的元素,不必进行顺序检验,因此满足要求;
符合上述进出栈条件的操作序列为1 in, 2 in, 2 out, 3 in, 4 in, 5 in, 5 out, 4 out, 3 out, 1 out.
算法
算法运行的模式如下:
- 检查排列是否可由栈得到:给定某个1~N的序列,从第一个元素开始,遍历其后的元素,对于每个小于其的元素,检验它们是否是降序排列的。
- 回答yes时输出相应的操作序列:检查栈顶元素,是否是出栈序列所需的下一个元素,若是,则从栈中取出该元素并输出它,若不是,将一个元素压入栈,然后再检查栈顶元素是否满足要求。当栈和入栈队列均为空,或者入栈队列已经为空,但给定序列仍未检查完毕时,退出循环。若待检验序列遍历完毕时,栈也已清空,则该排列可由栈得到。反之则不可以。事实上,1和2两个问题可以一并解决。
代码
def CheckStack(S): # 检查一个序列是否可由栈得到,是则返回True,否则返回False。函数入参为待检验序列,类型为list,
def pop(stack): # 定义出栈操作,若栈为空则返回提示,若不为空则返回栈顶元素
node = len(stack)
if(node == 0):
print('栈为空')
return
else:
pop = stack[node - 1]
del stack[node - 1]
return pop
def push(element, stack): # 定义入栈操作,将element压入stack中,并返回栈顶指针(此处为list的角标)
node = len(stack)
stack.append(element)
return node
stack = []
i, node, N = [0, 0, 1]
scale = len(S)
print('待检验序列为%s' % S)
# 初始化变量,stack为检查序列用到的栈,i,node,N分别为待检序列的角标、栈顶指针、下一个入栈的元素
# scale为变量变化的范围,因为待检序列、栈深、入栈元素的所在区间长度都一致
while((0<node<=scale and N <= scale) or i<scale):
if((node>0) and (stack[node - 1] == S[i])): # 若node大于0,表示栈中有元素,可以进行出栈操作
print('%d out' % stack[node-1])
pop(stack)
i += 1
node -= 1
elif(N <= scale): # 若N大于scale,表明待入栈元素已经全部进栈
push(N, stack)
print('%d in' % N)
N += 1
node += 1
else: # 两种情况会退出循环:进栈队列和栈中都为空时、栈不为空但栈顶元素与待检序列不匹配时
break
if(node==0 and i==scale): # 若栈和进栈队列均为空,则循环为正常退出,给定序列可以由栈得到
return True
else: # 异常退出,给定序列不可由栈得到
return False
def getS(N): # 生成待检验序列的函数,入参为序列中的最大整数N,返回一个由整数1~N组成的序列S
from random import randint
S = []
for i in range(N):
S.append(i+1)
for i in range(N): # 将原本升序排列的元素随机两两交换N次
j = randint(0, N-1)
k = randint(0, N-1)
S[j], S[k] = S[k], S[j]
return S
S1 = [2, 5, 4, 3, 1]
S2 = [2, 5, 4, 1, 3]
S3 = getS(4)
S4 = getS(7)
CheckStack(S1) # 返回True
CheckStack(S2) # 返回False
CheckStack(S3)
CheckStack(S4)