JS資料結構-棧-練習

小諾哥發表於2019-03-22

直接上程式碼。


/*
 * 陣列實現的棧: 順序棧
 * 連結串列實現的棧: 鏈式棧
 * 棧: 當某個資料集只涉及在一端插入和刪除資料, 並且滿足後進先出, 就該使用棧這種資料結構
 * 時間複雜度 O(1)
*/
class ArrayStack {
    constructor () {
        // 陣列
        this.items = []
        // 棧頂位置
        this.top = 0
        // 棧內元素個數
        this.count = 0
    }

    // 入棧: 棧頂位置更新
    push (item) {
        this.items[this.top++] = item
        this.count++
    }

    // 出棧: 返回出棧元素, 同時重新整理棧頂位置
    pop () {
        if (this.count <= 0) { return false }
        this.count--
        return this.items[--this.top]
    }

    // 獲取棧頂元素
    peek () {
        return this.items[this.top - 1]
    }
}

// 案例1: 進位制轉換 如32 => 2進位制, 100000, 125 => 8 進位制, 175

function mulBase (source, base) {
    const s = new ArrayStack()
    let res = ''
    while (source > 0) {
        s.push(source % base)
        source = Math.floor(source /= base)
    }
    while (s.count) {
        res += s.pop()
    }
    return res
}

console.log(mulBase(32, 2), mulBase(125, 8))

// 案例2: 迴文字串, 如'dad' ‘1001’, 從前寫=從後寫

function isPalindrome(word) {
    const s = new ArrayStack()
    let reverseWord = ''
    for (let i = 0; i < word.length; i++) {
        s.push(word[i])
    }
    while (s.count) {
        reverseWord += s.pop()
    }
    return Object.is(reverseWord, word)
}

console.log(isPalindrome('dad'), isPalindrome('racecar'))

// 案例3: 棧模擬階乘  如factorial(5)=120

function factorial (n) {
    if (n <= 0) { return }
    const s = new ArrayStack()
    let res = 1
    while (n) {
        s.push(n--)
    }
    while (s.count) {
        res *= s.pop()
    }
    return res
}

console.log(factorial(5), factorial(2)) // 120 2

// 案例4: 使用棧判斷一個算術表示式的括號是否匹配, 並且返回括號丟失的位置 如2.3+(23/12+(3.14159×0.24)

function matchBracket (str) {
    const index = new ArrayStack()
    const s = new ArrayStack()
    let res = []
    for (let i = 0; i < str.length; i++) {
        if (str[i] === '(') {
            s.push(str[i])
            index.push(i)
        } else if (str[i] === ')') {
            if (s.count <= 0) {
                index.push(i)
            } else {
                s.pop()
                index.pop()
            }
        }
    }
    if (index.count) {
        while (index.count) {
            res.push(index.pop())
        }
        return res.reverse().join(',')
    }
    return  true
}

console.log(matchBracket('1 + (31.1415926 * 0.24))',), matchBracket('1 + (31.1415926 * 0.24'), matchBracket('1 + (31.1415926 * 0.24)'))

// 案例5: 把中綴表示式轉字尾表示式, 並計算結果
// 中綴表示式(Infix Notation)就是我們數學課上學到的最常見的將操作符放在運算元中間的算術表示式。
// 字尾表示式(Postfix Notation)與之相反,是指運算子寫在運算元後面的不含括號的算術表示式,也叫做逆波蘭表示式。比如1 2 3 + -

/*
 * 假設有一箇中綴表示式a+b*c-(d+e):
 *
 * 1. 首先將這個中綴表示式的所有運算加括號((a+(b*c))-(d+e))
 * 2. 然後將所有運算子放到括號後面,這樣就變成了((a(bc)* )+ (de)+ )-
 * 3. 把所有括號去掉abc*+de+-,最後得出的結果就是字尾表示式。
 */

// 中綴表示式轉換成字尾表示式,這裡假設數字都只有1位且沒有空格,例如:'1+2*3-(4+5)'
// 中綴表示式轉換成字尾表示式,這裡假設數字都只有1位且沒有空格,例如:'1+2*3-(4+5)'
function transferHouzhuiExp(str) {
    let input = str.split(''),
    output = new ArrayStack(),
    temp = new ArrayStack(); // output表示輸出,temp表示臨時存放操作符的堆疊
	let yxj = {'+': 1, '-': 1, '*': 2, '/': 2}; // 優先順序
	input.forEach(current => {
		if(/\d/g.test(current)) output.push(current); // 如果是數字,直接輸出
		else if(current == '(') temp.push(current); // 如果左括號,放入堆疊
		else if(current == ')') { // 如果是右括號,迴圈輸出,直至匹配到左括號才停止
			while(temp.count > 0) {
				let op = temp.pop();
				if(op == '(') break;
				output.push(op);
			}
		} else { // 否則,如果是加減乘除,需要針對優先順序做不同處理
			while(temp.count >= 0) {
				let op = temp.peek();
				// 如果堆疊為空,或者上一個操作符是(,或者當前操作符優先順序比棧頂操作符優先順序要高
				if(!temp.count || op == '(' || yxj[current] > yxj[op]) {
					temp.push(current);
					break;
				} else output.push(temp.pop());
			}
		}
	});
	// 輸入迴圈結束後,如果操作符堆疊不為空,還要迴圈輸出
    while(temp.count > 0) output.push(temp.pop());
    // console.log(output) // [ '1', '2', '3', '*', '+', '4', '5', '+', '-' ]
	return output;
}

function parseExpress(input) {
    let reverseInput = new ArrayStack();
    let output = new ArrayStack();
    while (input.count) {
        reverseInput.push(input.pop())
    }
    while (reverseInput.count) {
        let item = reverseInput.pop()
        if(/\d/g.test(item)) output.push(item);
		else {
			let n2 = output.pop();
			let n1 = output.pop();
			output.push(count(n1, n2, item));
		}
    }
	// 這裡偷懶,直接使用eval
    function count(n1, n2, op) {return eval(n1+op+n2);}
	return output.peek();
}

console.log(parseExpress(transferHouzhuiExp('1+2*3-(4+5)'))); // -2

複製程式碼

相關文章