简介
栈是一种高效的数据结构,因为数据只能在栈顶添加或删除,所以这样的操作很快,而且我们比较容易实现。
一、对栈的操作
1.后入先出: 栈被称为一种“后入先出”的数据结构。类似于汉诺塔(游戏),区别在于这里我们不用考虑栈内元素的大小。在咖啡厅内的一摞盘子也是现实世界中常见的栈的例子,通常从最上面取盘子,盘子洗净后,也会将盘子摞在最上面。
2.栈顶: 栈是一种特殊的列表,栈内的元素只能通过列表的一端访问,这一端称为栈顶。由于栈具有后入先出的特点,所以任何不在栈顶的元素都无法访问。为了得到栈底的元素,我们必须先拿掉上面的元素。
3.入栈出栈: 入栈使用 push方法,出栈使用 pop方法。
4.预览栈顶的元素: 使用peek方法。pop方法虽然可以访问栈顶的元素,但是调用该方法后,栈顶元素也从栈中被永久性地删除了。显然这是不符合我们预期的,而peek方法则只返回栈顶元素,而不会删除它。
5.栈的主要方法: push、pop 和 peek。
6.其他方法和属性:
(1)clear方法将清除栈内所有元素。
(2)length方法将记录我们栈内元素的个数,也可以用来表示栈内是否含有元素。
二、栈的实现
栈的实现,可以采用数组来存储数据。我们首先定义Stack类的构造函数。
(1)dataStore:保存栈内元素,构造函数将其初始化为一个空数组。
(2)top变量:记录栈顶的位置,被构造函数初始化的值表示栈顶对应数组的起始位置。当有元素被压入栈,该变量的值会随之变化。
(3)top变量被构造函数初始化的值可为0或-1,总之就是标识我们的栈里面没有元素了。
初始化值的不同代码的写法会有所不同:特别特别注意++(或--)操作符的位置,它放在this.push(或this.pop或this.peek)的前面,表示先将变量top的值递增(递减)运算再进行当行操作。它放在this.push(或this.pop或this.peek)的前面,表示先进行当行操作再将变量 top 的值递增(递减)运算。两种方式可通过对比来理解记忆,具体如下:
1、被构造函数初始化的值为0时:
1.1.定义 Stack 类的构造函数
function Stack(){
// 栈的两个属性
this.dataStore = [];
this.top = 0;
// 栈的五个方法
this.push = push;
this.pop = pop;
this.peek = peek;
this.length = length;
this.clear = clear;
}
1.2.入栈操作:push方法
function push(element){
this.dateStore[this.top++] = element;
}
1.3.出栈操作:pop方法
function pop(){
return this.dateStore[--this.top];
}
1.4.获取栈顶元素操作:peek方法
function peek(){
return this.dateStore[this.top-1];
}
如果对一个空栈调用 peek方法,结果为 undefined。这是因为栈是空的,这时栈顶没有任何元素
1.5. 获取栈中元素个数:length方法
function length() {
return this.top;
}
1.6清除栈中所有元素:clear方法
function clear() {
this.top = 0;
}
2、当top变量被构造函数初始化的值为-1时:
一些实现细节上不同于top变量为0时,但不影响外部使用。
2.1.定义 Stack 类的构造函数
function Stack(){
// 栈的两个属性
this.dataStore = [];
this.top = -1;
// 栈的五个方法
this.push = push;
this.pop = pop;
this.peek = peek;
this.length = length;
this.clear = clear;
}
2.2.入栈操作:push方法
function push(element){
this.dateStore[++this.top] = element;
}
2.3.出栈操作:pop方法
function pop(){
return this.dateStore[this.top--];
}
2.4.获取栈顶元素操作:peek方法
function peek(){
return this.dateStore[this.top];
}
2.5. 获取栈中元素个数:length方法
function length() {
return this.top + 1;
}
2.6.清除栈中所有元素:clear方法
function clear() {
this.top = -1;
}
三、测试 Stack类
1、测试 Stack 类的实现
var s = new Stack();
s.push("David");
s.push("Raymond");
s.push("Bryan");
console.log("length: " + s.length());
console.log(s.peek());
var popped = s.pop();
console.log("The popped element is: " + popped);
console.log(s.peek());
s.push("Cynthia");
console.log(s.peek());
s.clear();
console.log("length: " + s.length());
console.log(s.peek());
s.push("Clayton");
console.log(s.peek());
2、测试代码输出结果为:
length: 3
Bryan
The popped element is: Bryan
Raymond
Cynthia
length: 0
undefined
Clayton
倒数第二行返回 undefined,这是因为栈被清空后,栈顶就没值了,这时使用 peek() 方法 预览栈顶元素,自然得到 undefined。
四、使用stack 类
1、数制间的相互转换
可以利用栈将数字转化为二至九进制的数字。假设想将数字 n 转换为以 b 为基数 的数字,实现转换的算法如下。
(1) 最高位为 n % b,将此位压入栈。
(2) 使用 n/b 代替 n。
(3) 重复步骤 1 和 2,直到 n 等于 0,且没有余数。
(4) 持续将栈内元素弹出,直到栈为空,依次将这些元素排列,就得到转换后数字的字符 串形式。
注: 此算法只针对基数为 2~9 的情况。
1.1.转化为二至九进制:
function mulBase(num, base) {
var s = new Stack();
do {
s.push(num % base);
num = Math.floor(num /= base);
}while (num > 0);
var converted = "";
while (s.length() > 0) {
converted += s.pop();
}
return converted;
}
1.1.1将数字转换为二进制和八进制
function mulBase(num, base) {
var s = new Stack();
do {
s.push(num % base);
num = Math.floor(num /= base);
} while (num > 0);
var converted = "";
while (s.length() > 0) {
converted += s.pop();
}
return converted;
}
var num = 32;
var base = 2;
var newNum = mulBase(num, base);
console.log(num + " converted to base " + base + " is " + newNum);
num = 125;
base = 8;
var newNum = mulBase(num, base);
console.log(num + " converted to base " + base + " is " + newNum);
输出为:
32 converted to base 2 is 100000
125 converted to base 8 is 175
2、回文
回文指的是:一个单词、短语或数字,正序和倒叙都是一样的。比如,单词“dad”、“racecar”就是回文;如果忽略空格和标点符号,下面这个句子也是文,“A man, a plan, a canal: Panama”;数字 1001 也是。
使用栈,可以轻松判断一个字符串是否是回文。当使用push()方法和pop()方法得到的两个字符串相等时,就是一个回文。
2.1.判断给定字符串是否是回文
function isPalindrome(word) {
var s = new Stack();
for (var i = 0;i < word.length; ++i) {
s.push(word[i]);
}
var rword = "";
while (s.length() > 0) {
rword += s.pop();
}
if (word == rword) {
return true;
}
else {
return false;
}
}
var word = "hello";
if (isPalindrome(word)) {
console.log(word + " is a palindrome.");
} else {
console.log(word + " is not a palindrome.");
}
word = "racecar"
if (isPalindrome(word)) {
console.log(word + " is a palindrome.");
} else {
console.log(word + " is not a palindrome.");
}
程序的输出为:
hello is not a palindrome.
racecar is a palindrome.
3、递归演示
为了演示如何用栈实现递归,考虑一下求阶乘函数的递归定义。 首先看看 5 的阶乘是怎么 定义的:
5! = 5 × 4 × 3 × 2 × 1 = 120
下面是一个递归函数,可以计算任何数字的阶乘:
function factorial(n) {
if (n === 0) {
return 1;
} else {
return n * factorial(n-1);
}
}
使用该函数计算 5 的阶乘,返回 120。
3.1.使用栈模拟递归过程
function fact(n) {
var s = new Stack();
while (n > 1) {
s.push(n--);
}
var product = 1;
while (s.length() > 0) {
product *= s.pop();
}
return product;
}
console.log(factorial(5)); // 显示 120
console.log(fact(5)); // 显示 120