上一篇文章我们一起实现了栈,那么这一篇文章我们一起来用栈解决问题。看看如何用栈来解决进制转换平衡圆括号以及汉诺塔问题,使我们对栈有更为深入的理解。

1、进制转换

  我们先来看看十进制如何转换成二进制,十进制整数转换为二进制整数采用"除2取余,逆序排列"法。具体做法是:用2整除十进制整数,可以得到一个商和余数;再用2去除商,又会得到一个商和余数,如此进行,直到商为0时为止,然后把先得到的余数作为二进制数的低位有效位,后得到的余数作为二进制数的高位有效位,依次排列起来。简单来说就是拿十进制数去除以二,如果整除了,那么余数为0,放入栈中,如果没有整除,余数就是1,放入栈中,直至相除的结果为0。依据所得到的结果,后得到的余数排列在最前面。也就是栈顶元素从左到右排列。

  我们已经知道了十进制如何转换成二进制,那么我们看看代码是怎么实现的吧。

function decimalToBinary(decNumber) {
//声明一个stack对象
const remStack = new Stack();
// 需要转换的数字
let number = decNumber;
//每次相除后所得的余数
let rem;
//转换后的二进制字符串
let binaryString = '';
//当number为0的时候结束循环
while (number > 0) {
//对余数向下取整,因为这里不取整的话会出现小数,js没有浮点或者整形这一说。
rem = Math.floor(number % 2);
// 存储当前的余数
remStack.push(rem);
//因为上面对number取余只是拿到了最后余数的结果,number本身并没有除以二,所以这里除以二是为了保证后面可以再一次取余的结果正确性
number = Math.floor(number / 2);
}
//这里的意思是如果栈中还有元素,那么就循环拼接字符串。
while (!remStack.isEmpty()) {
binaryString += remStack.pop().toString();
} return binaryString;
} console.log(decimalToBinary(100));//
console.log(decimalToBinary(1));//
console.log(decimalToBinary(2));//
console.log(decimalToBinary(111));//
console.log(decimalToBinary(65));//

  那么上面就实现了十进制转换二进制的方法,那么我想可不可以使它更完善一点,实现十进制转换成二进制,八进制,十六进制等。

function baseConverter(decNumber, base) {
const remStack = new Stack();
// 这是转换的对比字典,大家知道在十位以后的禁止转换中,10用A代表,11用B代表。
//所以当我们在转换的时候需要使余数的数字被字母所替换。
const digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
let number = decNumber;
let rem;
let baseString = '';
//这里只做2到36位之间的转换,因为理论上来讲可以进行任何位数之间的互相转换。
//但是在不同位数之间转换的时候会有更为复杂的情况
if (!(base >= 2 && base <= 36)) {
return '';
} while (number > 0) {
rem = Math.floor(number % base);
remStack.push(rem);
number = Math.floor(number / base);
} while (!remStack.isEmpty()) {
baseString += digits[remStack.pop()];
} return baseString;
} console.log(baseConverter(100,16));//
console.log(baseConverter(1,16));//
console.log(baseConverter(12,8));//
console.log(baseConverter(122323123,36));//20TT4J
console.log(baseConverter(111111111,28));//6CLF9R
console.log(baseConverter(99,16));//

  我们发现其实对于十进制转换成其它进制,貌似只是多了一个对照表digits,本质上并没有什么区别。

2、平衡圆括号

function parenthesesChecker(symbols) {
const stack = new Stack();
const opens = '([{';
const closers = ')]}';
let balanced = true;
let index = 0;
let symbol;
let top;
while (index < symbols.length && balanced) {
// 从0开始(index的初始值),给symbol赋值
symbol = symbols.charAt(index);
// 这里判断symbol是否在opens中存在,即opens.indexOf的返回值不为负数。
if (opens.indexOf(symbol) >= 0) {
// 如果存在,那么说明是开始符号,入栈。
stack.push(symbol);
//那么之前判断了是否存在开始符号,如果不存在的话就不会入栈,所以stack就没有元素,自然stack为空,balanced返回false。
//因为如果一开始的第一个符号就是尾部符号一定是无法对称平衡的。
} else if (stack.isEmpty()) {
balanced = false;
} else {
// 获取栈顶元素并移除
top = stack.pop();
// 这个else其实是当symbols中对应的下标没有值得时候,就说明是closer中的值之一。
//所以这里的symbol其实是closer,所以获取最近入栈的值进行比较,就能判断出是否是平衡的。
if (!(opens.indexOf(top) === closers.indexOf(symbol))) {
balanced = false;
}
}
// 继续循环
index++;
}
// 这里,如果是balanced的,并且stack为空,那么说明我们的所有元素必然是一一对称的。
//因为如果不对称是无法完全出栈的
if (balanced && stack.isEmpty()) {
return true;
}
return false;
} console.log(parenthesesChecker("(()()())"))
console.log(parenthesesChecker("(({})){["))
console.log(parenthesesChecker("{}()[]"))
console.log(parenthesesChecker("{{{}}}}"))
console.log(parenthesesChecker("[][][]["))

  其实这个方法的核心在于,判断当前下标的参数是头部分还是尾部分,头部就入栈,如果是尾部就出栈头部并和当前尾部对比。正是利用了栈的特性。

3、汉诺塔

  什么是汉诺塔?我相信很多人小时候都玩过,有图有真相,没图不BB。

  在开始玩汉诺塔游戏之前,我先给大家说一下汉诺塔游戏的规则

    规则一:每次操作只能移动一个圈圈,把它从一个柱子移到另一个柱子上。

    规则二:大圈圈不能架在小圈圈的上面。

  这是游戏的规则,那么换作程序的话,规则是这样的:假设这里有三根相邻的柱子,标号为A,B,C,A柱子上由下到上按金字塔状叠放着n个不同大小的圆盘,要把所有盘子一个一个移动到柱子B上,并且每次移动同一根柱子上都不能出现大盘子在小盘子上方,请问至少需要移动多少次?

  我的理解,1、目的是把这个汉诺塔从一个柱子依照由下到上的顺序完整的移动到另一个柱子上,

       2、大圈不能在小圈之下,但是可以隔层放置大小圈,比如八号最大,越往上越小,那么在移动的过程中,5号是可以放在7号上面的。

       3、可以往回放。

  如果还有不清楚规则的地方,可以去这里亲自玩一下这个游戏。

  我们已经对汉诺塔有了简单的了解,那么我们看看如何用栈来实现这个游戏吧:

//plates:盘子数量,source源柱子,helper暂存柱子,dest目标柱子,sourceName源柱子名称,helperName暂存柱子名称,destName目标柱子名称,moves步数(若不传值则默认为一个数组)
function towerOfHanoi(plates, source, helper, dest, sourceName, helperName, destName, moves = []) {
console.log(moves.length)
//如果盘子的数字不大于0 ,那么直接返回moves,终止递归的条件。
if (plates <= 0) return moves; if (plates === 1) {
dest.push(source.pop());
const move = {};
move[sourceName] = source.toString();
move[helperName] = helper.toString();
move[destName] = dest.toString();
moves.push(move);
} else {
//递归调用自身。并且将盘子的数量减少一个,这里交换了dest和helper的位置,是为了dest.push中存入的栈是helper栈,也就是说是为了存入对应的柱子。
towerOfHanoi(plates - 1, source, dest, helper, sourceName, destName, helperName, moves);
//从源柱子拿出最顶层的一个放入目标柱子(如果dest和helper互换了位置,那么其实这里的dest实际上代表的是helper)
dest.push(source.pop());
//声明常量,用来存储当前各个柱子的盘子栈况
const move = {};
move[sourceName] = source.toString();
move[helperName] = helper.toString();
move[destName] = dest.toString();
// 存入moves
moves.push(move);
towerOfHanoi(plates - 1, helper, source, dest, helperName, sourceName, destName, moves);
}
return moves;
} function hanoiStack(plates) {
// 创建每一个柱子的栈对象,source是最开始拥有所有圈圈的柱子,dest是目标柱子,helper是中间的暂存柱子
const source = new Stack();
const dest = new Stack();
const helper = new Stack();
//倒序循环把每一个圈圈序号放入source栈
for (let i = plates; i > 0; i--) {
source.push(i);
}
//通过return调用towerOfHanoi计算方法。
return towerOfHanoi(plates, source, helper, dest, 'source', 'helper', 'dest');
}
//这个方法是计算在汉诺塔的层数为plates的时候,每一个是从哪个柱子拿到哪个柱子的
function hanoi(plates, source, helper, dest, moves = []) {
if (plates <= 0) return moves;
if (plates === 1) {
moves.push([source, dest]);
} else {
hanoi(plates - 1, source, dest, helper, moves);
moves.push([source, dest]);
hanoi(plates - 1, helper, source, dest, moves);
}
return moves;
} console.log(hanoiStack(2))
console.log(hanoi(8, 'source', 'helper', 'dest'));

  到这里,我们对栈有了一定的了解,相信大家在今后无论什么情况下遇到“栈”这个词都不会再陌生和懵懂了。那么对栈的学习到这里就基本结束了。下一篇文章会跟大家一起学习一下队列这个数据结构。

  

  最后,由于本人水平有限,能力与大神仍相差甚远,若有错误或不明之处,还望大家不吝赐教指正。非常感谢!

用js来实现那些数据结构05(栈02-栈的应用)的更多相关文章

  1. 用js来实现那些数据结构—目录

    首先,有一点要声明,下面所有文章的所有内容的代码,都不是我一个人独立完成的,它们来自于一本叫做<学习JavaScript数据结构和算法>(第二版),人民邮电出版社出版的这本书.github ...

  2. 用js来实现那些数据结构及算法—目录

    首先,有一点要声明,下面所有文章的所有内容的代码,都不是我一个人独立完成的,它们来自于一本叫做<学习JavaScript数据结构和算法>(第二版),人民邮电出版社出版的这本书.github ...

  3. 用JS描述的数据结构及算法表示——栈和队列(基础版)

    前言:找了上课时数据结构的教程来看,但是用的语言是c++,所以具体实现在网上搜大神的博客来看,我看到的大神们的博客都写得特别好,不止讲了最基本的思想和算法实现,更多的是侧重于实例运用,一边看一边在心里 ...

  4. JS数据结构第四篇 --- 栈

    一.什么是数据结构栈 在数据结构中有一个栈结构,在内存空间中也有一个栈空间,这两个”栈“是两个不同的概念.这篇我们说的是数据结构中的栈.栈是一种特殊的线性表,特殊性在哪?就是只能在栈顶进行操作,往栈顶 ...

  5. JS: 数据结构与算法之栈

    栈 先来看一道题 Leetcode 32 Longest Valid Parentheses (最长有效括号) 给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度. 示例 ...

  6. 数据结构:C_顺序栈的实现

    数据结构顺序栈的实现(C语言版) 1.写在前面 栈是一种遵循元素先进(Push)后出(Pop)规则的线性表,它的实现可以用数组或者链表. ..... 2.代码分解 2.1对栈的结构定义: typede ...

  7. 【Java数据结构学习笔记之二】Java数据结构与算法之栈(Stack)实现

      本篇是java数据结构与算法的第2篇,从本篇开始我们将来了解栈的设计与实现,以下是本篇的相关知识点: 栈的抽象数据类型 顺序栈的设计与实现 链式栈的设计与实现 栈的应用 栈的抽象数据类型   栈是 ...

  8. java数据结构与算法之栈(Stack)设计与实现

    本篇是java数据结构与算法的第4篇,从本篇开始我们将来了解栈的设计与实现,以下是本篇的相关知识点: 栈的抽象数据类型 顺序栈的设计与实现 链式栈的设计与实现 栈的应用 栈的抽象数据类型 栈是一种用于 ...

  9. java数据结构和算法02(栈)

    什么叫做栈(Stack)呢?这里的栈和jvm的java栈可不是一个东西... 栈作为一种数据结构,我感觉栈就类似一种接口,实现的话有很多种,比如用数组.集合.链表都可以实现栈的功能,栈最大的特点就是先 ...

随机推荐

  1. bug单的生命周期

    测试工程师发现了软件的缺陷(bug),那修复bug的整个流程是怎么样的呢? 1.发现bug 2.和开发确认是否是bug 3.如果是bug则提bug单到测试经理,如果不是则放过 4.测试经理把bug单走 ...

  2. WEB页面异步调用场景测试

    在我们测试异步调用前,我们首先弄清楚异步调用到底是什么? 异步调用的定义:一个可以无需等待被调用函数的返回值就让操作继续进行的方法, 举一个形象的例子就是:领导给A分配了一个任务, 然后领导就干其他事 ...

  3. css3图片模糊过滤特效

    体验效果:点击这里查看效果 代码如下:<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " ...

  4. eclipse如何debug调试jdk源码(任何源码)并显示局部变量

    最近要看struts2源码 仿照了一下查看jdk源码的方式 首先你要有strtus2的jar包和源码,在struts官网上下载时,选择full版本,里面会有src也就是源码了. jar导入项目,保证可 ...

  5. 网易云音乐APP分析

    网易云音乐-感受音乐的力量 你选择的产品是?  网易云音乐 为什么选择该产品作为分析? 之前用的一直是QQ音乐,但是有一天一个朋友分享了一首网易云上的音乐(顺便分享一下歌名:Drop By Drop) ...

  6. 201621123040《Java程序设计》第九周学习总结

    1.本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容 泛型部分思维导图 集合部分学习总结 java.util.Collection 是一个集合接口;java.util. ...

  7. c++第0次作业

    1.你认为大学的学习生活.同学关系.师生应该是怎样? 随着大学生活的慢慢到来,我开始领悟到大学并不是自由的天堂,相反,我们更加的走进社会这个牢笼.在这个牢笼中有着从前的我们并不需要在意和考虑的规则与问 ...

  8. 展示博客(Beta版本)

    团队:xjbz 1. 团队成员博客,源码仓库地址. coding:https://git.coding.net/z404395979/xjbz.git 钟平辉(071):http://www.cnbl ...

  9. 【技巧】Java工程中的Debug信息分级输出接口

    也许本文的标题你们没咋看懂.但是,本文将带大家领略输出调试的威力. 灵感来源 说到灵感,其实是源于笔者在修复服务器的ssh故障时的一个发现. 这个学期初,同袍(容我来一波广告产品页面,同袍官网)原服务 ...

  10. 从PRISM开始学WPF(六)MVVM(二)Command?

    从PRISM开始学WPF(一)WPF? 从PRISM开始学WPF(二)Prism? 从PRISM开始学WPF(三)Prism-Region? 从PRISM开始学WPF(四)Prism-Module? ...