检查给定排列可否由栈得到

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是可能的。检查如下:

  1. 查看元素2,在其之前进栈的元素为1,在其之后出栈的元素为5,4,3,1,两者都符合的元素为1,由于只有一个元素,因此满足要求;
  2. 查看元素5,在其之前进栈的元素为1,2,3,4,在其之后出栈的元素为4,3,1,两者都符合的元素为4,3,1,且这三者的出栈顺序是进栈顺序的倒置,因此满足要求;
  3. 查看元素4,在其之前进栈的元素为1,2,3,在其之后出栈的元素为3,1,两者都符合的元素为3,1,且这两者的出栈顺序是进栈顺序的倒置,因此满足要求;
  4. 查看元素3,在其之前进栈的元素为1,2,在其之后出栈的元素为1,两者都符合的元素为1,由于只有一个元素,因此满足要求;
  5. 查看元素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. 检查排列是否可由栈得到:给定某个1~N的序列,从第一个元素开始,遍历其后的元素,对于每个小于其的元素,检验它们是否是降序排列的。
  2. 回答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)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值