Datawhale编程学习之栈和队列(2)
文章目录
- 1.学习目标
- 1.1 栈
- 1.2 队列
- 1.3 递归
- 1.4 LeetCode练习题
- 2.学习过程
- 2.1 栈
- 2.2 队列
- 2.3 递归
- 2.4 LeetCode练习题
- 3.参考链接
任务2: 3~4天
1.学习目标
1.1 栈
用数组实现一个顺序栈
用链表实现一个链式栈
编程模拟实现一个浏览器的前进、后退功能
1.2 队列
用数组实现一个顺序队列
用链表实现一个链式队列
实现一个循环队列
1.3 递归
编程实现斐波那契数列求值 f(n)=f(n-1)+f(n-2)
编程实现求阶乘 n!
编程实现一组数据集合的全排列
1.4 LeetCode练习题
栈
Valid Parentheses(有效的括号,第20题,难度:Easy)
英文版:Loading…
中文版:Loading…
Longest Valid Parentheses(最长有效的括号,第32题,难度,hard)
英文版:Loading…
中文版:Loading…
Evaluate Reverse Polish Notatio(逆波兰表达式求值,第150题,难度:Medium)
英文版:Loading…
中文版:Loading…
队列
Design Circular Deque(设计一个双端队列,第641题,难度:Medium)
英文版:Loading…
中文版:Loading…
Sliding Window Maximum(滑动窗口最大值,第239题,难度,Hard)
英文版:Loading…
中文版:Loading…
递归
Climbing Stairs(爬楼梯,第70题,难度:Easy)
英文版:Loading…
中文版:Loading…
2.学习过程
2.1 栈
首先应该明确栈的概念及特点,所谓栈,就是一种容器,其中添加移除新项总发生在同一端。这一端通常称为“顶部”。与顶部对应的端称为“底部”。
最重要特性:先进后出。这一特性让栈拥有反转元素顺序的功能。
2.1.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)
2.1.2用链表实现一个链式栈
链式栈自然就以表头为栈顶
class ListNode:def __init__(self, elem, next=None):self.elem = elemself.next = nextclass LStack():def __init__(self):self._top = Nonedef isEmpty(self):return self._top is Nonedef push(self, elem):self._top = ListNode(elem, self._top)def pop(self):if self._top is None:raise ValueError('wrong in pop!')p = self._topself._top = p.nextreturn p.elemdef peek(self):if self._top is None:raise ValueError('wrong in peek!')return self._top.elemdef __repr__(self) -> str:cur = self._topnums = []while cur:nums.append(cur._data)cur = cur._nextreturn "--> ".join(f"{num}" for num in nums)if __name__ == '__main__':stack = LStack()for i in range(10):stack.push(i)print(stack)
采用数组实现栈和采用链表实现栈对比:
采用数组实现栈的优点:一个元素值占用一个存储空间;它的缺点为:如果初始化申请的存储空间太大,会造成空间的浪费,如果申请的存储空间太小,后期会经常需要扩充存储空间,扩充存储空间是个费时的操作,这样会造成性能的下降。
采用链表实现栈的优点是:使用灵活方便,只有在需要的时候才会申请空间,它的缺点为:除了要存储元素外,还需要额外的存储空间存储指针信息。
2.1.3编程模拟实现一个浏览器的前进、后退功能
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!')returntop = self.x.pop()self.y.push(top)print('back to %s' % top, end='\n')def can_forward(self):if self.y.isEmpty():return Falsereturn Truedef can_back(self):if self.x.isEmpty():return Falsereturn Trueif __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()
2.2 队列
所谓队列,就是一种先进先出的数据结构
2.2.1 用数组实现一个顺序队列
下面给出了一种最简单的实现方式,用front来记录队列首元素的位置,用rear来记录队列尾元素往后一个位置。入队列的时候只需要将待入队列放入下标为rear位置,然后同时执行rear+,那么出队列就是front+。
class MyQueue(object):"""队列"""def __init__(self):self.items = []self.front = 0 # 队列头self.rear = 0 # 队列尾def is_empty(self):"""判断队列是否为空"""return self.items == self.reardef enQueue(self, item):"""进队列,从队尾加入"""self.items.append(item)self.rear += 1# self.items.insert(0,item) # 从对头进def deQueue(self):"""出队列,从队头出"""if self.rear > self.front:self.front += 1else:print("队列已经为空")# return self.items.pop() # 从对尾出def getFront(self):if self.is_empty():return Nonereturn self.items[self.front]def getBack(self):if self.is_empty():return Nonereturn self.items[self.rear-1]def size(self):"""返回大小"""return self.rear - self.front# return len(self.items) # 看大小
2.2.2 用链表实现一个链式队列
采用链表实现队列的方法与实现栈的方法类似,分别用两个指针指向队列的首元素与尾元素,而用pHead来指向队列的首元素,用pEnd来指向队列的尾元素。
class LNode(object):def __init__(self,x):self.data = xself.next = Noneclass MyQueue(object):def __init__(self):"""分配头结点"""self.pHead = Noneself.pEnd = Nonedef is_empty(self):"""判断是否为空"""if self.pHead == None:return Truereturn Falsedef size(self):"""获取队列的大小"""size=0p = self.pHeadwhile p != None:# while p is not None:p = p.nextsize += 1return sizedef enQueue(self, element):"""入队列,从队尾加"""p = LNode(element)p.data = elementp.next = Noneif self.pHead == None:self.pHead = self.pEnd=pelse:self.pEnd.next = pself.pEnd = pdef deQueue(self):"""出队列,删除首元素"""if self.pHead == None:print("出队列失败,队列已经为空")self.pHead = self.pHead.nextif self.pHead == None:self.pEnd = Nonedef getFront(self):"""返回队列首元素"""if self.pHead == None:print("获取队列首元素失败,队列已经为空")return Nonereturn self.pHead.datadef getBack(self):"""返回队列尾元素"""if self.pEnd == None:print("获取队列尾元素失败,队列已经为空")return Nonereturn self.pEnd.data
和栈中的优缺点类似,但与栈不同的是,对于队列,用链表的方式比数组更好,因为指针空间在这里的发挥空间更大。
2.2.3 实现一个循环队列
前面说的顺序队列显然不够高效,如果我们要实现所有操作都是O(1)时间,可以再设置一个指针,记住队头的位置,当出队操作完成后,更新队头指针。
基于上述思想,同时为了防止内存空置消耗,可以采用循环队列的实现。
# coding=utf-8
# @author: kaiyuan
# blog: https://blog.csdn.net/Kaiyuan_sjtu
from itertools import chainclass CirQueue:def __init__(self, capacity):self._items = []self._capacity = capacity + 1self._head = 0self._rear = 0def enqueue(self, elem):if (self._rear + 1) % self._capacity == self._head:return Falseself._items.append(elem)self._rear = (self._rear + 1) % self._capacityreturn Truedef dequeue(self):if self._head != self._rear:item = self._items[self._head]self._head = (self._head + 1) % self._capacityreturn itemdef __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)
2.3 递归
2.3.1 编程实现斐波那契数列求值 f(n)=f(n-1)+f(n-2)
class Solution:def Fibonacci(self, n):a = [0,1]if n<=1:return a[n]else:for i in range(2,n+1):a.append(a[i-1]+a[i-2])return a[n]
2.3.2 编程实现求阶乘 n!
def factorial(x):result = 1for i in xrange(2, x + 1):result *= ireturn result
2.3.3 编程实现一组数据集合的全排列
举个例子,比如你要对a,b,c三个字符进行全排列,那么它的全排列有abc,acb,bac,bca,cba,cab这六种可能,你们想想你们是如何得出这六种可能的。没错!就是当指针指向第一个元素a时,它可以是其本身a(即和自己进行交换),还可以和b,c进行交换,故有3种可能,当第一个元素a确定以后,指针移向第二位置,第二个位置可以和其本身b及其后的元素c进行交换,又可以形成两种排列,当指针指向第三个元素c的时候,这个时候其后没有元素了,此时,则确定了一组排列,输出。但是每次输出后要把数组恢复为原来的样子。
简单来说,它的思想即为,确定第1位,对n-1位进行全排列,确定第二位,对n-2位进行全排列。。。显然,这是一种递归的思想。
def permutations(arr, position, end):if position == end:print(arr)else:for index in range(position, end):arr[index], arr[position] = arr[position], arr[index]permutations(arr, position+1, end)arr[index], arr[position] = arr[position], arr[index]
arr = ["a","b","c"]
permutations(arr, 0, len(arr))
2.4 LeetCode练习题
2.4.1 有效括号
class Solution(object):def isValid(self, s):""":type s: str:rtype: bool"""if s is None:return Falsen = len(s)if n % 2 == 1:return Falsestack = []left = '([{'right = ')]}'lookup = { ')':'(', ']':'[', '}':'{'}for v in s:if v in left:stack.append(v)if v in right:if not stack:return Falsep = stack.pop()if p != lookup[v]:return Falsereturn stack == []
2.3.2 逆波兰表达式求值
遍历tokens里面的值,对于不是操作符op的项直接加入到栈stack中,当遇到op时,从栈中取出两个进行操作然后将操作后得到的结果再加入到stack里即可,直到遍历结束,返回stack中的值即可
这里需要注意的是,在做除法的时候,题目要求是除了以后返回商的整数部分
class Solution:def evalRPN(self, tokens):""":type tokens: List[str]:rtype: int"""stack = []for token in tokens:if token not in '+-*/':stack.append(int(token))else:r, l = stack.pop(), stack.pop()if token == '+':stack.append(r + l)elif token == '-':stack.append(l - r)elif token == '*':stack.append(r * l)else:stack.append(int(l / r))return stack.pop()
2.4.3 爬楼梯
递归
class Solution(object):def climbStairs(self, n):""":type n: int:rtype: int"""if n == 0:return 0if n == 1:return 1if n == 2:return 2return self.climbStairs(n-1) + self.climbStairs(n-2)
循环
class Solution:def climbStairs(self, n):""":type n: int:rtype: int"""if n < 3:return n dp = [1] * (n+1)for i in range(2, n+1):dp[i] = dp[i-1] + dp[i-2]return dp[-1]
2.4.4 滑动窗口最大值
最直观的就是暴力,时间复杂度: O(Nk)
我们可以尝试用队列维护一个大小为k的容器,然后每次求最大值后弹出压入循环做
class Solution(object):def maxSlidingWindow(self, nums, k):""":type nums: List[int]:type k: int:rtype: List[int]"""if not nums or len(nums) == 0:return []queue = []res = []for i in range(k):queue.append(nums[i])n = len(nums)for i in range(k, n):res.append(max(queue))queue.pop(0)queue.append(nums[i])res.append(max(queue))return res
3.参考链接
- https://blog.csdn.net/Kaiyuan_sjtu/article/details/88074508
- http://www.xuzhenggen.com/2019/03/03/栈、队列和递归的实现与总结/
- https://blog.csdn.net/qq_31601743/article/details/82053201
Datawhale编程学习之栈和队列(2)相关推荐
- java栈和队列实现删除,数据结构学习--Java栈和队列
栈:先进后出 队列:先进先出 都是数组存放,但是删除的时候不是删除了数组中的数据,而是使用增加游标标识的方式实现删除,"游标标识"加加或者减减完成删除操作,查看的时候,也不是直接查 ...
- 数据结构学习笔记——栈和队列
4 栈与队列 栈是限定仅在表尾进行插入和删除操作的线性表.队列是只允许在一端进行插入操作.而在另一端进行删除操作的线性表. 4.1 栈的定义 栈(stack)是限定仅在表尾进行插入和删除操作的线性 ...
- C语言/C++编程学习:栈的代码实现之数组方案
C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构.C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现 ...
- 408数据结构学习笔记——栈和队列的应用、特殊矩阵的压缩
目录 1.栈在括号匹配中的应用 2.栈在表达式求值中的运用 2.1.中缀表达式转换后缀表达式 2.2.后缀表达式的计算方法 2.3.中缀表达式转换前缀表达式 2.4. 中缀表达式转后缀表达式(机算- ...
- 《数据结构》实验三:栈和队列实验 (实验报告)
一.实验目的 巩固栈和队列数据结构,学会运用栈和队列. 1.回顾栈和队列的逻辑结构和受限操作特点,栈和队列的物理存储结构和常见操作. 2.学习运用栈和队列的知识来解决实际问题. 3.进一步巩固程序调试 ...
- 10.数据结构:栈和队列
大家好,我王有志又回来啦.关注王有志,回复DSA获取数据结构和算法学习资源. 最近被全链路优化搞得焦头烂额,等抽出时间来和大家分享下我司正在做的"全面提速工程". 今天我们来学习线 ...
- 啊哈算法浅识栈与队列
栈与队列 最近学习了栈与队列 文章目录 栈与队列 队列(先进先出) 1.定义 2.解密qq号 栈(后进先出) 1.定义 2.解密回文 总结 队列(先进先出) 1.定义 队列是一种特殊的线性结构它只允许 ...
- 【数据结构】栈和队列OJ练习(栈和队列相互实现+循环队列实现)
目录 前言 1.用队列实现栈 2.用栈实现队列 3.循环队列 前言 前面在学习了栈和队列的实现之后,相信大家对栈和队列的结构和使用方式都有了一些理解. 下面我们就来进行一些练习,这这章的练习相对于原来 ...
- java栈编程题_Java实现栈和队列面试题
面试的时候,栈和队列经常会成对出现来考察.本文包含栈和队列的如下考试内容: (1)栈的创建 (2)队列的创建 (3)两个栈实现一个队列 (4)两个队列实现一个栈 (5)设计含最小函数min()的栈,要 ...
- 《数据结构C语言版》——栈和队列详解(图文并茂),从零开始的学习
哈喽!这里是一只派大鑫,不是派大星.本着基础不牢,地动山摇的学习态度,从基础的C语言语法讲到算法再到更高级的语法及框架的学习.更好地让同样热爱编程(或是应付期末考试 狗头.jpg)的大家能够在学习阶段 ...
最新文章
- 为什么在反向传播中感知器初始值不能为0_人工智能可以为我们做什么?世界皆可二分类...
- opencv_contrib编译失败解决方法
- 评估微型计算机的主要指标,微型计算机的工作过程和主要性能指标.doc
- 正确判断js数据类型 总结记录
- 计算机三级之嵌入式系统学习笔记7
- 网络工程师应该掌握的知识要点
- LeetCode Number of Digit One
- 如何删除旧的和未使用的Docker映像
- linux下运行jar
- 新鲜出炉的自主协同操作系统研讨会纪要
- 远程控制别人计算机,如何远程控制别人的电脑?手把手教你远程操控别人的电脑!...
- AI数学基础之:P、NP、NPC问题
- 全新2021款 Jlink隔离器,ARM仿真器隔离,Jlink,Nu-link,ULINK的隔离,Cortex-M系列隔离仿真
- 儒家学派有哪些代表人物?
- App上架各大应用市场的地址及操作方法
- 纽约州立大学石溪分校计算机专业排名,纽约州立大学石溪分校美国大学排名及专业排名汇总(USNEWS美国大学排名版)...
- 我问自己代言,甄嬛篇
- vue 组件开发 ---- rui-vue-poster 海报制作
- 树莓派3B+使用GPIO实现串口通信
- C语言编程>第一周 ⑧ 输入两个正整数m和n,求其最大公约数和最小公倍数。