我们今天继续来看看周五留下的习题:

面试题:输入两个整数序列,第一个序列表示栈的压入顺序,请判断二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如:压入序列为{1,2,3,4,5},那{4,5,3,2,1} 就是该栈的弹出顺序,而{4,3,5,1,2} 明显就不符合要求;

这道题还是比较容易想到思路,很直观的想法就是建立一个辅助栈,把输入的第一个序列中的数字依次压入该辅助栈,并按照第二个序列的顺序依次从该栈中弹出数字。

提前想好测试用例

一样是老方法,我们先准备测试用例:

  • 传入两个 null,或者 1 个 null,或者空数组,此时应该不符合要求;
  • 传入两个不相等的数组,应该直接不符合要求;
  • 分别传入题干的示意值,他们应该分别满足和不满足要求;
  • 传入单个数字,选择一组满足要求的和一组不满足要求的;

思考程序逻辑

判断一个序列是不是栈的弹出序列的规律:如果下一个弹出的数字刚好是栈顶数字,那么直接弹出。如果下一个弹出的数字不在栈顶,我们把压栈序列中还没有入栈的数字压入辅助栈,直到把下一个需要弹出的数字压入栈顶为止。如果所有的数字都压入栈了仍然没有找到下一个弹出的数字,那么该序列不可能是一个弹出序列。

然而有的小伙伴还是容易被绕晕,这时候不妨我们可以直接作一个表格来模拟他们的压栈出栈过程,数据就采用我们题设中的数据吧~

用作图和模拟数据更容易给面试官一个你是个喜欢思考的好同事哟~

首先看看我们正确的数据。

判断操作 操作 栈 弹出数字
栈没数据,push 数组还有数据、压入 压入 1  
栈顶是 1,不等于pop[0],push 数组还有数据、压入 压入 1、2  
栈顶是 2,不等于pop[0],push 数组还有数据、压入 压入 1、2、3  
栈顶是 3,不等于pop[0],push 数组还有数据、压入 压入 1、2、3、4  
栈顶是 4,等于pop[0],弹出数字 4 弹出 1、2、3 4
栈顶是 3,不等于pop[1],push 数组还有数据、压入 压入 1、2、3、5  
栈顶是 5,等于pop[1],弹出数字 5 弹出 1、2、3 5
栈顶是 3,等于pop[2],弹出数字 3 弹出 1、2 3
栈顶是 2,等于pop[3],弹出数字 2 弹出 1 2
栈顶是 1,等于pop[4],弹出数字 1 弹出   1

实际上我们在草稿纸上并不需要做这么标准的表格,只要能表现意思即可。

我们仔细观察可以得知,我们判断压入还是弹出甚至是得出确定结论的标准是,先看当前栈顶元素和弹出的数字是否相等,如果相等则直接弹出;如果不相等则直接看看压入数组中还有没有元素,如果有则直接压入到辅助栈;如果已经没有数据则代表第二个序列不是第一个序列的弹出栈。

编写程序代码

实际上我们心中已经大概知道怎么写了。

private static boolean isPushStack(int[] push, int[] pop) {
if (push == null || pop == null || pop.length != push.length)
return false;
Stack<Integer> stack = new Stack<>();
int j = 0;
for (int i = 0; i < pop.length; i++) {
// 第一步判断栈顶元素是否和 pop[i] 相等
if (!stack.isEmpty() && pop[i] == stack.peek()) {
// 如果相等则直接 pop()
stack.pop();
} else {
// 栈顶和 pop[i] 不相等,则判断 push 数组还有没有数据
// 如果 push 数组没数据了,栈顶元素又不等于 pop[i],则说明不符合要求
if (j == push.length)
return false;
while (j < push.length) {
// 如果还有数据,则直接 push
stack.push(push[j]);
++j;
// push 后继续判断栈顶元素是否和 pop[i] 相等;
if (pop[i] == stack.peek()) {
// 如果相等则弹出栈,并且推出内层循环
stack.pop();
break;
}
}
}
}
return true;
}

验证测试用例

写毕代码后,我们得用自己事先准备的测试用例测试一下。

  • 测试 1 和测试 2 我们已经考虑到了,这样的情况直接在功能之前就判断,不符合条件的直接返回 false,测试通过。

  • 传入{1,2,3,4,5} 和 {4,5,3,2,1}:

    1. 进入循环,i = 0,pop[i] = 4,直接进入 else 语句,开始 push 数据,一直 push 到 j = 3。
    2. 此时栈内元素为 {1,2,3,4},push 里面还剩下 {5}。因为 pop[0] 等于栈顶,所以进入 if 语句,弹出 4,退出 while 循环;
    3. i = 1,栈内元素为{1,2,3},栈顶元素不等于 pop[1] = 5,进入 else 语句。push 数组还有数据,直接 push,结束后 j = 5,栈内元素为 {1,2,3,5},栈顶刚好等于 pop[1],故弹出数字 5,退出 while 循环;
    4. i = 2,栈内元素为{1,2,3},栈顶元素刚刚等于 pop[2] ,弹出数字 3;
    5. i = 3,栈内元素为 {1,2},栈顶元素刚等于 pop[3],弹出数字 2;
    6. i = 4,栈内元素为 {1},栈顶元素刚刚等于 pop[4],弹出数字 1;
    7. for 循环能直接执行结束,返回 ture,测试通过。
  • 传入 {1,2,3,4,5} 和 {4,3,5,1,2}:

    1. 进入循环,i = 0,pop[i] = 4,由于同上,所以直接进入到上面的步骤 3;
    2. 此时 i = 1,栈内元素为 {1,2,3},因为栈顶元素等于 pop[1],弹出数字 3;
    3. i = 2,栈内元素为 {1,2},栈顶元素不等于 pop[2],进入 else 语句,此时 push 数组还有元素 {5},所以进入 while 循环。push 后栈内元素为 {1,2,5},栈顶元素等于 pop[2],所以弹出数字 5,退出 while 循环;
    4. i = 3,栈内元素为{1,2},pop[3] = 1,和栈顶元素不相等,所以进入 else 语句,由于 push 里面已经没有了元素,所以直接返回 false,测试通过。
  • 传入 {1} 和 {2}:

    进入循环,i = 0,pop[i] = 2,进入 else 语句,不相等,直接进入 while 循环,push 后栈内元素为 {1},栈顶元素和 pop[i] 不相等,此时 j = 1,不符合 while 循环条件。循环结束,外循环也结束,返回 true。测试不通过。

修复程序逻辑

所以我们现在应该着重处理一下单个数字的情况,分析后明显可以得到,我们要判断这种情况只需要再判断结束 for 循环后栈内是否还有元素和 push 里面还是否有元素即可。

所以在最后增加一个条件判断即可。

public class Test16 {

    private static boolean isPushStack(int[] push, int[] pop) {
if (push == null || pop == null || pop.length != push.length)
return false;
Stack<Integer> stack = new Stack<>();
int j = 0;
for (int i = 0; i < pop.length; i++) {
// 第一步判断栈顶元素是否和 pop[i] 相等
if (!stack.isEmpty() && pop[i] == stack.peek()) {
// 如果相等则直接 pop()
stack.pop();
} else {
// 栈顶和 pop[i] 不相等,则判断 push 数组还有没有数据
// 如果 push 数组没数据了,栈顶元素又不等于 pop[i],则说明不符合要求
if (j == push.length)
return false;
while (j < push.length) {
// 如果还有数据,则直接 push
stack.push(push[j]);
++j;
// push 后继续判断栈顶元素是否和 pop[i] 相等;
if (pop[i] == stack.peek()) {
// 如果相等则弹出栈,并且推出内层循环
stack.pop();
break;
}
}
}
}
// 增加判断
if (!stack.isEmpty() && j == push.length)
return false;
return true;
} public static void main(String[] args) {
int[] push = {1, 2, 3, 4, 5};
int[] pop1 = {4, 5, 3, 2, 1};
int[] pop2 = {3, 5, 4, 2, 1};
int[] pop3 = {4, 3, 5, 1, 2};
int[] pop4 = {3, 5, 4, 1, 2};
System.out.println(isPushStack(push, pop1));
System.out.println(isPushStack(push, pop2));
System.out.println(isPushStack(push, pop3));
System.out.println(isPushStack(push, pop4));
int[] push1 = {1};
int[] pop5 = {2};
System.out.println(isPushStack(push1, pop5));
int[] push2 = {1};
int[] pop6 = {1};
System.out.println(isPushStack(push2, pop6));
}
}

上面在代码逻辑上并没有做多少操作,所以我们只需要再传入 {1} 和 {1} 测试就可以了。

直接进入到 while 循环,push 后栈内元素为 {1},因为栈顶元素刚刚等于 pop[0],所以推出数字 1。此后栈内无元素,所以直接返回 true。

总结

我亲爱的小伙伴想必也一定在上面的分析中收获到东西了吧,这也是南尘给大家的箴言。

  • 在思路不是很清晰的时候画表或者画图来处理;
  • 在验证测试用例的时候,一定从简单的开始,比如上面,我们其实更加建议先验证单个数字的情况。

拓展延伸

本次学习的方法将非常有效,不信大家可以试试下明天的拓展题。

面试题:从上到下打印二叉树的每个结点,同一层按照从左到右的顺序打印。例如数的结构如下:

​ 1
2 3
4 5 6 7

则依次打印 1、2、3、4、5、6、7

 

面试 16:栈的压入压出队列(剑指 Offer 第 22 题)的更多相关文章

  1. 《剑指Offer》附加题_用两个队列实现一个栈_C++版

    在<剑指Offer>中,在栈和队列习题中,作者留下来一道题目供读者自己实现,即"用两个队列实现一个栈". 在计算机数据结构中,栈的特点是后进先出,即最后被压入(push ...

  2. 牛客网剑指offer第21题——判断出栈序列是否是入栈序列

    题目: 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈 ...

  3. 面试 15:顺时针从外往里打印数字(剑指 Offer 第 20 题)

    面试 15:顺时针从外往里打印数字 题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印每一个数字.例如输入: {{1,2,3}, {4,5,6}, {7,8,9}} 则依次打印数字为 1.2.3. ...

  4. 面试题07_用两个栈实现队列——剑指offer系列

    题目描写叙述: 用两个栈实现一个队列. 队列的声明例如以下,请实现它的两个函数appendTail 和 deleteHead.分别完毕在队列尾部插入结点和在队列头部删除结点的功能. 解题思路: 栈的特 ...

  5. 《剑指offer》第九题(用两个栈实现队列)

    // 面试题:用两个栈实现队列 // 题目:用两个栈实现一个队列.队列的声明如下,请实现它的两个函数appendTail // 和deleteHead,分别完成在队列尾部插入结点和在队列头部删除结点的 ...

  6. 5、两个栈实现队列------------>剑指offer系列

    题目 用两个栈来实现一个队列,完成队列的Push和Pop操作. 队列中的元素为int类型. 思路 栈1: 用于入队列存储 栈2: 出队列时将栈1的数据依次出栈,并入栈到栈2中 栈2出栈即栈1的底部数据 ...

  7. [剑指offer] 5. 用两个栈实现队列+[剑指offer]30. 包含min函数的栈(等同于leetcode155) +[剑指offer]31.栈的压入、弹出序列 (队列 栈)

    c++里面stack,queue的pop都是没有返回值的, vector的pop_back()也没有返回值. 思路: 队列是先进先出 , 在stack2里逆序放置stack1的元素,然后stack2. ...

  8. 剑指Offer系列之题16~题20

    目录 16.反转链表 17.合并两个排序的链表 18.树的子结构

  9. [互联网面试笔试汇总C/C++-9] 实现赋值运算符函数-剑指offer

    题目:如下为类型CMyString的声明,请为该类型添加赋值运算符函数. class CMyString { public: CMyString(char* pData = NULL); CMyStr ...

随机推荐

  1. Linux下图形数据库Neo4j单机安装

    Neo4j数据库简介 Neo4j 是一个NoSQL的图形数据库(Graph Database).Neo4j使用图(graph)相关的概念来描述数据模型,把数据保存为图中的节点以及节点之间的关系.很多应 ...

  2. css图片垂直水平居中及放大(实现水平垂直居中的效果有哪些方法?)

    实现水平垂直居中方法有很多种: 一.万能法: 1.已知高度宽度元素的水平垂直居中,利用绝对定位和负边距实现. <style type="text/css"> .wrap ...

  3. Spring入门详细教程(三)

    前言 本篇紧接着spring入门详细教程(二),建议阅读本篇前,先阅读第一篇和第二篇.链接如下: Spring入门详细教程(一) https://www.cnblogs.com/jichi/p/101 ...

  4. iOS开发创建UI的耗时操作处理

    项目中有网络请求.读写操作等一系列耗时操作时,为了避免阻塞主线程,我们会把这些耗时操作放到子线程中去处理,当处理完成后,再回到主线程更新UI,这样就不会阻塞主线程.但是创建UI的时候一般都是在主线程中 ...

  5. 使用concurrent.futures模块中的线程池与进程池

    使用concurrent.futures模块中的线程池与进程池 线程池与进程池 以线程池举例,系统使用多线程方式运行时,会产生大量的线程创建与销毁,创建与销毁必定会带来一定的消耗,甚至导致系统资源的崩 ...

  6. adb入门学习笔记

    连接模拟器(模拟器桥接模式) 使用adb devices列出已连接到工作站的设备. 使用adb shell 启动设备或模拟器上的shell. 列出设备已安装的所有软件包 将电脑文件移动到手机模拟器上( ...

  7. [Hive_add_11] Hive 使用 UDTF 实现日志降维

    0. 说明 对日志进行降维处理,将日志分为几个小表 通过编写 UDTF ,对日志降维,将日志聚合体相关字段抽取出来,形成新表. 1. 操作流程 1.0 日志部分内容 ##{\"appChan ...

  8. PHP中Snoopy类的使用

    最近看PHP书籍,发现了一个好东东,就是Snoopy类.Snoopy是一个php类库,用来模拟浏览器的功能,可以获取网页内容,发送表单. Snoopy的一些特点: 1抓取网页的内容 fetch 2 抓 ...

  9. February 11th, 2018 Week 7th Sunday

    Grasp all, lose all. 欲尽得,必尽失. Not to be greedy and not to try to get everything. Our time, energy an ...

  10. python3内置函数练习

    1. abs() abs() 函数返回数字的绝对值. print(abs(1.5236)) # 1.5236 print(abs(1.0)) # 1.0 2. all() all() 函数用于判断给定 ...