如何实现一个栈的方法,它能够返回栈的最小值且时间复杂度为O(1)
wengjq opened this issue · comments
wengjq commented
push 和 pop 操作很明显就是 O(1)的时间复杂度,关键是 min 函数,一般来说,我们求栈中的最小值,会从栈顶开始遍历栈,并设置一个变量 Min 来保存每次遍历时的最小值 ,遍历到比 Min 还小的元素,就将该元素赋给 Min,但这种方法的时间复杂度为 O(n)。
思路一:每次入栈和出栈同时操作两个元素,下面的元素是需要入栈的元素,上面的元素是整个栈的最小元素。
function Stack() {
this.items = [];
}
Stack.prototype = {
constructor: Stack,
add: function (element) {
//如果数组内没有元素,则最小元素就是入栈的元素
if (!this.items.length) {
this.items.push(element);
this.items.push(element);
} else {
//获取当前最小元素
var minElement = this.getMinElement();
if (element < minElement) {
minElement = element;
}
this.items.push(element);
this.items.push(minElement);
}
},
//实现出栈的方法
remove: function () {
this.items.pop();
return this.items.pop();
},
//获取栈的最小值的方法
getMinElement: function () {
if (!this.items.length) {
return null;
} else {
return this.items[this.items.length - 1];
}
}
}
思路二:同时维护两个栈,一个用来存储元素,另一个来存储最小值的下标。两个栈同步更新,这样就能实现获取最小值的功能。
function Stack() {
this.items = []; // 栈 A ,存储元素
this.minItemsSubscript = []; // 栈 B ,存储栈 A 元素最小值下标
}
Stack.prototype = {
constructor: Stack,
add: function (element) {
// 当第一个元素进入栈 A 的时候,让新元素的下标进入栈 B 。这个唯一的元素是栈 A 的当前最小值
if (!this.items.length) {
this.items.push(element);
this.minItemsSubscript.push(0);
} else {
// 每当新元素进入栈 A 时,比较新元素和栈 A 当前最小值的大小,如果小于栈 A 当前最小值,则让新元素的下标进入栈 B ,此时栈 B 的栈顶元素就是栈 A 当前最小值的下标
var minElement = this.getMinElement();
if (element < minElement) {
this.minItemsSubscript.push(this.items.length);
}
this.items.push(element);
}
},
// 每当栈 A 有元素出栈时,如果出栈元素是栈 A 当前最小值,则让栈 B 的栈顶元素也出栈。此时栈 B 余下的栈顶元素所指向的,是栈 A 当中原本第二小的元素,代替刚才的出栈元素成为了栈 A 的当前最小值。(备胎转正)
remove: function () {
var minElement = this.getMinElement();
var popItem = this.items.pop();
if (popItem === minElement) {
this.minItemsSubscript.pop();
}
return popItem;
},
// 直接返回栈 B 的栈顶所指向的栈 A 对应元素
getMinElement: function () {
if (!this.items.length || !this.minItemsSubscript.length) {
return null;
} else {
return this.items[this.minItemsSubscript[this.minItemsSubscript.length - 1]];
}
}
}