栈 Stack
栈是先进后出LIFO的线性表,数据进出都在栈顶,另一端叫栈尾。
在web开发中,文本编辑器的“撤消”操作实现了栈的数据组织形式。每次将文本添加到文本编辑器时,该文本被压入栈中。其中第一次添加的文本位于栈底;最后一次的修改位于栈顶。如果用户希望撤销最后一次修改,则删除处于栈顶的那段文本,这个过程可以不断重复,一直到栈中没有更多内容,这时我们会得到一个空白文件。
用JavaScript结合原型实现栈
- push(data):添加一个或是几个新元素到栈顶。
- pop():移除栈顶的元素,同时返回被移除元素。
function Stack(){
this.size = 0
this.storage= []
}
// 把方法添加到原型对象中
Stack.prototype = {
push: function(data){
this.storage[this.size++] = data // this.size++表示push一个data时size加1
},
pop: function(){
// 判断如果栈不为空才执行pop
if(this.size){
// 3. 储存被删除元素
var deleteData = this.storage[this.size]
// 1. 删除栈顶元素
delete this.storage[this.size]
// 2. 栈大小减一
this.size--
// 4. 返回被删除元素
return deleteData
}
}
}
语法笔记
(昨天刚刚全面学习了一遍this和原型,这里结合使用巩固一下)
- 原型的使用
因为每一个栈的实例都栈的以上方法,所以它们添加到栈的prototype对象中。把所有对象实例需要共享的属性和方法直接定义在prototype对象上,可以节省内存,每次调用构造函数生成对象时,就不用都创建一遍重复的属性和方法。 - this的使用
在原型对象通过this获取的属性和方法就是构造函数中this的this.size,this.storage,在创建新对象时指向这个新对象。
直接调用js的api实现队列
给栈声明以下方法:
- push(data):添加一个或是几个新元素到栈顶。
- pop():移除栈顶的元素,同时返回被移除元素。
- peek():返回栈顶的元素,但并不对栈顶的元素做出任何的修改。
- isEmpty():检查栈内是否有元素,如果有返回true,没有返回false。
- clear():清除栈里的元素。
- size():返回栈的元素个数。
- print():打印栈里的元素。
function Stack(){
this.storage = []
this.push = function(data){
this.storage.push(data)
}
this.pop = function(){
return this.storage.pop()
}
this.peek = function(){
return this.storage[this.storage.length-1]
}
this.isEmpty = function(){
return this.storage.length == 0
}
this.size = function(){
return this.storage.length
}
this.print = function(){
console.log(this.storage.toString())
}
}
语法笔记
- 调用api的写法不能用在原型对象中
队列 Queue
队列是先进先出FIFO的线性表。只允许一端进行插入操作,另一端进行删除操作。
在web开发中,队列的例子是Web浏览器的事件循环。当触发不同事件时,例如单击某个按钮,点击事件将被添加到事件循环队列中,并按照它们进入队列的顺序进行处理。
用JavaScript结合原型实现队列
- enqueue(data) 将数据添加到队列中。
- dequeue() 删除最早加入队列的数据。
- size() 返回队列的长度。
function Queue(){
// 设置双指针,new用来增加数据,old用来删除数据
this.OldestIdx = 0
this.NewestIdx = 0
this.storage = []
}
Queue.prototype = {
size: function(){
return this.NewestIdx - this.OldestIdx + 1
}
enqueue: function(data){
this.storage[this.NewestIdx++] = data
},
dequeue:function(){
// 3. 因为要返回被删的数据,这里要先保存起来
var deleteData = this.storage[this.OldestIdx]
// 5. 如果两个指针相遇表示队列为空,不能再删
if (this.OldestIdx != this.NewestIdx){
// 1. 删除旧数据
delete this.storage[this.OldestIdx]
// 2. 旧数据指针向前移动
this.OldestIdx ++
// 4. 返回被删除元素
return deleteData
}
}
}
直接调用js的api实现队列
给队列声明以下方法:
- enqueue(data):向队列尾部添加一个(或是多个)元素。
- dequeue():移除队列的第一个元素,并返回被移除的元素。
- front():返回队列的第一个元素——最先被添加的也是最先被移除的元素。队列不做任何变动。
- isEmpty():检查队列内是否有元素,如果有返回true,没有返回false。
- size():返回队列的长度。
- print():打印队列的元素。
function Queue(){
this.storage = []
this.enqueue = function(data){
this.storage.push(data)
}
this.dequeue = function(){
return this.storage.shift()
}
this.front = function(){
return this.storage[0]
}
this.isEmpty = function(){
return this.storage.length == 0
}
this.print = function(){
console.log(this.storage.toString())
}
}
练习1 leetcode 232. 用栈实现队列 (easy)
使用栈实现队列的下列操作:
push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。
示例:
MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
queue.peek(); // 返回 1
queue.pop(); // 返回 1
queue.empty(); // 返回 false
解题思路
- 用两个栈实现一个队列,两个栈都是先进后出,队列在两端先进先出,想办法获取一个栈栈尾的元素,放到另一个栈栈顶
图摘自负负得正,使用两个栈,一个专门入队,一个专门出队- 关键1.一次性把stackPush的元素都pop出来,即调用了一次pop,stackPush就为空了,那peek方法咋办?为了在不确定数据是在stackPush还是stackPop的时候,调用peek,为了保证得到队首元素,就在peek方法里也操作一次,把所有stackPush里的数据push到stackPop里。前面判断一下,如果stackPop不为空,即前面有操作过这一步骤了,就不用操作。发现有重复使用这一步骤,就封装成一个方法PushtoPop 。
- 关键2.因为要判断是否为空的时候才PushtoPop,所以不是每一次执行push方法时都会执行PushtoPop,就要在push和pop中都调用PushtoPop,以防多次pop,使stackPop为空,而stackPush中还有元素。
var MyQueue = function() {
this.stackPush = []
this.stackPop = []
};
MyQueue.prototype.PushtoPop = function() {
// 只要确保stackPop中一定存有stackPush栈尾的值,并将其push在stackPop栈顶即可
if(this.stackPop.length === 0){ // 关键1
while(this.stackPush.length > 0){
this.stackPop.push(this.stackPush.pop())
}
}
};
MyQueue.prototype.push = function(x) {
this.stackPush.push(x)
this.PushtoPop() // 关键2
};
MyQueue.prototype.pop = function() {
if(this.stackPop.length === 0 && this.stackPush.length === 0){
throw Error ("The queue is empty")
}
this.PushtoPop() // 关键2
return this.stackPop.pop()
};
MyQueue.prototype.peek = function() {
if(this.stackPop.length === 0 && this.stackPush.length === 0){
throw Error ("The queue is empty")
}
this.PushtoPop() // 关键1
return this.stackPop[this.stackPop.length - 1]
};
MyQueue.prototype.empty = function() {
return this.stackPop.length === 0 && this.stackPush.length === 0
};
语法笔记
- js中获取数组元素最后一项不能用
arr[-1]
索引,要用arr[arr.length - 1]
- 逻辑运算符的内涵是
只要“||”前面为false,结果都返回“||”后面的值。
只要“||”前面为true,结果都返回“||”前面的值。
只要“&&”前面是false,结果都将返“&&”前面的值;(可以记为返回false)
只要“&&”前面是true,结果都将返“&&”后面的值;(可以记为根据后面的true false判断)
但是这样在写的时候就很容易晕,如果只是在boolean判断的时候用逻辑运算符,直接记住
&& 与 两个操作数同时为true,结果为true,否则都是false
|| 或 两个操作数有一个为true,结果为true,否则为false