【数据结构与算法基础】队列、栈、递归

栈(Stack)

定义

  • 栈是一种容器,其中添加移除新项总发生在同一端。这一端通常称为“顶部”。与顶部对应的端称为“底部”。
  • 最重要特性:先进后出。这一特性让栈拥有反转元素顺序的功能。如下图:
    【数据结构与算法基础】队列、栈、递归_第1张图片

用数组实现一个顺序栈

  • Python的list及其操作可以提供与栈的使用方式有关的功能,可以使用list来实现栈。这里我们默认list末尾为栈顶。
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[len(self.items)-1]

     def size(self):
         return len(self.items)

用链表实现一个链式栈

  • 为什么需要链式栈?

主要考虑顺序表的两个特点:1)扩大存储需要一次高代价操作;2)顺序表需要完整的大块存储区。

  • 链式栈自然就以表头为栈顶
class ListNode:

    def __init__(self, elem, next=None):
        self.elem = elem
        self.next = next

class LStack():
    def __init__(self):
        self._top = None

    def isEmpty(self):
        return self._top is None

    def push(self, elem):
        self._top = ListNode(elem, self._top)

    def pop(self):
        if self._top is None:
            raise ValueError('wrong in pop!')
        p = self._top
        self._top = p.next
        return p.elem

    def peek(self):
        if self._top is None:
            raise ValueError('wrong in peek!')
        return self._top.elem

    def __repr__(self) -> str:
        cur = self._top
        nums = []
        while cur:
            nums.append(cur._data)
            cur = cur._next
        return "--> ".join(f"{num}" for num in nums)

if __name__ == '__main__':
    stack = LStack()
    for i in range(10):
        stack.push(i)
    print(stack)

编程模拟实现一个浏览器的前进、后退功能

class Browser():

    def __init__(self):
        self.x = LStack()  # 前进
        self.y = LStack()  # 后退

    def view(self, page):
        print('Viewing %s' % page, end='\n')
        self.x.push(page)

    def forward(self):
        if self.y.isEmpty():
            print( 'can not forward!')
            return 
        top = self.y.pop()
        self.x.push(top)
        print('go to %s' % top, end='\n')

    def backward(self):
        if self.x.isEmpty():
            print('can not backward!')
            return
        top = self.x.pop()
        self.y.push(top)
        print('back to %s' % top, end='\n')

    def can_forward(self):
        if self.y.isEmpty():
            return False
        return True

    def can_back(self):
        if self.x.isEmpty():
            return False
        return True

if __name__ == '__main__':
    b = Browser()
    for i in ['a', 'b', 'c']:
        b.view(i)
    while b.can_back():
        b.backward()

    while b.can_forward():
        b.forward()
    b.forward()

Leetcode栗子

  • 20.Valid Parentheses
    http://note.youdao.com/noteshare?id=060ed93ca5145cdb9f9f000468584a5a&sub=B5F79C070F704F229BCC2D7B0503CE8F
  • 150.Evaluate Reverse Polish
    http://note.youdao.com/noteshare?id=301aab49bd4670990a050fe707e525ec&sub=844D15A999444DB4A71C709CCD7FACBF

队列(Queue)

定义

  • 队列也是一种容器,其中最重要的特性就是先进先出。如下图:
    【数据结构与算法基础】队列、栈、递归_第2张图片

用链表实现一个链式队列

  • 最简单的单链表实现的话只能完成O(1)时间复杂度的enqueue操作和O(N)时间复杂度的dequeue操作,所以基于单链表的实现不太高效
  • 如果要实现首尾都是O(1)的话,我们可以尝试双向链表

用数组实现一个顺序队列

  • 基于python的list实现顺序队列,最直接的实现方法入队采取append(),出队采取pop(0),这样就得到一个O(1)时间复杂度的enqueue操作和O(N)时间复杂度的dequeue操作。

实现循环队列

  • 前面说的顺序队列显然不够高效,如果我们要实现所有操作都是O(1)时间,可以再设置一个指针,记住队头的位置,当出队操作完成后,更新队头指针。
  • 基于上述思想,同时为了防止内存空置消耗,可以采用循环队列的实现。
# coding=utf-8
# @author: kaiyuan
# blog: https://blog.csdn.net/Kaiyuan_sjtu
from itertools import chain

class CirQueue:

    def __init__(self, capacity):
        self._items = []
        self._capacity = capacity + 1
        self._head = 0
        self._rear = 0

    def enqueue(self, elem):
        if (self._rear + 1) % self._capacity == self._head:
            return False
        self._items.append(elem)
        self._rear = (self._rear + 1) % self._capacity
        return True

    def dequeue(self):
        if self._head != self._rear:
            item = self._items[self._head]
            self._head = (self._head + 1) % self._capacity
            return item

    def __repr__(self):
        if self._rear >= self._head:
            return " ".join(item for item in self._items[self._head: self._rear])

        else:
            return " ".join(item for item in chain(self._items[self._head:], self._items[:self._rear]))

if __name__ == '__main__':
    cq = CirQueue(10)
    for i in range(10):
        cq.enqueue(str(i))
    print(cq)
    for i in range(5):
        cq.dequeue()
    print(cq)

队列栗子

  • 239.Sliding Window Maximum
    http://note.youdao.com/noteshare?id=452a2230fcaeabe561f36388e2b433ed&sub=70A2634D64344B1F98E230294E2FEAC3

递归

  • 递归的思路就是将问题分解成更小的子问题,知道得到一个足够小的问题可以被很简单的解决。通常涉及调用函数自身。
  • 所有的递归算法必须服从三个重要的定律:
    • 必须具有基本情况,即算法停止递归的条件;
    • 必须改变其状态并向基本情况靠近,即每一步问题的规模都变小;
    • 必须以递归方式调用自身
  • 递归实例:
    • 斐波那契数列:
      http://note.youdao.com/noteshare?id=14bd15bf2064325e56094d2bf7979f68&sub=21FD2E2D79EB41E28C54FC328E8A0ADC
    • 汉诺塔游戏:
    • 求阶乘
    • 爬楼梯:
      http://note.youdao.com/noteshare?id=7cf4e1f6fb826a3852e7ce00f70a58e7&sub=9E2BE6F55BCC470CA94783E6272917B8
    • etc

你可能感兴趣的:(数据结构)