Python3 棧的實現

這篇博客主要記錄我在學習python算法時實現棧的過程,這裏棧的實現只是最簡單的實現,其中也包括符號匹配,前綴、中綴以及後綴表達式的實例。參考書目爲:javascript

problem-solving-with-algorithms-and-data-structure-using-python 中文版。css

運行環境:Python3.6 + Jupyter notebook。html

這篇博客至關於學習筆記,請勿轉載html5

原文Python3 棧的實現。java

 

 

Python 實現棧

 

棧的抽象數據結構

 
  • 棧是有序的LIFO(後進先出)。
  • 棧的操做有:
    • Stack() 建立新的空棧。
    • push(item) 添加新項到棧頂部。
    • pop() 刪除棧頂項並返回棧頂項的值。棧被修改。
    • peek() 返回棧頂部項。不修改棧。
    • isEmpty() 測試棧是否爲空,返回 Bool 值。
    • size() 返回棧長度(棧中 item 數量)。
 

利用列表實現棧

In [1]:
class Stack:
    def __init__(self):
        self.items = []
    
    def isEmpty(self):
        return self.items == []
    
    def push(self, item):
        self.items.append(item)
    
    def pop(self):
        return self.items.pop()
    
    def peek(self):
        return self.items[-1]
    
    def size(self):
        return len(self.items)

if __name__ == '__main__':
    s = Stack()
    print(s.isEmpty())
    s.push(4)
    s.push('dog')
    print(s.peek())
    s.push(True)
    print(s.size())
    print(s.isEmpty())
    s.push(8.4)
    print(s.pop())
    print(s.pop())
    print(s.size())    
 
True
dog
3
False
8.4
True
2
 

簡單括號匹配

 

區分括號是否匹配的能力是識別不少編程語言結構的重要部分。具備挑戰的是如何編寫一個算法,可以從左到右讀取一串符號,並決定符號是否平衡。爲了解決這個問題,咱們須要作一個重要的觀察。從左到右處理符號時,最近開始符號必須與下一個關閉符號相匹配(見 Figure 1)。此外,處理的第一個開始符號必須等待直到其匹配最後一個符號。結束符號以相反的順序匹配開始符號。他們從內到外匹配。這是一個能夠用棧解決問題的線索。python

 

括號匹配

 

Figure 1jquery

In [2]:
def parChecker(symbol_sring):
    s = Stack()
    balanced = True
    index = 0
    while index < len(symbol_sring) and balanced:
        symbol = symbol_sring[index]
        if symbol == '(':
            s.push(symbol)
        else:
            if s.isEmpty():
                balanced = False
            else:
                s.pop()
        index += 1
        
    if balanced and s.isEmpty():
        return True
    else:
        return False

print(parChecker('((()))'))
print(parChecker('((()'))
 
True
False
 

注:棧是處理括號匹配極好的數據結構linux

In [8]:
def check(strings):
    s = Stack()
    for string in strings:
        if string == '(':
            s.push(string)
        elif string == ')':
            try:
                s.pop()
            except IndexError:
                return False
        else:
            return False
    return s.isEmpty()

print(check('((()))'))
print(check('()))'))       
 
True
False
 

符號匹配

 

上面的兩個程序僅用於'()'的匹配,實際上 Python 中經常使用的括號有 { } [ ] ( )。符號字符串如:android

{ { ( [ ] [ ] ) } ( ) }css3

[ [ { { ( ( ) ) } } ] ]

[ ] [ ] [ ] ( ) { }

 

都是正確的匹配。

In [12]:
def check(strings):
    s = Stack()
    for string in strings:
        if string in '{[(':
            s.push(string)
        elif string in ')]}':
            try:
                match(open=s.pop(), close=string)
            except(IndexError, ValueError):
                return False
        else:
            return False
    return s.isEmpty()

def match(open, close):
    opens = '{[('
    closers = '}])'
    if opens.index(open) != closers.index(close):
        raise ValueError

print(check('[][][](){}'))
print(check('[{()]'))
 
True
False
 

進制轉換

 

首先看看 Python3 內置函數進制轉換用法

 
  • bin(x) oct(x) hex(x)
In [15]:
help(bin)
 
Help on built-in function bin in module builtins:

bin(number, /)
    Return the binary representation of an integer.
    
    >>> bin(2796202)
    '0b1010101010101010101010'

In [16]:
bin(256)
Out[16]:
'0b100000000'
 

注:二進制字符串以0b開頭

In [17]:
oct(256)
Out[17]:
'0o400'
 

注:八進制字符串以0o開頭

In [18]:
hex(256)
Out[18]:
'0x100'
 

注:十六進制字符串以0x開頭

 
  • int([number | string[, base]])
 

# 其餘進制 to 十進制

In [28]:
int(bin(256), 2)
Out[28]:
256
In [29]:
int(oct(256), 8)
Out[29]:
256
In [30]:
int(hex(256), 16)
Out[30]:
256
 
其餘進制轉換
 

其餘進制轉換能夠以十進制爲媒介

In [31]:
bin(int(oct(256), 8))   # oct2bin
Out[31]:
'0b100000000'
 

Python3 實現十進制 to 其餘進制

 

基本思想是將十進制數不斷迭代除以2,並跟蹤餘數。Figure2 以二進制爲例,展現了棧是解決這個問題的數據結構。

 

dec2bin

 

Figure2

In [45]:
class Stack:
    def __init__(self):
        self.items = []
    
    def isEmpty(self):
        return self.items == []
    
    def push(self, item):
        self.items.append(item)
    
    def pop(self):
        return self.items.pop()
    
    def peek(self):
        return self.items[-1]
    
    def size(self):
        return len(self.items)
    
    def __iter__(self):
        for i in self.items[::-1]:
            yield i
    
def box(dec_number, base=2):
    remstack = Stack()
    base_num = [str(i) for i in range(10)] + [chr(x) for x in range(ord('A'), ord('A')+6)]
    prefix = 'box'
    while dec_number > 0:
        index = dec_number % base
        remstack.push(base_num[index])
        dec_number = dec_number // base
    try:
        return '0'+ prefix[[2, 8, 16].index(base)] + ''.join(remstack)
    except ValueError:
        return ''.join(remstack)
# test
print(box(233, 2))
print(box(233,3))
print(box(233,5))
print(box(233,8))
print(box(233,10))
print(box(233,16))
 
0b11101001
22122
1413
0o351
233
0xE9
 

注:box()函數實現十進制至2到16之間任意進制轉換,可是除bin, oct, hex 外均無前綴。

 

中綴、前綴和後綴表達式

 

咱們日常的算術表達式通常是中綴表達式,例如:A+B*C 就是中綴表達式:運算符在操做數之間。

可是如何辨別運算順序即優先級是個問題。咱們能夠用括號來明確:(A+(B*C))

其實,咱們能夠用前綴表達式:+ A * B C,運算符在操做數前面緊鄰;

利用後綴表達式:A B C * +

這樣一來,運算符的優先級很容易體現:緊鄰的運算符優先級高。

 

Table1

 

Table2

 

下面討論中綴轉後綴表達式,基本思想是:

  1. 建立一個名爲 opstack 的空棧以保存運算符。給輸出建立一個空列表。
  2. 經過使用字符串方法拆分將輸入的中綴字符串轉換爲標記列表。
  3. 從左到右掃描標記列表。
    • 若是標記是操做數,將其附加到輸出列表的末尾。
    • 若是標記是左括號,將其壓到 opstack 上。
    • 若是標記是右括號,則彈出 opstack,直到刪除相應的左括號。將每一個運算符附加到輸出列表的末尾。
    • 若是標記是運算符,*,/,+- ,將其壓入 opstack。可是,首先刪除已經在 opstack 中具備更高或相等優先級的任何運算符,並將它們加到輸出列表中。
  4. 當輸入表達式被徹底處理時,檢查 opstack。仍然在棧上的任何運算符均可以刪除並加到輸出列表的末尾。
 

Figure3

In [58]:
import re
def infix2postfix(infixexpr):
    #肯定優先級
    prec = {}
    prec['('] = 1
    prec['+'] = 2
    prec['-'] = 2
    prec['*'] = 3
    prec['/'] = 3
    opstack = Stack()    # 存儲運算符
    postfix_list = []    # 後綴表達式
    token_list = [i for i in infixexpr if not re.match(r'\s+',i)]
    
    for token in token_list:
        if re.match(r'[0-9a-zA-Z]', token):
            postfix_list.append(token)    # 操做數加入後綴表達式
        elif token == '(':
            opstack.push(token)    # '(' 壓入棧內
        elif token == ')':
            top_token = opstack.pop()
            while top_token != '(':
                postfix_list.append(top_token)
                top_token = opstack.pop()
        else:
            while (not opstack.isEmpty()) and (prec[opstack.peek()] >= prec[token]):
                  postfix_list.append(opstack.pop())
            opstack.push(token)
    while not opstack.isEmpty():
        postfix_list.append(opstack.pop())
    return ' '.join(postfix_list)

print(infix2postfix("A * B + C * D"))
print(infix2postfix("( A + B ) * C - ( D - E ) * ( F + G )"))
 
A B * C D * +
A B + C * D E - F G + * -
 

後綴表達式求值

 

基本思想以下圖:

 

Figure

In [61]:
def postfix_eval(postfix_expr):
    s = Stack()
    token_list = postfix_expr.split()
    
    for token in token_list:
        if re.fullmatch(r'([0-9])|([1-9][0-9]+)', token):
            s.push(int(token))
        else:
            opra2 = s.pop()
            opra1 = s.pop()
            result = do_math(token, opra1, opra2)
            s.push(result)
    return s.pop()

def do_math(op, op1, op2):
    if op == "*":
        return op1 * op2
    elif op == "/":
        return op1 / op2
    elif op == "+":
        return op1 + op2
    else:
        return op1 - op2

print(postfix_eval('7 8 + 3 2 + /'))
 
3.0
 

zxzhu 2018/2/12

 

Reference:

相關文章
相關標籤/搜索