上一篇文章咱們一塊兒實現了棧,那麼這一篇文章咱們一塊兒來用棧解決問題。看看如何用棧來解決進制轉換,平衡圓括號以及漢諾塔問題,使咱們對棧有更爲深刻的理解。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'));
到這裏,咱們對棧有了必定的瞭解,相信你們在從此不管什麼狀況下遇到「棧」這個詞都不會再陌生和懵懂了。那麼對棧的學習到這裏就基本結束了。下一篇文章會跟你們一塊兒學習一下隊列這個數據結構。
最後,因爲本人水平有限,能力與大神仍相差甚遠,如有錯誤或不明之處,還望你們不吝賜教指正。很是感謝!