写这篇的灵感,是源自看到网上一个Google的面经。以下一段话引自面经原文:
“第三道题目他先是说这东西可能没用,说stack和queue哪个更基本一些,我马上说stack,并告诉他我知道是stack,但不知道为什么是stack,他又具体举了个例子,说1和-1哪个更基本,我几乎没思考就说是1,他说是-1,因为-1*-1可以得到1,而1怎么也得不到-1,我辩解说这要看你怎么定义“基本”这个词,于是他回到了stack和queue,说其中一个可以模拟另一个,而反之不行,我重复了他的问题,说等同于系统只提供了stack这个数据结构,没有提供queue,要我用stack模拟queue,但是最后我绕了一大圈,把问题搞得很复杂,甚至往hanoi方向上想,也没想出如何解决这个问题,当然挂了电话以后很快就知道怎么弄了,一只手拿着电话听筒确实没法思考...”
看完这份面经,我的第一反应是惊讶。记得在大四的时候Intel给我电面时,Danming就问我“如何用两个栈实现一个队列”,我当时的回答让他很满意。那个时候都能轻松答出的题目,现在想当然觉得不困难。看到Google也会问这么简单的题目,不免有点惊讶。
静下心来,透过现象看本质。同样一个问题,感觉这个Google的面试官问出来就很有哲理。但是当我细心想想之后的时候,才发现这其中漏洞百出。
-1和1哪个更基本
哪个更基本?我想每个人的第一反应都会像求职者所答一样:是1。答后却得到面试官的否定一定让人不免有些惊讶。面试官补上一句貌似颇有道理的解释,又让人貌似恍然大悟。但如求职者所辩解,怎么定义“基本”这个词?这个面试官根本没有给出严谨的定义。因为两个-1相乘可以得到1,所以-1更基本?那么我们可以说用三个1,让其中一个减去另外两个就可以得到-1。又如何解释?或是更简单的,对一个-1做平方运算,会得到1;反之,对1做开方运算,也可以得到-1。这里有一个让人混淆的一点就是,我们除了运算子是-1或1之外,没有在意运算的本质又引入了其他的数字。例如-1*-1=1,实际是有两个运算子,面试官此时已经悄悄引入了一个数字2。哪怕是一元运算符平方即2次方,也蕴含着一个2在其中。学计算机的人都知道,有了2,就有了世界。
那么究竟哪个真的更基本一些?我相信答案是1。我也相信面试官小学数学先学到的应该是1,而后学的-1。因为如果没有1,-1不会有任何意义,就根本不会存在-1。
堆栈可以模拟队列
这是面试官信心满满提出的问题,求职者在放下电话之后也恍然大悟,其实答案很简单。如果你之前没有想过这个问题,请先不要继续读下去,可以先独立思考一个这个答案,再看后文。其实答案很简单,就是把数据push到栈A,再把栈A的数据依次pop并push到栈B。那么先入栈A的数据就会移到栈B的顶部,对栈B进行pop时就会先出栈B。恰恰符合了队列的本质——First in First out。
反之不行?
往往前辈(一个统称,包括老师,面试官,老板,领导等)说不行的,给人们的感觉就是“真的不行”。而忽略了两个问题“是否真的不行?”和“为什么不行?”学习任何知识,思考这两个问题都是有益的,绝对不会多余,哪怕真的是“不行”。所以我就去想,结果发现两个队列也是可以模拟堆栈的。方法也很简单:把数据都enqueue到队列A,如果你想取出最后一个enqueue的数据(即Last in),那么就把队列A的数据依次dequeue并enqueue到队列B,但是不能全部移到队列B,要留下一个。这一个就是你需要的数据把它dequeue出来,就会得到Last in的数据。这个过程,相当于对栈进行了依次pop操作,符合——Last in First out。需要再次pop的时候,按照同样的方法,只需将对A、B的操作交换就可以了。
如此简单的问题,不知道为什么面试官却说了一句“反之不行”。即便真的是反之不行,就真的能说明栈比队列更“基本”吗?
一个堆栈和一个队列也可以相互转换?
刚刚看到网上有些人总结的面试题中出现了“用一个堆栈实现一个队列”和“用一个队列实现一个堆栈”,不免让我有些吃惊。第一反应是不可行,为什么不可行呢?比如,你想要一个栈底部的数据,必然需要将顶部的数据pop出来啊,那么pop出来之后放在哪里呢,前提是只有一个栈,没有别的数据结构了啊?抱着这种质疑的态度,继续看答案:“用一个堆栈实现一个队列。思路:用递归的方法把数据从最底部移出来。用一个队列实现一个堆栈。思路:对于每次pop,用递归的方法反转队列元素的排列,然后返回第一个元素”。答案思路后面还跟着一份实现的代码。我看了思路已经心知肚明,没有再看代码。因为我看到“递归“二字,答题者在这里用”递归“的说法悄悄的引入了一个栈,这与利用-1*-1=1来引入2是同样的伎俩。也就是说,所谓的“用一个堆栈实现一个队列”,实质还是用了两个栈。
后记
这个面试的题目其实没有多大的意思,不论是用栈来实现队列还是用队列实现栈,本质是要把当前拥有的数据结构中的数据都挪到一边去,取出来剩下的最后那一个,就是需要的结果。至于前面那些数据都挪到哪里了,可以说只要能装得下,挪到了哪里都无所谓。可以这样说,一个栈加”另一个数据结构“就可以实现一个队列(反之亦然),这”另一个数据结构“可以是一个栈,也可以是一个队列,哪怕是一个b树,一个图,都无所谓。
以上的所有,都是我弱弱的思考,目的仅是品味愉快的思考过程,观点中必然有不严谨不科学的各种问题,非常欢迎大家拍砖。
ref:http://blog.csdn.net/baby313/article/details/8059884
分享到:
相关推荐
理解各种数据结构如堆栈、队列、链表、树等,以及它们之间的相互转换和组合,是成为一名优秀程序员的基础。 至于压缩包子文件中的".DS_Store"是一个Mac OS X系统特有的隐藏文件,它包含了目录视图的相关设置,通常...
虽然这不是最直接或效率最高的实现方式,但它展示了数据结构间转换的概念,并且可以作为理解数据结构相互关系的一个练习。在实际应用中,使用原生的Array对象提供的push和shift方法通常能更高效地实现栈操作。不过,...
这门课程旨在使学生理解和掌握不同数据结构(如线性表、堆栈、队列、树、图)的特性和算法,以便在实际问题中选择合适的数据结构并进行有效操作。此外,课程还强调算法的时间分析和空间效率,培养学生的数据抽象能力...
掌握在表达式求值和打印杨辉三角的方法中堆栈和队列所起的作用,并了解其算法。 ### 第四章串 * 串的定义、存储结构及其基本算法 * 教学要求:掌握串的定义、存储结构及其相应算法的实现。 ### 第五章数组和广义...
9. 数据结构中的线性表、数组、堆栈和队列都是基于连续存储单元的数据元素序列。 10. 计算机控制系统中的输入变送器和输出执行机构通常采用0~10mA或4~20mA的直流信号作为统一标准。 名词解释: 1. 采样过程:将连续...
6. **堆栈和队列**:除了Java标准库中的`Stack`和`Queue`,Commons Collections提供了`StackUtils`和`QueueUtils`,包含了一些额外的操作,如`createStack()`、`createQueue()`,以及各种转换和操作方法。...
1. **栈和队列**:栈是一种后进先出(LIFO)的数据结构,意味着最后插入的元素最先被删除,通常用于临时存储和处理数据,如函数调用中的调用堆栈。队列则是先进先出(FIFO)的数据结构,就像一个排队系统,先来的...
- **堆栈和队列的变种**:如堆栈实现的LRU缓存淘汰策略、队列实现的BFS搜索。 3. **算法与数据结构的关系**: - 算法依赖于数据结构:不同的数据结构为算法提供了不同的操作可能性,例如二叉搜索树支持快速查找,...
堆栈和队列常用于进程调度和I/O操作的管理;树结构在文件系统中用于表示文件和目录的层次关系。 3. 索引式文件组织的优点:索引式文件组织允许快速定位数据,便于用户存取,尤其适合大数据量的文件系统。 4. 文件...
使用锁来确保线程A和线程B对队列的操作是互斥的; 3. 使用条件变量来通知线程B队列中有新数据可供读取。 **示例代码**(Python): ```python import threading import queue class Producer(threading.Thread): ...
另外,进程通常还包括进程堆栈段(包括临时数据,如函数参数、返回地址和局部变量)和数据段(包括全局变量)。进程还可能包括堆(heap),它是在进程运行期间动态分配的内存。 从操作系统管理角度出发,进程由数据...
4. **commons-collections-3.2.1.jar**:Apache Commons Collections提供了对Java集合框架的增强,包括新的集合类、迭代器、比较器、堆栈、队列等。在处理JSON时,可能需要使用这些高级数据结构来存储和操作数据。 ...
`push()`和`pop()`可以模拟堆栈操作,`shift()`和`unshift()`则模拟队列操作。 - **多维数组**: 可以创建嵌套数组,即数组的元素也是数组,用于表示矩阵或其他复杂数据结构。 - **ES6新增的数组特性**: 包括...
该章节介绍了树和森林的概念、遍历和存储结构,还有树、森林和二叉树的相互关系,树或森林怎样转化成二叉树,二叉树又如何转换为树和森林等算法。 第八章:散列结构 散列结构是一种查找效率很高的一种数据结构。本...
它具备基本的运算功能,可以处理数值、字符串和字符,还支持数组,能够构造出多种数据结构,如堆栈、队列、树和图。 总的来说,Raptor是一个理想的入门级编程工具,它通过可视化的方式帮助学生建立计算思维,理解...
这篇学习笔记将探讨几个关键概念,包括`auto`的使用、类的对象和成员、`static`关键字、访问控制(public/protected/private)、对象类型的转化、堆栈队列的理解、内联函数以及引用类型。 1. `auto`在C++中的使用:...
二叉树与树、林的相互转换 二叉树可以转换为树,树也可以转换为二叉树。林也可以转换为二叉树,反之亦然。 例如,我们可以将一棵二叉树转换为树,将一棵树转换为二叉树,将林转换为二叉树。 哈夫曼树 哈夫曼树是...
通过学习二叉树的基本操作实现,以及树、森林与二叉树之间的相互转换,读者将深刻理解树的遍历方法,同时接触到了Huffman树和编码的概念,进一步拓宽了解决实际问题的思路。 ### 第七章:图 图论是现代计算机科学...
- **树与森林的转换与遍历**:讲解了树、森林与二叉树之间的相互转换方法,以及树和森林的遍历算法,还讨论了根据遍历序列还原树结构的技术。 - **Huffman树**:介绍了二叉编码树和Huffman树的概念,以及Huffman编码...