栈/队列/递归

栈和队列

在这里插入图片描述栈的抽象数据类型由以下结构和操作定义。如上所述,栈被构造为项的有序集合,其中项被添加和从末端移除的位置称为“顶部”。栈是有序的 LIFO 。栈操作如下。

Stack() 创建一个空的新栈。 它不需要参数,并返回一个空栈。
push(item)将一个新项添加到栈的顶部。它需要 item 做参数并不返回任何内容。
pop() 从栈中删除顶部项。它不需要参数并返回 item 。栈被修改。
peek() 从栈返回顶部项,但不会删除它。不需要参数。 不修改栈。
isEmpty() 测试栈是否为空。不需要参数,并返回布尔值。
size() 返回栈中的 item 数量。不需要参数,并返回一个整数。

基本编程

1. 用数组实现一个顺序栈
参考:
https://facert.gitbooks.io/python-data-structure-cn/3.基本数据结构/3.5.Python实现栈/

class Stack(object):
    def __init__(self):
        self.item=[]

    def push(self,item):
        self.item.append(item)

    def pop(self):
        return self.item.pop()

    def peek(self):
        return self.item[len(self.item)-1]

    def isEmpty(self):
        return self.item==[]

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

2. 用链表实现一个链式栈
参考:
https://blog.csdn.net/zhisuihen6347/article/details/84496891
https://blog.csdn.net/weixin_40520563/article/details/82825526

class Node():
    def __init__(self,item):
        self.item=item
        self.next=None
    
class Stack():
    def __init__(self):
        self.head=None
        self.N=0
    
    def push(self,item):
        oldhead=self.head
        self.head=Node(item)
        self.head.next=oldhead
        self.N+=1

    def pop(self):
        if self.N==0:
            print('Stackunderflow')
        else:
            item=self.head.item
            self.head=self.head.next
            return item
    
    def peek():
        if self.N==0:
            print('Stackunderflow')
        else:
            item=self.head.item
            return item
            
    def isEmpty():
        return self.N==0
    
    def size():
        return self.N

3. 编程模拟实现一个浏览器的前进、后退功能
参考:https://www.jianshu.com/p/0dace6c0fe59

  • 需要建立两个线性栈,一个主栈用来保存当前的页面和之前的几个页面,一个副栈用来保存当前页面之后的几个页面。
  • 当进行后退操作时,副栈获取主栈最上面的元素,主栈删除这个元素,从而后退到前一个页面。
  • 当进行前进操作时,主栈获取副栈最上面的元素,副栈删除这个元素,从而前进到下一个页面。
  • 当新的页面被创建,显然新页面前面不存在其他页面,这是我们要清空副栈。
  • 当主栈中只有一个元素时,显然浏览器已经后退到第一个页面,不能再后退了;当副栈没有元素时,显然浏览器已经前进到最后一个页面了,不能再前进了。
import curses
from curses import wrapper
from datetime import datetime

stdscr = curses.initscr()
count = 1

class StackBrowser(object):
    def __init__(self, length=200):
        self.item = []
        self.length = length

    def push(self, value):
        if self.is_full():
            return None
        else:
            self.item.append(value)

    def pop(self):
        if self.is_empty():
            return None
        self.item.pop()

    def top(self):
        if self.is_empty():
            return None
        return self.item[-1]

    def is_empty(self):
        return len(self.item) == 0

    def is_full(self):
        if len(self.item) >= self.length:
            return True

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

    def show_stack(self):
        print(self.item)

def print_board(stdscr):
    # stdscr.clear()
    # 清除屏幕
    for i in range(4):
        stdscr.addstr('-' * 22 + '\n')
        for j in range(4):
            stdscr.addstr('|')
            if board[i, j]:
                stdscr.addstr('{:^4d}'.format(board[i, j]))
            else:
                stdscr.addstr('    '.format())
        stdscr.addstr('|')
        stdscr.addstr('\n')
    stdscr.addstr('-' * 22 + '\n')


def print_browser(stdscr, order, stack, temp_stack):
    global count
    # datetime.now() 获取当前时间和日期
    # strftime 将日期格式化输出 年--日 时::秒
    now = '网页创建时间:{}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S \n'))
    stdscr.clear()
    
    # 退出程序
    if order == ord('q'):
        exit()
    # 新建页面
    elif order == ord('n'):
        count += 1
        stack.push(now)
        while not temp_stack.is_empty():
            temp_stack.pop()
    # 后退功能
    elif order == ord('b'):
        length = stack.size()
        if length > 1:
            temp_stack.push(stack.top())
            stack.pop()
    # 前进功能
    elif order == ord('f'):
        length = temp_stack.size()
        if length >= 1:
            stack.push(temp_stack.top())
            temp_stack.pop()
    # 打印功能
    if stack.top():
        stack_size = stack.size()
        sum_size = stack.size() + temp_stack.size()
        stdscr.addstr(stack.top())
        stdscr.addstr('共创建了 {} 个页面 \n'.format(count))
        stdscr.addstr('当前页面:第 {} 页 \n'.format(stack_size))
        stdscr.addstr('共 {} 页 \n'.format(sum_size))
        
def main(stdscr):
    global count
    # 不显示输入
    curses.noecho()
    # 创建 stack 类
    # stack 为主栈
    # temp_stack 为副栈
    stack = StackBrowser()
    temp_stack = StackBrowser()
    # 获取当前时间
    now = '网页创建时间:{}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S \n'))
    # 栈保存网页创建时间
    stack.push(now)
    stack_size = stack.size()
    sum_size = stack.size() + temp_stack.size()
    stdscr.addstr(stack.top())
    stdscr.addstr('共创建了 {} 个页面 \n'.format(count))
    stdscr.addstr('当前页面:第 {} 页 \n'.format(stack_size))
    stdscr.addstr('共 {} 页 \n'.format(sum_size))
    while 1:
        order = stdscr.getch()
        print_browser(stdscr=stdscr, order=order, stack=stack, temp_stack=temp_stack)


if __name__ == '__main__':
    wrapper(main)

练习

1. Valid Parentheses(有效的括号)

英文版:https://leetcode.com/problems/valid-parentheses/

中文版:https://leetcode-cn.com/problems/valid-parentheses/

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。

示例 1:
输入: “()”
输出: true
示例 2:
输入: “()[]{}”
输出: true
示例 3:
输入: “(]”
输出: false
示例 4:
输入: “([)]”
输出: false
示例 5:
输入: “{[]}”
输出: true

from pythonds.basic.stack import Stack

def parChecker(symbolString):
    s = Stack()
    balanced = True
    index = 0
    while index < len(symbolString) and balanced:
        symbol = symbolString[index]
        if symbol in "([{":
            s.push(symbol)
        else:
            if s.isEmpty():
                balanced = False
            else:
                top = s.pop()
                if not matches(top,symbol):
                       balanced = False
        index = index + 1
    if balanced and s.isEmpty():
        return True
    else:
        return False
        
def matches(open,close):
    opens = "([{"
    closers = ")]}"
    return opens.index(open) == closers.index(close)

2. Evaluate Reverse Polish Notatio(逆波兰表达式求值)

英文版:https://leetcode.com/problems/evaluate-reverse-polish-notation/

中文版:https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/

根据逆波兰表示法,求表达式的值。
有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
说明:
整数除法只保留整数部分。
给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

示例 1:
输入: [“2”, “1”, “+”, “3”, “"]
输出: 9
解释: ((2 + 1) * 3) = 9
示例 2:
输入: [“4”, “13”, “5”, “/”, “+”]
输出: 6
解释: (4 + (13 / 5)) = 6
示例 3:
输入: [“10”, “6”, “9”, “3”, “+”, “-11”, "
”, “/”, “*”, “17”, “+”, “5”, “+”]
输出: 22
解释:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

from pythonds.basic.stack import Stack

def postfixEval(postfixExpr):
    operandStack = Stack()
    tokenList = postfixExpr.split()

    for token in tokenList:
        if token in "0123456789":
            operandStack.push(int(token))
        else:
            operand2 = operandStack.pop()
            operand1 = operandStack.pop()
            result = doMath(token,operand1,operand2)
            operandStack.push(result)
    return operandStack.pop()

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

参考:

队列

  • 队列是项的有序结合,其中添加新项的一端称为队尾,移除项的一端称为队首。当一个元素从队尾进入队列时,一直向队首移动,直到它成为下一个需要移除的元素为止。
  • 最近添加的元素必须在队尾等待。集合中存活时间最长的元素在队首,这种排序成为 FIFO,先进先出,也被成为先到先得。
    在这里插入图片描述
    基本操作
    队列抽象数据类型由以下结构和操作定义。如上所述,队列被构造为在队尾添加项的有序集合,并且从队首移除。队列保持 FIFO 排序属性。 队列操作如下。

    Queue() 创建一个空的新队列。 它不需要参数,并返回一个空队列。
    enqueue(item) 将新项添加到队尾。 它需要 item 作为参数,并不返回任何内容。
    dequeue() 从队首移除项。它不需要参数并返回 item。 队列被修改。
    isEmpty() 查看队列是否为空。它不需要参数,并返回布尔值。
    size() 返回队列中的项数。它不需要参数,并返回一个整数。

1. 用数组实现一个顺序队列
假定队尾在列表中的位置为 0。这允许我们使用列表上的插入函数向队尾添加新元素。弹出操作可用于删除队首的元素(列表的最后一个元素)。这也意味着入队为 O(n),出队为 O(1)。

class Queue():
    #前进后出
    def __init__(self):
        self.item=[]
    
    def isEmpty(self):
        return self.item==[]
    
    def enqueue(self,item):
        self.item.insert(0,item)

    def dequeue():
        return self.item.pop()

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

2. 用链表实现一个链式队列

注:链表的头部容易被删掉,所以在链表头部添加元素(队尾rear),在尾部删除元素(队首front)

class Empty(Exception):
    def __init__(self, m):
        super().__init__(self)
        self.message = m

    def __str__(self):
        return self.message

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

class Queue2():
    def __init__(self):
        self.rear=None
        self.front=None
        self.N=0

    def isEmpty(self):
        return self.N==0

    def enqueue(self,item):
        a=Node(item)
        if self.N==0:
            self.front=a
            self.rear=a
        else:
            self.rear.next=a
            self.rear=a
        self.N+=1

    def dequeue(self):
        if self.N==0:
            raise Empty('Queue is empty')
        item=self.front.item
        self.front=self.front.next
        self.N-=1
        return item
        
    def size(self):
        return self.N

3. 实现一个循环队列
参考:https://blog.csdn.net/u012626619/article/details/80658397

  • 循环队列包括两个指针, front 指针指向队头元素, rear 指针指向队尾元素的下一个位置。
  • 队列为空的判断条件是:
    front == rear
    队列满的判断条件是:
    (rear+1)%maxsize == front
    队列长度的计算公式:
    (rear-front+maxsize)%maxsize
class SqQueue(object):
    def __init__(self, maxsize):
        self.queue = [None] * maxsize
        self.maxsize = maxsize
        self.front = 0
        self.rear = 0

    # 返回当前队列的长度
    def QueueLength(self):
        return (self.rear - self.front + self.maxsize) % self.maxsize

    # 如果队列未满,则在队尾插入元素,时间复杂度O(1)
    def EnQueue(self, data):
        if (self.rear + 1) % self.maxsize == self.front:
            print("The queue is full!")
        else:
            self.queue[self.rear] = data
           # self.queue.insert(self.rear,data)
            self.rear = (self.rear + 1) % self.maxsize

    # 如果队列不为空,则删除队头的元素,时间复杂度O(1)
    def DeQueue(self):
        if self.rear == self.front:
            print("The queue is empty!")
        else:
            data = self.queue[self.front]
            self.queue[self.front] = None
            self.front = (self.front + 1) % self.maxsize
            return data

    # 输出队列中的元素
    def ShowQueue(self):
        for i in range(self.maxsize):
            print(self.queue[i],end=',')
        print(' ')

练习:

1. Design Circular Deque(设计一个双端队列)

英文版:https://leetcode.com/problems/design-circular-deque/

中文版:https://leetcode-cn.com/problems/design-circular-deque/

你的实现需要支持以下操作:

MyCircularDeque(k):构造函数,双端队列的大小为k。
insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true。
insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true。
deleteFront():从双端队列头部删除一个元素。 如果操作成功返回 true。
deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true。
getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1。
getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1。
isEmpty():检查双端队列是否为空。
isFull():检查双端队列是否满了。

示例:
MyCircularDeque circularDeque = new MycircularDeque(3); // 设置容量大小为3
circularDeque.insertLast(1); // 返回 true
circularDeque.insertLast(2); // 返回 true
circularDeque.insertFront(3); // 返回 true
circularDeque.insertFront(4); // 已经满了,返回 false
circularDeque.getRear(); // 返回 2
circularDeque.isFull(); // 返回 true
circularDeque.deleteLast(); // 返回 true
circularDeque.insertFront(4); // 返回 true
circularDeque.getFront(); // 返回 4

提示:
所有值的范围为 [1, 1000]
操作次数的范围为 [1, 1000]
请不要使用内置的双端队列库。


class MyCircularDeque:

    def __init__(self, k: int):
        """
        Initialize your data structure here. Set the size of the deque to be k.
        """
        self.head, self.tail = -1, -1
        self.vec = [None for _ in range(k)]

        
    def insertFront(self, value: int) -> bool:
        """
        Adds an item at the front of Deque. Return true if the operation is successful.
        """
        if self.isFull(): return False
        self.head -= 1
        if self.head < 0:
            self.head = len(self.vec)-1
        self.vec[self.head] = value
        return True

    def insertLast(self, value: int) -> bool:
        """
        Adds an item at the rear of Deque. Return true if the operation is successful.
        """
        if self.isFull():
            return False
        self.tail += 1
        if self.tail == len(self.vec):
            self.tail = 0
        self.vec[self.tail] = value
        return True

    def deleteFront(self) -> bool:
        """
        Deletes an item from the front of Deque. Return true if the operation is successful.
        """
        if self.isEmpty():
            return False
        self.vec[self.head] = None
        self.head += 1
        return True

    def deleteLast(self) -> bool:
        """
        Deletes an item from the rear of Deque. Return true if the operation is successful.
        """
        if self.isEmpty(): return False
        self.vec[self.tail] = None
        self.tail -= 1
        return True
        

    def getFront(self) -> int:
        """
        Get the front item from the deque.
        """
        print(self.head, len(self.vec))
        return self.vec[self.head]

    def getRear(self) -> int:
        """
        Get the last item from the deque.
        """
        return self.vec[self.tail]

    def isEmpty(self) -> bool:
        """
        Checks whether the circular deque is empty or not.
        """
        for x in self.vec:
            if x != None:
                return False
        return True

    def isFull(self) -> bool:
        """
        Checks whether the circular deque is full or not.
        """
        for x in self.vec:
            if x == None:
                return False
        return True

# Your MyCircularDeque object will be instantiated and called as such:
# obj = MyCircularDeque(k)
# param_1 = obj.insertFront(value)
# param_2 = obj.insertLast(value)
# param_3 = obj.deleteFront()
# param_4 = obj.deleteLast()
# param_5 = obj.getFront()
# param_6 = obj.getRear()
# param_7 = obj.isEmpty()
# param_8 = obj.isFull()

2. Sliding Window Maximum(滑动窗口最大值)

英文版:https://leetcode.com/problems/sliding-window-maximum/

中文版:https://leetcode-cn.com/problems/sliding-window-maximum/

给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口 k 内的数字。滑动窗口每次只向右移动一位。返回滑动窗口最大值。

示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7

注意:
你可以假设 k 总是有效的,1 ≤ k ≤ 输入数组的大小,且输入数组不为空。

#滑动窗口最大值,双端队列
class Solution(object):
    def maxSlidingWindow(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        if not nums or k == 0:
            return []
        if k == 1:
            return nums
        #队列里保存的是索引
        queue = [0]
        res = []
        for i in range(1, len(nums)):
            #队列的第一个索引始终为最大值索引,(当前索引-队列第一个索引)大于滑动窗口长度时,删除第一个索引
            if i-queue[0] >= k:
                queue.pop(0)
            #保证当前最大的值的索引是第一个
            while queue and nums[queue[-1]] < nums[i]:
                queue.pop(-1)
            queue.append(i)
            if i >= k-1:
                res.append(nums[queue[0]])
        return res

递归

编程

1.编程实现斐波那契数列求值 f(n)=f(n-1)+f(n-2)
参考:https://www.jianshu.com/p/fb970cf597b3

def fibonacci(n):
    if n==1 or n==2:
        return 1
    return fibonacci(n-1)+fibonacci(n-2)

2.编程实现求阶乘 n!
https://www.cnblogs.com/ncuhwxiong/p/7060689.html

def factorial(n):
    if n==1 or n==0:
        return 1
    else:
        return n*factorial(n-1)

number = int(input("请输入一个正整数:"))
result = factorial(number)
print("%d 的阶乘是 %d" %(number,result))

3. 编程实现一组数据集合的全排列
参考:https://blog.csdn.net/qq_42015869/article/details/79996227
排列:从n个元素中任取m个元素,并按照一定的顺序进行排列,称为排列;
全排列:当n==m时,称为全排列;

比如:集合{ 1,2,3}的全排列为:
{ 1 2 3}
{ 1 3 2 }
{ 2 1 3 }
{ 2 3 1 }
{ 3 2 1 }
{ 3 1 2 }

递归全排列:
1、列表只有一个元素[a],它的全排列只有a。
2、列表有两个元素[a, b],它的全排列为[a, b], [b, a]:
{ 将第一个元素a固定,对b进行全排列得到[a, b]。
将第一个元素与第二个元素交换得到[b, a]。
将b固定,对a进行全排列,得到[b, a] }
3、列表有三个元素[a, b, c]
{ 将a固定,对bc进行全排列{ 将b固定,对c全排列[abc]。交换bc,将c固定对b进行全排列[acb] }
交换ab,[b, a, c] 对ac进行全排列{ … }
… …}
4、列表有n个元素,将第一个元素固定,对剩下n - 1个元素进行全排列。
将第一个元素依此与其他元素交换,对每次交换后剩下的n-1个元素进行全排列。
5、对剩下的n - 1个元素全排列,同上,固定后对n - 2排列。
6、直到数组数量为1,全排列就是它自己,完成一次排列。

def perm(data, begin, end):
    if begin == end:  # 递归结束条件,当交换到最后一个元素的时候不需要交换,1的全排列还是1print(data)  # 打印一次排列完成后的数组。
    else:
        j = begin
        for i in range(begin, end):  # 从begin到end全排列。
            data[i], data[j] = data[j], data[i]
            perm(data, begin + 1, end)
            data[i], data[j] = data[j], data[i]  # 递归完成后,交换回原来的位置。

练习:

Climbing Stairs(爬楼梯)

英文版:https://leetcode.com/problems/climbing-stairs/

中文版:https://leetcode-cn.com/problems/climbing-stairs/

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。

示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1.1 阶 + 1 阶
2.2 阶
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1.1 阶 + 1 阶 + 1 阶
2.1 阶 + 2 阶
3.2 阶 + 1 阶

class Solution(object):
#借助阶乘
    def factorial(self,n):
        if n==1 or n==0:
            return 1
        else:
            return n*self.factorial(n-1)

    def climbStairs(self, n):
        """
        :type n: int
        :rtype: int
        """
        num=n
        num_1=n
        num_2=0
        methord=0
        while (n-2*num_2)>=0:
            methord=methord+self.factorial(num)/(self.factorial(num_1)*self.factorial(num_2))
            num_2+=1
            num_1=num_1-2
            num=num_1+num_2
        return methord
class Solution:
    #动态规划,用数组记录每个台阶的所有走法个数,时间复杂度降为 O(n)
    def climbStairs(self, n):
        # 递归实现,时间太长,舍去
        # 使用动态规划
        if n<4:
            return n
        else:
            l = [1,2,3]
            for i in range(3,n):
                l.append(l[i-1] + l[i-2])
            return l[i]
class Solution:
#动态规划,由于只需要返回最后一步的所有走法个数,不需要数组记录过程,利用Python的同时赋值特性,只需两个变量就行,空间复杂度降为 O(1)
    def climbStairs(self, n):
        one = two = 1
        for _ in range(1, n):
            one, two = one + two, one
        return one
#递归,显然是个斐波那契数列,时间复杂度 O(2^n)很高,这样没法通过LeetCode,参考 @wikizero 的解法可以加个LRU缓存
class Solution:        
    # @lru_cache(10**8)
    def climbStairs(self, n: int) -> int:
        if n <= 2:
            return n
        return self.climbStairs(n - 1) + self.climbStairs(n - 2)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值