问题描述
假设我们有一组数字按从小到大的顺序执行进栈和出栈的操作,比如我们有数字0, 1, 2, 3, 4, 5, 6, 7, 8, 9。它们按照顺序混合执行push, pop操作。其中pop操作返回的数字组成一个序列。那么,当我们给定一个序列的时候,能否判断这个序列是可以通过这么一组push, pop操作形成呢?
问题分析
对于这个问题,一开始确实有点不太好分析。虽然数字都是按照顺序入栈的,但是它们出栈的顺序却不确定。假设我们以一组数字0, 1, 2为例来看它们push, pop操作形成的序列。那么它们对应如下的一些情况:
1. push 0, pop 0, push 1, pop 1, push 2, pop 2 (0, 1, 2)
2. push 0, pop 0, push 1, push 2, pop 2, pop 1 (0, 2, 1)
3. push 0, push 1, pop 1, pop 0, push 2, pop 2 (1, 0, 2)
4. push 0, push 1, pop 1, push 2, pop 2, pop 0 (1, 2, 0)
5. push 0, push 1, push 2, pop 2, pop 1, pop 0 (2, 1, 0)
按照这里的分析,至少上面这些序列都是通过入栈出栈操作形成的合法序列。而对于上面这三个数字来说,它们所能够形成了所有排列有6个。针对上面的5个序列来看,有一个排列是不能生成的。这个序列是2, 0, 1。
那么对于这个不能生成的序列,它有什么特点呢?因为在这里,当2出栈的时候,在栈里它下面的元素必然都是比它小的元素。一旦这些比它小的元素压入栈之后并没有马上出栈,它们就只能等到上面这些大的元素出了之后才能出了。
而且这个规律还要一个比较有意思的递归特性。对于任何一个元素来说,当它进栈的时候,它下面的元素必然都比它小。因为我们前面所有元素都是按照从小到大的顺序进栈的。而出栈的时候呢,当这个大的元素出栈的时候,后面接着要出栈的元素里只要是原来它下面的元素,就必然一个比一个小。所以,我们也可以概括成这样,对于栈里任何一个出栈的元素来说,生成的序列里它后面所有比它小的元素必然构成一个递减的序列。 所以,按照这个规律,我们可以来判断一个给定的序列是否为通过入栈出栈生成的。
public static boolean validSequence(int[] a) { for(int i = 0; i < a.length; i++) { int cur = a[i]; for(int j = i + 1; j < a.length; j++) { if(a[j] < a[i]) { if(a[j] > cur) return false; else cur = a[j]; } } } return true; }
这里的代码实现就是从一个序列里遍历每个元素,去看后面所有比它小的元素是否构成一个递减的序列,如果没有就返回false,否则返回true。这种实现比较简单,但是性能方面有一些值得改进的地方。它的时间复杂度为O(N^2)。因为对于某个元素来说,比如3,如果我们判断过后面所有比它小的元素0, 1, 2都是符合条件的。对于序列中3后面的元素,比如4来说,对于0, 1, 2的情况其实都不用再去判断了。同样,对于比3小的元素,我们这么遍历的时候都可以直接跳过去。
所以,为了实现这么一个改进,我们新增加了一个数组boolean[] marked,用来表示前面已经比较过的元素,一旦这些比较过的元素已经符合前面描述的递减关系,我们就标记marked[i] = true,在循环中把这些情况的跳过去。
pulic static boolean isValid(int[] a) { boolean[] marked = new boolean[a.length]; for(int i = 0; i < a.length; i++) { if(marked[i]) continue; int cur = a[i]; marked[i] = true; for(int j = i + 1; j < a.length; j++) { if(marked[j]) continue; if(a[j] < a[i]) { if(a[j] > cur) return false; else cur = a[j]; marked[j] = true; } } } return true; }
按照这种思路,上面代码的时间复杂度在最理想情况下可以达到O(N)。
总结
这是一个看似不起眼的问题,实际上深入分析的时候并不好解决。需要发现它的一些规律。从它引申出来的一些问题比如这种序列的个数之类的包含了不少数学的东西,比如卡塔兰数之类的。比较有意思,以后有机会再深入的分析分析。
相关推荐
栈是一种线性数据结构,具有后进先出(LIFO, Last In First Out)的特点,只允许在表的一端(栈顶)进行插入和删除操作。在计算机科学和编程中,栈常常用于实现内存管理、函数调用、表达式求解等多种任务。 在给定...
回溯法是一种试探性搜索技术,适用于求解具有多个解空间的问题。在车厢调度问题中,回溯法被用来遍历可能的调度状态树,通过不断试探和回退,寻找所有可能的车厢序列。这种方法的核心在于维护状态树的遍历路径,一旦...
栈是一种特殊的线性数据结构,其特点是后进先出(LIFO),即最后存入的数据最先被取出,这与火车车厢的进出站顺序相吻合。 首先,我们要理解栈的工作原理。栈的基本操作包括压栈(Push)和弹栈(Pop)。压栈是将...
文档中的内容涵盖了多个知识点,主要涉及数据结构与算法、栈的操作序列判断、动态规划以及二叉树的层次遍历。以下是对这些知识点的详细说明: 1. 冒泡排序算法: 冒泡排序是一种简单的排序算法,它重复地遍历要排序...
栈是一种后进先出(LIFO)的数据结构,元素的进出栈操作受限,每个元素只能入栈一次和出栈一次。在给定初始为空的栈和入栈序列的情况下,可以根据栈的特性推断合法的出栈序列。 10. 树的数据结构 树结构描述了数据...
商品货架管理和括号匹配检验两个实验则展示了栈在实际问题中的应用,前者通过栈模拟商品货架的管理,后者则通过检查期待的紧迫程度来判断括号序列的合法性。最后,停车场管理实验涉及队列的概念,模拟车辆的进出顺序...
- **解析**:栈是一种先进后出(First In Last Out, FILO)的数据结构,根据这一特性,只有选项B是可能的出栈序列。 #### 15. VBA逻辑值的算术运算 - **题目解析**:该题考查了VBA中逻辑值的算术运算。 - **解析**...
- **进出栈序列**:给定一系列元素的入栈序列,可以通过合理的入栈和出栈操作得到不同的出栈序列。但是,并非所有的出栈序列都是合法的。 ### 8. E-R图与关系模型 **知识点:** - **E-R图**:实体-关系图,用于...
实验分析显示,递归是构建和遍历二叉树的一种强大工具,而栈则用于处理非线性遍历的问题,尤其是在前序遍历中处理右子树。通过这次实验,我们可以深入理解递归算法的工作原理以及数据在栈中的进出过程,这对理解...
括号匹配检验则需检测输入的括号序列是否合法。最后,停车场管理实验需要设计算法管理车辆的进出,模拟只有一个车位的停车场情况。 这些实验涵盖了数据结构中的基本概念,如线性表、栈、队列以及链表操作,同时也...
题目中描述了一种元素进出栈和队列的场景,用来测试栈的容量。由出队序列可知,栈的最小容量至少为4,因为需要至少4次入栈操作才能得到给定的出队序列。 5. C语言表达式:C语言中的位运算符|表示按位或,所以(25 | ...
N-S图(Nassi-Shneiderman图)是一种替代方案,它采用矩形框来表示程序的不同部分,如初始化、处理、判断等,通过这种方式提高了程序的结构化和可读性。 #### 结构化程序设计 结构化程序设计强调程序的清晰性和可...
该文档是一个关于数据结构实验的报告,主要模拟了一个停车场管理系统,使用了栈和队列作为核心数据结构。在这个系统中,停车场的狭道部分使用顺序栈(SeqStackCar)进行管理,而便道部分则使用链式存储的队列...
1. **栈的基本概念**:栈是一种后进先出(Last In First Out, LIFO)的数据结构。 2. **栈的操作**:入栈(push)、出栈(pop)。 3. **栈的性质**:栈中的元素按照后进先出的原则进出栈,这意味着最后一个入栈的...
栈是一种后进先出(LIFO)的数据结构,非常适合模拟车辆的进出顺序。当停车场未满时,新到达的车辆会直接进入停车场,即压入栈顶。当有车辆离开时,最后进入的车辆(栈顶元素)必须先退出以为空位,这通过调用栈的...
2. 栈的特性:栈是一种后进先出(LIFO)的数据结构,栈顶指针的变化决定了元素的进出,因此选项C是正确的。 3. 软件测试的目的:软件测试主要是为了发现程序中的错误,而不是改正它们,所以选项D是正确的。 4. ...
- **合法的出栈序列**:在栈数据结构中,元素的进出遵循“后进先出”(LIFO)的原则。对于给定的进栈序列FEDCBA,D选项BCDAEF无法通过合法的进栈和出栈操作得到。 ### 13. 后缀表达式的生成 - **中缀转后缀**:...
11年08级网络安全复习重点(胡道元) 1、风险分析是对需要保护的资产(物理资源、知识资源、时间资源、信誉资源)的 鉴别...远程操作系统识别的主要方法有系统服务旗标识别、主动协议栈 指纹探测(ICMP响应分析、TCP报
11年08级网络安全复习重点(胡道元) 1、风险分析是对需要保护的资产(物理资源、知识资源、时间资源、信誉资源)的 鉴别...远程操作系统识别的主要方法有系统服务旗标识别、主动协议栈 指纹探测(ICMP响应分析、TCP报