上一篇文章咱们一块儿实现了栈,那么这一篇文章咱们一块儿来用栈解决问题。看看如何用栈来解决进制转换,平衡圆括号以及汉诺塔问题,使咱们对栈有更为深刻的理解。git
一、进制转换数组
咱们先来看看十进制如何转换成二进制,十进制整数转换为二进制整数采用"除2取余,逆序排列"法。具体作法是:用2整除十进制整数,能够获得一个商和余数;再用2去除商,又会获得一个商和余数,如此进行,直到商为0时为止,而后把先获得的余数做为二进制数的低位有效位,后获得的余数做为二进制数的高位有效位,依次排列起来。简单来讲就是拿十进制数去除以二,若是整除了,那么余数为0,放入栈中,若是没有整除,余数就是1,放入栈中,直至相除的结果为0。依据所获得的结果,后获得的余数排列在最前面。也就是栈顶元素从左到右排列。数据结构
咱们已经知道了十进制如何转换成二进制,那么咱们看看代码是怎么实现的吧。ide
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));//1100100 console.log(decimalToBinary(1));//1 console.log(decimalToBinary(2));//10 console.log(decimalToBinary(111));//1101111 console.log(decimalToBinary(65));//1000001
那么上面就实现了十进制转换二进制的方法,那么我想可不可使它更完善一点,实现十进制转换成二进制,八进制,十六进制等。学习
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));//64 console.log(baseConverter(1,16));//1 console.log(baseConverter(12,8));//14 console.log(baseConverter(122323123,36));//20TT4J console.log(baseConverter(111111111,28));//6CLF9R console.log(baseConverter(99,16));//63
咱们发现其实对于十进制转换成其它进制,貌似只是多了一个对照表digits,本质上并无什么区别。spa
二、平衡圆括号code
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("[][][]["))
其实这个方法的核心在于,判断当前下标的参数是头部分仍是尾部分,头部就入栈,若是是尾部就出栈头部并和当前尾部对比。正是利用了栈的特性。htm
三、汉诺塔对象
什么是汉诺塔?我相信不少人小时候都玩过,有图有真相,没图不BB。blog
在开始玩汉诺塔游戏以前,我先给你们说一下汉诺塔游戏的规则:
规则一:每次操做只能移动一个圈圈,把它从一个柱子移到另外一个柱子上。
规则二:大圈圈不能架在小圈圈的上面。
这是游戏的规则,那么换做程序的话,规则是这样的:假设这里有三根相邻的柱子,标号为A,B,C,A柱子上由下到上按金字塔状叠放着n个不一样大小的圆盘,要把全部盘子一个一个移动到柱子B上,而且每次移动同一根柱子上都不能出现大盘子在小盘子上方,请问至少须要移动多少次?
个人理解,一、目的是把这个汉诺塔从一个柱子依照由下到上的顺序完整的移动到另外一个柱子上,
二、大圈不能在小圈之下,可是能够隔层放置大小圈,好比八号最大,越往上越小,那么在移动的过程当中,5号是能够放在7号上面的。
三、能够往回放。
若是还有不清楚规则的地方,能够去这里亲自玩一下这个游戏。
咱们已经对汉诺塔有了简单的了解,那么咱们看看如何用栈来实现这个游戏吧:
//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'));
到这里,咱们对栈有了必定的了解,相信你们在从此不管什么状况下遇到“栈”这个词都不会再陌生和懵懂了。那么对栈的学习到这里就基本结束了。下一篇文章会跟你们一块儿学习一下队列这个数据结构。
最后,因为本人水平有限,能力与大神仍相差甚远,如有错误或不明之处,还望你们不吝赐教指正。很是感谢!