常见面试算法题JS实现-设计一个有getMin功能的栈

作者:分分快三计划

正文:

代码实现

classSolution{
publicbooleanisValid{
Stack<Character>stack=newStack<>();
char[]chars=s.toCharArray();
for(charaChar:chars){
if(stack.size()==0){
stack.push;
}elseif(isSym(stack.peek{
stack.pop();
}else{
stack.push;
}
}
returnstack.size()==0;
}

privatebooleanisSym(charc1,charc2){
return(c1=='('&&c2==')')||(c1=='['&&c2==']')||(c1=='{'&&c2=='}');
}
}

那么CPU如何知道一段内存空间被当作栈来使用呢?

我们前面讲过,CS:IP指向的内容是代表指令下一条要执行的指令的内存地址。显然栈是操作栈元素,也应该有相应的寄存器来保存栈顶元素内存地址,8086CPU中有2个寄存器,段寄存器SS(stack segment),寄存器SP,栈顶的段地址存放在SS中,偏移地址存放在SP中。当执行push或者pop指令时,CPU从SS和SP中得到栈顶地址,任意时刻,SS:SP指向栈顶元素。

我们可以描述 push ax 指令的执行过程:
1、首先将SP = SP - 2
2、然后,ax中内容送入SS:SP指向的内存单元处,此时SS:SP指向了新的栈顶。

分分快三计划 1

push指令执行过程示意图

从图中我们可以看出,push指令执行的时候,栈顶由高地址向低地址方向移动。

接下来,我们描述pop指令执行的过程
pop ax执行的过程,正好和push ax相反。
1、首先将SS:SP指向的内存单元的数据送入寄存器ax中
2、将SP = SP 2,SS:SP指向了当前栈顶下面的单元,以移动后的新的单元作为栈顶。

分分快三计划 2

pop指令执行过程.png

注意,我们看到执行完pop以后呢,2266H数据依然存在,但是它已经不在栈中,之后如我们执行push,会有新的数据将它覆盖。

分分快三计划,  这个是计划写成一个系列,主要参考的就是左大神的《程序员代码面试指南——IT名企算法与数据结构题目最优解》,左大神在书里是用JAVA实现的,基本看得懂,但是因为我是用JS的,总觉得差点意思,反正也是学习,干脆就自己实现JS的写法,并且分享出来,也算是让我继续坚持的一个动力,当然,因为本人是菜鸟小白,肯定或多或少会出现一些问题,希望各位大牛们在嘲笑之余能够请不吝赐教~康桑阿米达~阿尼嘎多~Thx~谢谢~

题目四:包含 min 函数的栈

栈的总结

  • 8086CPU提供了栈,在SS:SP存放栈的段地址和偏移地址。
  • 提供了push 和 pop 指令,它们将根据SS:SP指向的地址,按照栈的方式访问这个内存单元。
  • push执行的步骤
    • 首先将SP = SP - 2,将偏移地址向低位减2
    • 然后向当前SS:SP指向的新地址传送字单元的数据
  • pop执行步骤
    • 首先将SS:SP指向的内存字单元的数据输出
    • 将SP = SP 2,将偏移地址向高位加2
  • 任意时候,SS:SP指向栈顶元素。
  • 8086CPU只记录栈顶元素,而不管理栈的大小和超界问题。
  • CPU将内存单元中的某段内容当作代码,是因为CS:IP指向了那里,CPU将某一段内存中当作数据,是以为DS指向了那里,CPU将某段内存单元当作栈,是因为SS:SP指向了那里。

  【思路】定义一个stackData和一个stackMin,stackData用于存放实际数据,stackMin用于存放stackData中的最小值。重写pop和push方法,实现stackData和stackMin的数据同步。

代码实现

privateStack<Integer>dataStack=newStack<>();
privateStack<Integer>minStack=newStack<>();

publicvoidpush(intnode){
dataStack.push;
minStack.push(minStack.isEmpty()?node:Math.min(minStack.peek;
}

publicvoidpop(){
dataStack.pop();
minStack.pop();
}

publicinttop(){
returndataStack.peek();
}

publicintmin(){
returnminStack.peek();
}

push 和 pop 指令 栈与内存

我们一直使用push ax 和 pop ax,显然push 和 pop 指令是可以在寄存器和栈空间之间传送数据的。栈空间也是内存空间一部分,它只是一段可以以特殊方式进行访问的内存空间。

push 寄存器   // 将一个寄存器的内容入栈
pop  寄存器   //将栈顶的元素送入寄存器

push 段寄存器   // 将一个寄存器的内容入栈
pop  段寄存器   //将栈顶的元素送入寄存器

同样,push 和 pop 也可以在内存单元和内存单元之间传送数据。

mov ax,1000H
mov ds,ax  //内存单元的段地址 要放在ds段寄存器中
push [0]     //将1000:0处的字单元入栈
pop  [2]    //用1000:2处的字单元接受出栈数据

  已经确定工作了~下周一正式入职,按理说应该是可以好好浪荡一周的,但是内心总是不安,总觉得自己这个水平真的太菜了,还是趁着现在有自己的时间,赶紧多看看书,多学习学习吧orz所以把之前校招买的书,又翻出来看,都是很经典的书,但是因为自己找到工作之后就放纵了,几乎都放在书架上长灰,现在拿出来,一是希望自己能够养成一个学习的好习惯,即使在工作忙的时候,依然要挤出一点时间学习新的知识,不能得过且过,二是希望记录一下正在努力时的自己,也算是跟想要偷懒时的自己说,“喂,懒鬼,快点学习,不然你就真的对不起曾经努力的自己和以后懊悔的自己了”,嗯呢,闲话又说多了,接下来就正式开始咯~

问题描述

定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的 min 函数。

一篇前言

这是一系列的汇编语言学习的读书笔记,以及一些自己的理解系列文章。我想我会在循序渐进的学习中,写下对汇编的点点滴滴,一方面是给自己立一个短期的小目标,另一方面,如果您有缘看到了这一系列的文章,希望对您有所帮助。

  【实现】实现的方式有两种,详见代码。

代码实现

Stack<Integer>in=newStack<Integer>();
Stack<Integer>out=newStack<Integer>();

publicvoidpush(intnode){
in.push;
}

publicintpop()throwsException{
if(out.isEmpty
while(!in.isEmpty
out.push;

if(out.isEmpty
thrownewException("queueisempty");

returnout.pop();
}

栈顶超界的问题

我们知道SS:SP指示栈顶的地址,并提供push和pop指令实现入栈和出栈,但是还有一个问题,SS:SP只记录了栈顶的地址,依靠SS和SP可以保证出栈或者入栈时能够找到栈顶,可是却不能够保证入栈和出栈不会超出栈的界限。下图描述了在执行push指令后,栈顶找出界限的情况:

分分快三计划 3

执行push指令后栈顶超出栈空间.png

图中,我们将10010H~1001EH这段内存空间当作栈,在执行8次push ax后,栈已经处于满的状态,此时SS:SP指向了10010H处,如果此时,再次执行push ax,那么SS:SP将会指向1000EH,紧接着AX中的内容将会把1000H的内存字单元覆盖,这也就是将栈空间以外的数据覆盖了。

同样pop执行执行后,栈顶也会存在超界情况:

分分快三计划 4

执行pop指令后栈顶超出栈空间.png

同样,此时我们把10010H~1001F这段空间当作栈空间,该空间容量为16个字节,当前栈为满的状态,SS:SP = 10010H,在执行8次pop ax之后,SS:SP指向了10020H(栈的最后一个内存单元的下一个单元),此时,栈空。如果我们再次执行pop ax,那么会将SS:SP指向10022H处,此后,如果再执行push指令呢,会将10020H和10021H处的数据覆盖,栈超界。

栈超界是非常危险的,因为既然安排一段空间,那么栈空间之外的内存单元很可能存放了其它有用的数据,如数据,代码,这些数据和代码很可能是我们自己程序,也可能是其它程序的,但是由于我们的不小心,意外的将这些数据改写,将会引发不可想象的错误。

我们当然希望CPU可以帮助我们解决这个问题,比如CPU中存在能够记录栈顶上限和栈底的寄存器,然而8086CPU并没有,不保证程序员对栈操作不会超界,也就说CPU只知道栈顶在何处,而不知道我们安排了多大的内存空间作为栈。这就是好像CS:IP只指示当前要执行的指令,而不知道究竟有多少指令要执行,从这2点上可以看出,8086CPU的工作机制,它只考虑当前的情况:当前栈顶在何处,当前执行的指令是哪一条。

所以,我们在编程的时候,要自己考虑栈的大小,根据可能用到的最大空间,来合理安排栈的大小,防止入栈的数据太多而导致超界。同样执行出栈操作的时候,也要注意以防栈空的时候继续出栈而导致超界。

后话:

问题描述

给定一个只包括 '','{','}','[',']' 的字符串,判断字符串是否有效。

CPU提供 栈 的机制

8086CPU提供相关的指令来以栈的方式访问内存空间。这意味着我们可以在编程的时候,将一段内存空间当作栈来使用。
8086提供出栈和入栈操作的指令:
入栈:push
出栈:pop

push ax  //将寄存器ax中的数据 入栈
pop  ax  //从栈顶取出数据,送入寄存器ax中

 

题目二:用两个栈实现队列

栈的定义

栈在计算机领域应用可以为数据暂时存储的地方,实际上内存空间的分配和操作的一种方式。提供一种特殊的访问方式的存储空间,它的特殊性就在于,最先进入这个空间的数据,最后出去,而最后进入这个空间的数据,最先出去。

我们可以用一个盒子和3本书来描述栈的这种操作方式。

分分快三计划 5

入栈的方式

当我们把3本书按顺序放进盒子里,现在问题是,一次只允许取一本书,我们如何取出这个3本书。显然,我们只能从盒子的最顶端取,这样取出来书的顺序是:《软件工程》,《c语言》,《高等数学》,如下图所示:

分分快三计划 6

出栈的方式

由上面放入书和取出书的过程,我们可以看出,栈有2个基本操作:入栈和出栈。入栈就是将一个新的元素放到栈顶,而出栈呢则是从栈顶取出一个元素。

 // 方法一
 1 class MyStack {
 2     constructor() {
 3         this.stackData = [];
 4         this.stackMin = [];
 5     }
 6     push() {
 7         let args = arguments[0];
 8         if (typeof args === 'number') {
 9             //将新数据压入stackData栈中
10             this.stackData.push(args);
11             //判断是否将新数据压入stackMin栈中
12             if (this.stackMin.length > 0) {
13                 //stackMin栈不空,需要判断当前数据是否小于等于stackMin的栈顶元素
14                 let top = this.getMin();
15                 if (args <= top) {
16                     this.stackMin.push(args);
17                 }
18             } else {
19                 //stackMin栈空,则压入
20                 this.stackMin.push(args);
21             }
22         }
23     }
24     pop() {
25         if (this.stackMin.length === 0) {
26             throw new Error('Stack is empty!');
27         }
28         let p = this.stackData.pop();
29         let top = this.getMin();
30         if (p === top) {
31             this.stackMin.pop();
32         }
33         return p;
34     }
35     getMin() {
36         if (this.stackMin.length === 0) {
37             throw new Error('Stack is empty!');
38         }
39         let len = this.stackMin.length;
40         return this.stackMin[len - 1];
41     }
42 }
43 
44 let s = new MyStack();
45 s.push(4);
46 s.push(2);
47 s.push(1);
48 console.log(s.getMin());
49 s.pop();
50 console.log(s.getMin());
51 s.pop();
52 s.pop();
53 s.pop();    //抛出异常

 //方法二
 1 class MyStack {
 2     constructor() {
 3         this.stackData = [];
 4         this.stackMin = [];
 5     }
 6     push() {
 7         let args = arguments[0];
 8         if (typeof args === 'number') {
 9             //将新数据压入stackData栈中
10             this.stackData.push(args);
11             //判断是否将新数据压入stackMin栈中
12             if (this.stackMin.length > 0) {
13                 //stackMin栈不空,需要判断当前数据是否小于等于stackMin的栈顶元素
14                 let top = this.getMin();
15                 if (args <= top) {
16                     this.stackMin.push(args);
17                 } else {
18                     this.stackMin.push(top);
19                 }
20             } else {
21                 //stackMin栈空,则压入
22                 this.stackMin.push(args);
23             }
24         }
25     }
26     pop() {
27         if (this.stackMin.length === 0) {
28             throw new Error('Stack is empty!');
29         }
30         let p = this.stackData.pop();
31         this.stackMin.pop();
32         return p;
33     }
34     getMin() {
35         if (this.stackMin.length === 0) {
36             throw new Error('Stack is empty!');
37         }
38         let len = this.stackMin.length;
39         return this.stackMin[len - 1];
40     }
41 }
42 
43 let s = new MyStack();
44 s.push(4);
45 s.push(2);
46 s.push(1);
47 console.log(s.getMin());
48 s.pop();
49 console.log(s.getMin());
50 s.pop();
51 s.pop();
52 // s.pop();    //抛出异常

题目三:栈的压入、弹出序列

  【题目】实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作。

小程序

小程序名称:图解剑指offer

剑指offer上面的 66 道题目都挪上去了,每一道题目基本上都有详细说明解法,更多的解法我还在添加中,需要准备刷题的可以在坐地铁的零碎时间拿出来看看^_^

分分快三计划 7图 1 分分快三计划 8图 2 分分快三计划 9

  【要求】1. pop、push、getMin操作的时间复杂度都为O(1)

问题描述

用两个栈来实现一个队列,完成队列的 Push 和 Pop 操作。

 

代码实现

publicbooleanIsPopOrder(int[]pushSequence,int[]popSequence){
intn=pushSequence.length;
Stack<Integer>stack=newStack<>();
for(intpushIndex=0,popIndex=0;pushIndex<n;pushIndex  ){
stack.push(pushSequence[pushIndex]);
while(popIndex<n&&!stack.isEmpty()
&&stack.peek()==popSequence[popIndex]){
stack.pop();
popIndex  ;
}
}
returnstack.isEmpty();
}

前言:

解题思路

in 栈用来处理入栈操作,out 栈用来处理出栈操作。一个元素进入 in 栈之后,出栈的顺序被反转。当元素要出栈时,需要先进入 out 栈,此时元素出栈顺序再一次被反转,因此出栈顺序就和最开始入栈顺序是相同的,先进入的元素先退出,这就是队列的顺序。

  • push 元素时,始终是进入栈,pop 和 peek 元素时始终是走出栈。
  • pop 和 peek 操作,如果出栈为空,则需要从入栈将所有元素移到出栈,也就是调换顺序,比如开始push的顺序是 3-2-1,1 是最先进入的元素,则到出栈的顺序是 1-2-3,那 pop 操作拿到的就是 1,满足了先进先出的特点。
  • pop 和 peek 操作,如果出栈不为空,则不需要从入栈中移到数据到出栈。

分分快三计划 10动画 1

      2. 设计的栈类型可以使用现成的栈结构

问题描述

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列 1,2,3,4,5 是某栈的压入顺序,序列 4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

解题思路

使用两个 stack,一个作为数据栈,另一个作为辅助栈。其中 数据栈 用于存储所有数据,而 辅助栈 用于存储最小值。

举个

本文由分分快三计划发布,转载请注明来源

关键词: 分分快三计划 堆栈 队列 几道 汇编语言