數據結構與算法_渡劫6_棧和隊列

一、棧

1. 定義

棧(stack)是限定僅在表尾進行插入和刪除操作的線性表。

  • 何時選擇棧?

當某個數據集合只涉及在一段插入和刪除數據,並且滿足後進先出、先進後出的特性,首選棧

2. 棧實現

棧的主要操作有兩個:

  • 入棧:棧頂插入一個數據

  • 出棧:棧頂刪除一個數據

根據實現方式不同,分爲:

  • 順序棧:用數組實現的棧

  • 鏈式棧:用鏈表實現的棧

時間複雜度和空間複雜度均是O(1)

3. 棧的應用

最常見的是函數調用棧


int main() {

   int a = 1; 

   int ret = 0;

   int res = 0;

   ret = add(3, 5);

   res = a + ret;

   printf("%d", res);

   reuturn 0;

}



int add(int x, int y) {

   int sum = 0;

   sum = x + y;

   return sum;

}

4. 遞歸

  • 遞歸的祕訣:

將大問題分解爲小問題,寫出遞推公式和終止條件。

  • 警惕:
  1. 不要用人腦去分解遞歸的每個調用關係,即每一個步驟

  2. 不要棧溢出

  3. 不要重複計算

5. 代碼實現棧的操作123

  • 創建空棧

  • push(入棧)

  • pop(出棧)

  • top(取出棧最後壓入的元素)


  • 順序棧,python用表實現,表尾爲棧頂:

# coding=utf-8
'''
@ Summary: python中的list可以滿足棧的操作,但是多了棧所沒有的操作
           缺乏安全性,因此把list作爲類的內部,作爲實現基礎
@ Update:  

@ file:    3-1.棧的順序表實現.py
@ version: 1.0.0

@ Author:  [email protected]
@ Date:    19-10-7 下午6:17
'''

class SStack(object):
    def __init__(self):
        self._elem = []

    def is_empty(self):
        # 是否空棧
        return self._elem == []

    def top(self):
        # 取棧頂元素
        if self.is_empty():
            return None
        return self._elem[-1]

    def push(self, elem):
        # 入棧
        return self._elem.append(elem)

    def pop(self):
        # 出棧
        if self.is_empty():
            return None
        return self._elem.pop()

if __name__ == "__main__":
    st1 = SStack()
    st1.push(3)
    # print(st1.top())
    st1.push(5)
    while not st1.is_empty():
        print(st1.pop())

  • 鏈接表實現棧,表頭爲棧頂:

# coding=utf-8
'''
@ Summary: 用鏈表實現棧的相關操作
@ Update:  

@ file:    3-2.棧的鏈接表實現.py
@ version: 1.0.0

@ Author:  [email protected]
@ Date:    19-10-7 下午6:29
'''

class Node(object):
    def __init__(self, val):
        self.val = val
        self.next = None

    def __repr__(self):
        return str(self.val)

class LStack(object):
    @ staticmethod
    def c_link(vals):
        # create link
        if not vals:
            return None
        head = Node(len(vals))  # head node
        move = head  # move node
        for val in vals:
            tmp = Node(val)
            move.next = tmp
            move = tmp
        return head.next

    @ staticmethod
    def p_link(link):
        # print link
        if not link:
            return None
        while link:
            yield link.val
            link = link.next

    @ staticmethod
    def top(link):
        # 取出棧頂元素
        if not link:
            return None
        return link.val

    @ staticmethod
    def push(link, elem):
        # 入棧
        elem = Node(elem)
        elem.next = link
        return elem

    @ staticmethod
    def pop(link):
        # 出棧
        if not link:
            return None
        p = link
        link = link.next
        return p.val, link


if __name__ == "__main__":
    _list = list(range(1, 7))
    # link = None
    # for i in _list:
    #     link = LStack.push(link, i)
    # for node in LStack.p_link(link):
    #     print(node, end=" ")

    top = LStack.c_link(_list)
    for node in LStack.p_link(top):
        print(node, end=" ")
    print()
    # output: 1 2 3 4 5 6

    top = LStack.push(top, 10)
    for node in LStack.p_link(top):
        print(node, end=" ")
    print()
    # output: 10 1 2 3 4 5 6

    elem, top = LStack.pop(top)
    print(elem)
    for node in LStack.p_link(top):
        print(node, end=" ")
    print()

    elem = LStack.top(top)
    print(elem)


  • 代碼練習1:二進制轉十進制

# coding=utf-8
'''
@ Summary: 二進制轉十進制,用棧實現
@ Update:  

@ file:    3-3.二進制轉十進制.py
@ version: 1.0.0

@ Author:  [email protected]
@ Date:    19-10-7 下午7:16
'''

class SStack(object):
    def __init__(self):
        self._elem = []

    def is_empty(self):
        # 是否空棧
        return self._elem == []

    def push(self, elem):
        # 入棧
        return self._elem.append(elem)

    def pop(self):
        # 出棧
        if self.is_empty():
            return None
        return self._elem.pop()

if __name__ == "__main__":
    _input = 1010110
    _list = list(map(int, str(_input)))

    st1 = SStack()
    for i in _list:
        st1.push(i)
    print(st1._elem)

    sum = count = 0
    while not st1.is_empty():
        # print(st1.pop(), end=" ")
        _pop = st1.pop()
        sum = sum + _pop * 2**count
        count += 1
    print(sum)

  • 代碼練習2:二進制轉八進制

# coding=utf-8
'''
@ Summary: 用棧實現二進制轉八進制
@ Update:  

@ file:    3-3.二進制轉八進制.py
@ version: 1.0.0

@ Author:  [email protected]
@ Date:    19-10-7 下午10:05
'''

class SStack(object):
    def __init__(self):
        self._elem = []

    def is_empty(self):
        # 是否空棧
        return self._elem == []

    def push(self, elem):
        # 入棧
        return self._elem.append(elem)

    def pop(self):
        # 出棧
        if self.is_empty():
            return None
        return self._elem.pop()

if __name__ == "__main__":
    _input = 11001001  # 初始值
    _list = list(map(int, str(_input)))  # int to list
    while len(_list) % 3 != 0:  # 三的倍數,不夠補0
        _list.insert(0, 0)

    _list.insert(0, 0)  # 爲了後面的while 循環到棧底的時候繼續運行,增加一個0
    st1 = SStack()  # 二進制的棧
    st2 = SStack()  # 八進制的棧
    for i in _list:  # 二進制digui入棧
        st1.push(i)
    print(st1._elem)

    # 二進制 三個彈出,計算,壓入八進制棧
    tmp = []  # 儲存三個二進制數
    count = 1  # 計數
    while not st1.is_empty():
        if count < 4:
            # 找到三個二進制數
            tmp.append(str(st1.pop()))
            count += 1
        else:
            num = "".join(tmp)
            # 二進制轉八進制
            num = int(num[2]) * 2**2 + int(num[1]) * 2**1 + \
                  int(num[0]) * 2**0
            st2.push(num)  # 入棧
            count = 1
            tmp = []

    print(st2._elem)

  • 代碼練習3:逆波蘭表達式

# coding=utf-8
'''
@ Summary: 逆波蘭表達式,又稱後綴表達式
@ Update:  

@ file:    3-4.逆波蘭表達式.py
@ version: 1.0.0

@ Author:  [email protected]
@ Date:    19-10-8 下午3:29
'''

class Node(object):
    def __init__(self, val):
        self.val = val
        self.next = None

    def __repr__(self):
        return str(self.val)

class LStack(object):
    def __init__(self):
        self.top = None

    def push(self, elem):
        # 入棧
        elem = Node(elem)
        elem.next = self.top
        self.top = elem

    def pop(self):
        # 出棧
        if not self.top:
            return None
        p = self.top
        self.top = self.top.next
        return p.val

    def p_link(self):
        if not self.top:
            return None
        while self.top:
            print(self.top.val, end=" ")
            self.top = self.top.next


def RPN(_list):
    # _int = [str(i) for i in range(20)]
    operators = "+-*/"

    st1 = LStack()
    for item in _list:
        try:
            item = int(item)
            st1.push(item)
        except ValueError:
            if item in operators:
                x2 = st1.pop()
                x1 = st1.pop()
                if item == "+":
                    st1.push(x1 + x2)
                elif item == "-":
                    st1.push(x1 - x2)
                elif item == "*":
                    st1.push(x1 * x2)
                elif item == "/":
                    st1.push(x1 / x2)

    return st1.top

if __name__ == "__main__":
    # _input = "1 2 - 4 5 + *"  # -9
    # _input = "5 6 7 + 8 * - 9 4 / +"  # -96.75
    _input = "3 5 - 6 17 4 * + * 3 /"
    _list = _input.split(" ")  # str to list

    print(RPN(_list))

---



- 代碼練習4: 中綴表達式轉後綴表達式

```python

# coding=utf-8
'''
@ Summary: 中綴表達式轉換爲後綴表達式
@ Update:  

@ file:    3-6.逆波蘭進階.py
@ version: 1.0.0

@ Author:  [email protected]
@ Date:    19-10-8 下午4:43
'''
class Node(object):
    def __init__(self, val):
        self.val = val
        self.next = None

    def __repr__(self):
        return str(self.val)

class LStack(object):
    def __init__(self):
        # 初始化 top node
        self._top = None

    def push(self, elem):
        # 入棧 self._top 始終指向first node
        elem = Node(elem)
        elem.next = self._top
        self._top = elem

    def pop(self):
        # 出棧 self._pop指向第二個節點, 返回第一個節點的值
        if not self._top:
            return None
        p = self._top
        self._top = self._top.next
        return p.val


def mid_2_after(l):
    # 中綴轉後綴

    # 優先級
    ops_relu = {"+":1, "-":1,
                "*":2, "/":2,
                "(":1, ")":1}

    stack = LStack()  # 儲存運算符和"()"的棧
    lt = []  # 輸出列表

    for item in l:
        # 遇到數字添加進列表中,非數字進行棧的相關操作
        try:
            item = int(item)
            lt.append(item)
        except ValueError:
            if item != ")":
                # 優先級順序,棧頂的優先級高則彈出棧中所有元素
                if stack._top and ops_relu[item] < ops_relu[stack._top.val]:
                    while stack._top:
                        lt.append(stack.pop())
                stack.push(item)  # 入棧
            else:
                # 遇到")", 彈出棧中的元素, 直到"("爲止
                while stack._top.val != "(":
                    lt.append(stack.pop())
                stack.pop()  # delete "("

    # 彈出棧中所有元素
    while stack._top:
        lt.append(stack.pop())

    return lt


if __name__ == "__main__":
    _input = "1 + ( 2 - 3 ) * 4 + 10 / 5"  # "1 2 3 - 4 * + 10 5 / +"
    _list = _input.split(" ")  # str to list
    print("The origin list is: {}.".format(_list))

    print(mid_2_after(_list))

  • 代碼練習4:斐波那契

最常見的棧:函數調用


# coding=utf-8
'''
@ Summary: 斐波那契
@ Update:  

@ file:    3-7.斐波那契.py
@ version: 1.0.0

@ Author:  [email protected]
@ Date:    19-10-8 下午10:18
'''

def fib(n):
    # 遞歸
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

def fib2(n):
    # 生成器
    if n < 0:
        return None
    a, b = 0, 1
    for _ in range(n):
        yield b
        a, b = b, b + a


if __name__ == "__main__":
    for i in fib2(5):
        print(i)

  • 代碼練習5:字符串反轉

# coding=utf-8
'''
@ Summary: 遞歸實現字符串翻轉
@ Update:  

@ file:    3-8.字符串翻轉.py
@ version: 1.0.0

@ Author:  [email protected]
@ Date:    19-10-9 上午9:05
'''

def reverse(s):
    if len(s) <= 1:
        return s
    elif len(s) == 2:
        return s[1] + s[0]
    return s[-1] + reverse(s[:-1])

if __name__ == "__main__":
    s = "ABC"
    print(reverse(s))

  • 代碼練習6:漢諾塔

# coding=utf-8
'''
@ Summary: 遞歸實現漢諾塔
@ Update:  

@ file:    3-9.漢諾塔.py
@ version: 1.0.0

@ Author:  [email protected]
@ Date:    19-10-9 上午9:19
'''
def hanoi(n, x, y, z):
    if n == 1:
        print(x + " --> " + z)
    else:
        # 將n-1個從X移動到Y上,藉助Z
        hanoi(n-1, x, z, y)
        # 將第n個從X移動到Z上
        print(x + " --> " + z)
        # 將n-1個從Y移動到Z上,藉助X
        hanoi(n-1, y, x, z)

if __name__ == "__main__":
    hanoi(4, "X", "Y", "Z")

  • 代碼練習7:八皇后問題

  • 代碼練習8:括號匹配



# coding=utf-8
'''
@ Summary: 
@ Update:  

@ file:    括號匹配.py
@ version: 1.0.0

@ Author:  [email protected]
@ Date:    10/15/19 9:55 PM
'''

def is_valid(s):
    if not s:
        return None
    parent = "()[]{}"
    opposite = {")":"(", "]":"[", "}":"{"}
    st = []  # 棧
      for item in s:
        if item in parent:
            st.append(item)
        if item in opposite:
            del_elem = st.pop()
            if not st or opposite[del_elem] != st.pop():
                return False
    return not st

if __name__ == "__main__":
    s = "9(){}"
    if is_valid(s):
        print("{} is OK.".format(s))
    else:
        print("{} is not OK.".format(s))

二、隊列

1. 定義

先進先出,比如排隊買票

2. 入隊、出隊

  • 鏈式隊列(鏈表表頭爲隊頭,表尾爲隊尾)

# coding=utf-8
'''
@ Summary: 鏈表實現隊列,任意輸入一串字符 # 結束
@ Update:  
@ file:    隊列.py
@ version: 1.0.0
@ Author:  [email protected]
@ Date:    10/17/19 4:21 PM
'''
class Node(object):
    def __init__(self, val):
        self.val = val

        self.next = None


    def __repr__(self):
        return str(self.val)


class LQueue(object):
    def __init__(self):
        self.front = Node(0)
        self.rear = Node(0)


    def is_empty(self):
        # 判斷是否爲空
        return self.front.val == self.rear.val


    def enqueue(self, elem):
        # 入隊
        elem = Node(elem)
        if self.is_empty():
            self.front.next = elem
        self.rear.next = elem
        self.rear = elem


    def dequeue(self):
        # 出隊
        if self.is_empty():
            return None
        del_node = self.front.next.val
        if self.front.next == self.rear:
            # 當只有一個元素的時候
            self.front = self.rear
        else:
            self.front.next = self.front.next.next
        return del_node


    def get_head(self):
        # 查看隊頭元素
        if self.is_empty():
            return None
        return self.front.next


    def p_queue(self):
        # print queuep
        if self.is_empty():
            return None
        tmp = self.front.next
        while tmp:
            print(tmp, end=" ")
            tmp = tmp.next


if __name__ == "__main__":
    # 創建隊列
    lq = LQueue()
    data = input("請輸入(#結束):")
    while data != "#":
        lq.enqueue(data)
        data = input("請輸入(#結束):")
    lq.p_queue()  # 最初的鏈式列表

  • 順序隊列的出隊和入隊

# coding=utf-8
'''
@ Summary: 順序隊列
@ Update:  
@ file:    隊列2.py
@ version: 1.0.0
@ Author:  [email protected]
@ Date:    10/18/19 12:19 AM
'''
class SQueue(object):
    def __init__(self):
        self._queue = []


    def is_empty(self):
        return self._queue == []


    def en_queue(self, elem):
        self._queue.insert(0, elem)
        # self._queue.append(elem)


    def de_queue(self):
        if self.is_empty():
            return None
        del_node = self._queue.pop()
        # del_node = self._queue.pop(0)
        return del_node


    def p_queue(self):
        if self.is_empty():
            return None
        return self._queue


if __name__ == "__main__":
    sq = SQueue()
    data = input("請輸入(輸入# 結束):")
    while data != "#":
        sq.en_queue(data)
        data = input("請輸入(輸入# 結束):")
    print(sq.p_queue())
    sq.de_queue()
    print(sq.p_queue())


隊列跟棧一樣,也是一種操作受限的線性表數據結構。

3. 循環隊列


# coding=utf-8
'''
@ Summary: 順序表實現循環隊列
@ Update:  
@ file:    循環隊列.py
@ version: 1.0.0
@ Author:  [email protected]
@ Date:    10/18/19 10:24 AM
'''

class SQueue(object):
    def __init__(self, maxsize):
        self.maxsize = maxsize  # 自定義隊列長度
        self._queue = [None] * maxsize
        self.front = 0  # 指向隊頭
        self.rear = 0  # 指向隊尾元素的下一個位置


    def is_empty(self):
        # 判斷是否爲空
        return self.front == self.rear


    def q_length(self):
        # 當前隊列長度計算 (rear-front+maxsize)%maxsize
        return (self.rear - self.front + self.maxsize) % self.maxsize


    def enqueue(self, elem):
        # 隊尾入隊 elem push
        if (self.rear + 1) % self.maxsize == self.front:
        # 判斷隊列是否填滿 (rear+1)%maxsize == front
            print("The queue is full!")
        self._queue[self.rear] = elem
        self.rear = (self.rear + 1) % self.maxsize


    def dequeue(self):
        # 隊頭出隊 elem pull
        if self.is_empty():
            return None
        del_elem = self._queue[self.front]
        self._queue[self.front] = None
        self.front = (self.front + 1) % self.maxsize
        return del_elem


    def p_queue(self):
        # print queue
        if self.is_empty():
            return None
        for i in range(self.maxsize):
            print(self._queue[i], end=" ")
        print()



if __name__ == "__main__":
    s = "abcdde"
    sq = SQueue(15)  # 循環隊列
    for item in range(10):
        # 入隊
        sq.enqueue(item)
    sq.p_queue()  # 原始隊列
    for i in range(5):  # 刪除隊頭的5個元素:0~4
        sq.dequeue()
    sq.p_queue()
    for i in range(8):  # 從隊尾增加8個元素:0~7
        sq.enqueue(i)
    sq.p_queue()

4.代碼練習:

  • 用兩個棧實現隊列 (劍指offer和力扣)

# coding=utf-8
'''
@ Summary: 劍指Offer-用兩個棧來實現一個隊列,完成隊列的Push和Pop操作
@ Update:  (先進先出)

@ file:    3-15.2棧實現隊列.py
@ version: 1.0.0

@ Author:  [email protected]
@ Date:    19-10-21 下午10:00
'''
import pysnooper

class SStack(object):

    def __init__(self):
        self.stackA = []
        self.stackB = []

    def is_empty(self):
        # 判斷satackA是否爲空
        return self.stackA == []

    def push(self, elem):
        # stackA入棧
        self.stackA.append(elem)

    @pysnooper.snoop(watch=("self.stackA", "self.stackB"))
    def s_pop(self):
        # stackA 出棧 stackB 入棧
        if self.stackB:
            return self.stackB.pop()
        while self.stackA:
            self.stackB.append(self.stackA.pop())
        return self.stackB.pop()


if __name__ == "__main__":
    ss = SStack()
    for i in range(7):
        ss.push(i)
    print(ss.s_pop())
    ss.push(8)
    print(ss.s_pop())

  • 用兩個隊列實現棧(力扣)

# coding=utf-8
'''
@ Summary: 進棧:元素入隊列A(後進先出)
           出棧:判斷如果隊列A只有一個元素,則直接出隊。否則,把隊A中的元素出隊併入隊B,
           直到隊A中只有一個元素,再直接出隊。爲了下一次繼續操作,互換隊A和隊B。
@ Update:

@ file:    3-16.2隊列實現棧.py
@ version: 1.0.0

@ Author:  [email protected]
@ Date:    19-10-22 下午7:12
'''
class SQueue(object):
    def __init__(self):
        self.queueA = []
        self.queueB = []

    def is_empty(self):
        return self.queueA == [] and self.queueB == []

    def enqueue(self, elem):
        # 入隊
        self.queueA.append(elem)

    def dequeue(self):
        #出隊
        if self.is_empty():
            return None
        # if len(self.queueA) == 1:
        #     return self.queueA.pop()
        while len(self.queueA) > 1:
            self.queueB.append(self.queueA.pop(0))
        self.queueA, self.queueB = self.queueB, self.queueA
        return self.queueB.pop()


if __name__ == "__main__":
    sq = SQueue()
    for i in range(1, 7):
        sq.enqueue(i)
    print(sq.dequeue())
    sq.enqueue(3)
    print(sq.dequeue())
    print(sq.dequeue())

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章