1、什麼是數據結構棧html
在數據結構中有一個棧結構,在內存空間中也有一個棧空間,這兩個」棧「是兩個不一樣的概念。這篇咱們說的是數據結構中的棧。棧是一種特殊的線性表,特殊性體如今只能在棧頂進行操做,往棧頂添加元素,通常叫push, 入棧;從棧頂移除元素,通常叫pop, 出棧,操做如圖:git
這個特徵叫」後進先出「,Last In First On, 簡稱LIFO。和JS數組中的push和pop函數功能有點像。固然棧的內部設計,就能夠用數組,或者也能夠用鏈表。github
2、棧結構設計和應用示例數組
2.1 內部實現:數據結構
棧結構對外暴露的方法有入棧(push)、出棧(pop)、獲取棧頂元素(top)、獲取棧長度(length)、清空棧內元素。如圖:ide
這裏貼出內部用數組實現的棧設計構造函數,鏈表實現見Github函數
/** * 數據結構棧:先進後出,後進先出,即LIFO,棧的內部實現能夠用數組,也能夠用鏈表; * 這裏先用數組實現,對外暴露的方法有: * push(element): 放入一個元素,即入棧 * pop() : 移除棧頂元素,即出棧 * top() : 獲取棧頂元素 * clear() : 移除全部棧元素 * length() : 獲取棧長度 */ const Stack = function(){ let arr = []; //內部數組 //入棧方法 function push(element){ arr.push(element); } //出棧方法 function pop(){ return arr.pop(); } //獲取棧頂元素 function top(){ return arr.length > 0 ? arr[arr.length-1]:null; } //移除全部棧元素 function clear(){ arr = []; } //獲取棧長度 function length(){ return arr.length; } this.push = push; this.pop = pop; this.top = top; this.clear = clear; this.length = length; }
2.2 在這個棧構造函數的基礎上,作幾題目應用實踐一下測試
2.2.1 用棧實現將十進制數字轉成二進制,和八進制this
/** * 十進制轉成二進制或八進制 * @param num 十進制數字 * @param base =2表示轉成二進制,=8表示轉成八進制 */ function numChange(num, base){ var stack = new Stack(); do { stack.push(num%base); num = Math.floor(num/base); }while(num > 0) let str = ''; while(stack.length()>0){ str += stack.pop(); } return str; } console.log(numChange(8, 2)); console.log(numChange(9, 2)); console.log(numChange(10, 2)); console.log(numChange(8, 8)); console.log(numChange(17, 8)); console.log(numChange(35, 8));
2.2.2 用棧來判斷一個字符串是否迴文。好比"abc"不是迴文,"abcba"是迴文,"abccba"是迴文spa
//測試3,判斷一個字符串是否迴文 function isCircle(s){ let stack = new Stack(); for(let i = 0; i < s.length; i++){ stack.push(s[i]); } let newStr = ''; while(stack.length() > 0){ newStr += stack.pop(); } return newStr == s; } console.log("\n\n判斷一個字符串是否迴文...."); console.log(isCircle("abc")); console.log(isCircle("abcdcba")); console.log(isCircle("helloolleh"));
3、力扣棧結構應用題目
3.1 有效的括號_第20題
/** * @param {string} s * @return {boolean} */ var isValid = function(s) { /** * 執行用時 :68 ms, 在全部 JavaScript 提交中擊敗了98.63%的用戶 * 內存消耗 :33.7 MB, 在全部 JavaScript 提交中擊敗了76.56%的用戶 */ let arr = [], temp = top = null; for (let i = 0; i < s.length; i++){ temp = s[i]; //碰到左括號,入棧 if (temp == '(' || temp == '[' || temp == '{'){ arr.push(temp); } //碰到右括號,出棧 else{ if (arr.length < 1) return false; top = arr[arr.length-1]; if ((temp == ')' && top == '(')|| (temp == ']' && top == '[')|| (temp == '}' && top == '{')){ arr.pop(); } else{ return false; } } } return arr.length > 0 ? false : true; };
3.2 根據括號計算分數_第856題
/** * @param {string} S * @return {number} * 執行用時 :80 ms, 在全部 JavaScript 提交中擊敗了62.96%的用戶 * 內存消耗 :33.7 MB, 在全部 JavaScript 提交中擊敗了33.33%的用戶 */ var scoreOfParentheses = function(S) { let arr = [], temp = null; for (let i = 0; i < S.length; i++){ temp = S[i]; if (temp == '('){ arr.push(temp); } else{ if (arr.length < 1) return 0; //調整棧,找到數組裏面配套的左括號,若是目標左括號在棧頂,調整"("爲1; // 若是目標左括號不在棧頂,則從棧頂到目標左括號累加,每累加一次,出棧一次; // 一直到目標左括號成爲棧頂,這事根據規則(A)=2*A,則目標左括號位置的值爲2*累加分 // 好比:()()((()(()())())()()) // [1,1,(,(,1,4,1 findTarget(arr); } } return findTarget(arr); }; //尋找匹配左括號, 倒序找到第一個匹配的"(",而後調整目標位置的值 function findTarget(arr){ let score = 0; for (let i = arr.length-1; i >= 0; i--){ if (arr[i] == '('){ arr[i] = score > 0 ? 2*score : 1; return 0; } else{ score += arr.pop(); } } return score; }
3.3 逆波蘭式求值_第150題
咱們普通的運算式叫中綴表達式,後綴表達式是把運算符號寫在數字後面,前綴表達式是把運算符號寫在數字前面。好比
中綴表達式:a+b, 用後綴表達式爲:ab+, 用前綴表達式爲:+ab ;
中綴表達式:(a + b) * c - d/e, 用後綴表達式爲:ab+c*de/-, 前綴表達式爲:-*+abc/de
中綴表達式方便人類識別,後綴表達式是爲了方便計算機識別,後綴表達式也叫逆波蘭式;前綴表達式也叫波蘭式
/** * @param {string[]} tokens * @return {number} * 執行用時 :92 ms, 在全部 JavaScript 提交中擊敗了91.72%的用戶 * 內存消耗 :37.2 MB, 在全部 JavaScript 提交中擊敗了40.48%的用戶 */ var evalRPN = function(tokens) { //用棧來實現,碰到數字入棧,碰到運算符號,出棧計算 let arr = [], str = null; for (let i =0; i < tokens.length; i++){ str = tokens[i]; if (str == "+" || str == "-" || str == "*" || str == "/"){ let num2 = parseInt(arr.pop()), num1 = parseInt(arr.pop()), res = 0; if (str == "+"){ res = num1 + num2; } else if(str == "-"){ res = num1 - num2; } else if(str == "*"){ res = num1 * num2; } else if(str == "/"){ res = parseInt(num1 / num2); } arr.push(res); } else{ arr.push(str); } } return arr.length ? arr.pop() : 0; };
3.4 基本計算器_第224題(困難)
/** * 按順序計算,存儲左括號前面的運算符,碰到「-」號,後面匹配的數字爲相反數。至關於去括號計算 * @param s * @return {number} * 執行用時 :112 ms, 在全部 JavaScript 提交中擊敗了88.14%的用戶 * 內存消耗 :36.8 MB, 在全部 JavaScript 提交中擊敗了94.12%的用戶 */ var calculate = function(s){ let arr = [], sum = 0, num = 0, flag = 1; s = "+" + s; //前面加一個+號 for (let i = 0; i < s.length; i++){ if (s[i] == '+' || s[i] == '-'){ //+-後面可能爲數字/空格/左括號 temp = s[i]; let need_up = false; while(s[i+1] < '0' || s[i+1] > '9'){ if (s[i+1] == '('){ //將左括號前面的符號壓入棧。一個左括號匹配一個+-號 need_up = true; arr.push(temp); } i++; } //+-號後面的數字,或者+-號後面口號裏面的數字 while(s[i+1] >= '0' && s[i+1] <= '9'){ num = num*10 + (s[i+1] - '0'); //拼接完整數字 i++; } if (temp == '+'){ sum += flag * num; } else{ sum -= flag * num; } num = 0; if (need_up && arr[arr.length-1] == '-'){ flag *= (-1); //若是左括號前是-號,flag須要反轉 } } else if(s[i] == ')'){ if (arr[arr.length-1] == '-'){ flag *= (-1); //移除-號時,flag須要反轉 } arr.pop(); //碰到右括號移除一個運算符 } } return sum; }
棧鏈表實現和題目完整Demo見:https://github.com/xiaotanit/Tan_DataStruct