如何用栈实现浏览器的前进和后退?

640?wx_fmt=jpeg

2019 年第 79 篇文章,总第 103 篇文章

数据结构与算法系列的第四篇文章,前三篇文章:

前言

浏览器的前进和后退功能怎么用栈来实现呢?

这里先介绍一下栈的定义和实现,并介绍它的一些常用的应用,最后再简单实现一个简单的浏览器前进和后退的操作。

栈是一种“操作受限”的线性表只允许在一端插入和删除数据,特点就是后进先出、先进后出

目录:

  • 栈的实现
  • 栈在函数调用中的应用
  • 栈在表达式求值中的应用
  • 栈在括号匹配中的应用
  • 利用栈实现浏览器的前进和后退功能

栈的实现

栈既可以通过数组实现,也可以通过链表实现。数组实现的栈,称为顺序栈;链表实现的栈,称为链式栈

这里用 Python 分别实现这两种栈。

顺序栈的实现代码:

from typing import Optional	class ArrayStack:	def __init__(self, nums):	# 初始化数组	self._data = list()	# 数组的大小	self._nums = nums	# 数组元素个数	self._count = 0	def push(self, data) -> bool:	'''	入栈	:param data:	:return:	'''	if (self._count + 1) == self._nums:	# 栈已经满了	return False	self._data.append(data)	self._count += 1	return True	def pop(self) -> Optional[int]:	'''	出栈	:return:	'''	if self._count:	value = self._data[self._count - 1]	self._data.pop(self._count - 1)	self._count -= 1	return value	def __repr__(self) -> str:	'''	打印栈	:return:	'''	nums = reversed(self._data)	return " ".join("[{}]".format(num) for num in nums)	if __name__ == '__main__':	stack = ArrayStack(10)	for i in range(15):	stack.push(i)	# 输出:[8] [7] [6] [5] [4] [3] [2] [1] [0]	print(stack)	for _ in range(5):	stack.pop()	# 输出:[3] [2] [1] [0]	print(stack)

链式栈的实现代码:

from typing import Optional	# 链表结点类	
class Node:	def __init__(self, data: int, next=None):	self._data = data	self._next = next	class LinkedStack:	"""	链表实现的栈	"""	def __init__(self):	self._top = None	def push(self, value: int):	'''	入栈,将新结点放在链表首部	:param value:	:return:	'''	new_top = Node(value)	new_top._next = self._top	self._top = new_top	def pop(self) -> Optional[int]:	if self._top:	value = self._top._data	self._top = self._top._next	return value	def __repr__(self) -> str:	'''	打印栈元素	:return:	'''	current = self._top	nums = []	while current:	nums.append(current._data)	current = current._next	return " ".join("[{}]".format(num) for num in nums)	if __name__ == '__main__':	stack = LinkedStack()	# 入栈	for i in range(9):	stack.push(i)	# 输出:入栈结果:  [8] [7] [6] [5] [4] [3] [2] [1] [0]	print('入栈结果: ', stack)	# 出栈	for _ in range(3):	stack.pop()	# 输出:出栈结果:  [5] [4] [3] [2] [1] [0]	print('出栈结果: ', stack)

看完上述实现代码,这里思考下栈的操作的时间和空间复杂度分别是多少呢?

对于空间复杂度,入栈和出栈都只需要一两个临时变量存储空间,所以空间复杂度是 O(1)

时间复杂度,入栈和出栈也只是涉及到栈顶的数据的操作,因此时间复杂度是 O(1)

栈在函数调用中的应用

栈的一个比较经典的应用就是函数调用栈

操作系统给每个线程分配了一块独立的内存空间,它被组织为“栈”这种结构,用来存储函数调用时的临时变量。每进入一个函数,就会将临时变量作为一个栈帧入栈,当被调用的函数执行完成,返回之后,将这个函数对应的栈帧出栈

下面是一个例子:

def add(x, y):	sum = x + y	return sum	def main():	a = 1	ret = 0	res = 0	ret = add(3, 5)	res = a + ret	print('%d', res)

这段代码中,main() 函数调用了 add() 函数,获取计算结果,并且和变量 a 相加,得到最终结果 res ,然后打印。这段代码中的函数调用栈情况如下所示,它显示的是在调用 add() 函数并执行相加时的情况。

640?wx_fmt=png

栈在表达式求值中的应用

栈的另一个常用场景就是实现表达式求值,编译器实现表达式求值的方法是通过两个栈来实现。一个保存操作数,一个保存运算符。其实现思路如下:

  1. 从左到右遍历表达式,遇到数字,就压入操作数栈
  2. 遇到运算符,就和运算符栈的栈顶元素进行比较,如果比栈顶元素的优先级高,就压入栈如果比栈顶元素的优先级低或者是相同优先级,就取出栈顶的运算符,然后从操作数栈的栈顶取 2 个操作数,进行计算后将计算结果放入操作数栈,接着继续比较运算符和当前运算符栈的栈顶元素。

这里给出一个例子,如何计算表达式 3+5*8-6,如下图所示:

640?wx_fmt=png

栈在括号匹配中的应用

栈的第三个应用是可以检查表达式中的括号是否匹配。

通过栈来检查括号匹配问题的方法思路如下:

  1. 从左到右扫描表达式,遇到左括号,压入栈中;
  2. 如果扫描到右括号,从栈顶取出一个左括号,如果可以匹配,继续扫描表达式;但如果不能匹配,或者栈为空,说明表达式是非法的格式;
  3. 扫描完毕后,栈说空,说明表达式是合法格式;否则,说明还有未匹配的左括号,表达式是非法格式。

利用栈实现浏览器的前进和后退功能

最后一个应用是实现浏览器的前进和后退功能,这里采用两个栈来解决。

我们使用两个栈,X 和 Y,我们把首次浏览的页面依次压入栈 X,当点击后退按钮时,再依次从栈 X 中出栈,并将出栈的数据依次放入栈 Y。当我们点击前进按钮时,我们依次从栈 Y 中取出数据,放入栈 X 中。当栈 X 中没有数据时,那就说明没有页面可以继续后退浏览了。当栈 Y 中没有数据,那就说明没有页面可以点击前进按钮浏览了

实现代码如下所示:

from Stack.linked_stack import LinkedStack	
import copy	class NewLinkedStack(LinkedStack):	def is_empty(self):	return not self._top	class Browser():	def __init__(self):	# forward_stack 保存打开浏览器或者前进时候的页面	self.forward_stack = NewLinkedStack()	# back_stack 保存后退时候从 forward_stack 弹出的页面	self.back_stack = NewLinkedStack()	def can_forward(self):	if self.back_stack.is_empty():	return False	return True	def can_back(self):	if self.forward_stack.is_empty():	return False	return True	def open(self, url):	print('Open new url {}'.format(url))	self.forward_stack.push(url)	def back(self):	if self.forward_stack.is_empty():	return	# 点击后退按钮,从 forward_stack 中弹出当前页面,并保存到 back_stack 中	top = self.forward_stack.pop()	self.back_stack.push(top)	print('back to {}'.format(top))	def forward(self):	if self.back_stack.is_empty():	return	# 点击前进按钮,从 back_stack 中弹出,然后保存到 forward_stack 中	top = self.back_stack.pop()	self.forward_stack.push(top)	print('forward to {}'.format(top))	def __repr__(self):	copy_forward_stack = copy.deepcopy(self.forward_stack)	url_list = list()	while not copy_forward_stack.is_empty():	url_list.append(copy_forward_stack.pop())	return " ".join("{}".format(url) for url in url_list)	if __name__ == '__main__':	browser = Browser()	browser.open('a')	browser.open('b')	browser.open('c')	print('open the browser: {}'.format(browser))	if browser.can_back():	browser.back()	if browser.can_forward():	browser.forward()	browser.back()	browser.back()	browser.back()	print('browser: {}'.format(browser))	

输出结果:

Open new url a	
Open new url b	
Open new url c	
open the browser: c b a	
back to c	
forward to c	
back to c	
back to b	
back to a	
browser:

总结

本文先介绍了如何实现一个栈,然后介绍了栈的几个应用,包括函数调用、表达式求值、括号匹配、浏览器前进和后退的实现等。

欢迎关注我的微信公众号--算法猿的成长,或者扫描下方的二维码,大家一起交流,学习和进步!

640?wx_fmt=png

如果觉得不错,在看、转发就是对小编的一个支持!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/408467.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据科学家令人惊叹的排序技巧

2019 年第 80 篇文章,总第 104 篇文章本文大约 7800 字,阅读大约需要20分钟原题 | Surprising Sorting Tips for Data Scientists作者 | Jeff Hale原文 | https://towardsdatascience.com/surprising-sorting-tips-for-data-scientists-9c360776d7e译者 …

几个有趣的python技巧

2019 年第 82 篇文章,总第 106 篇文章标题 | python-is-cool作者 | chiphuyen原文 | https://github.com/chiphuyen/python-is-cool译者 | kbsc13("算法猿的成长"公众号作者)声明 | 翻译是出于交流学习的目的,欢迎转载,但请保留本文…

20191215周学习总结

最近会打算每周总结一下学习的内容,主要内容可能是看过的书的一些学习笔记、论文阅读、学习的知识点以及推荐一些文章。这周的学习包括:推荐系统的知识点整理机器学习的技巧学习linux下两台机器的ssh免密登陆方式书籍阅读效率方法推荐系统因为工作方向的…

玩转12306之系统登录

【申明:本文所涉及的技术和分析的目的都是为了学习和交流,任何人使用文中所提的技术或成果做出的违法事情与我无关,大家购买火车票还是去12306官网上去购买。】 从今天起,我开始分析12306网站的Http请求,以及编写一个客…

Nginx快速搭建和基本使用

2019年第 83 篇文章,总第 107 篇文章最近在工作中项目需要上线,所以也了解到关于一些部署上线的知识内容,Nginx 就是其中一个知识点,主要是可以用它来进行负载均衡,本文的目录如下:简介安装配置基本使用简介…

第二期周总结

第二期的周总结,这次学习的内容可能没有上次那么广泛,主要是因为这周我负责的模块需要测试并进行上线,所以主要学习了解的就是工程开发方面的内容,准确说是部署上线的内容,所以本周主要简单总结这次上线过程的一些内容…

AI知识点(1)--激活函数

2019年第 84 篇文章,总第 108 篇文章本文大约 5000 字,阅读大约需要 15 分钟AI知识点(AI Knowledge)系列第一篇文章--激活函数。本文主要的目录如下:激活函数的定义为什么需要激活函数常见的激活函数1. 激活函数的定义…

Linux 定时执行shell 脚本

2019年第 85 篇文章,总第 109 篇文章本文大约2000字,阅读大约需要6分钟crontab 可以在指定的时间执行一个shell脚本以及执行一系列 Linux 命令。定时执行shell 脚本简单给出执行 shell 脚本的步骤。首先是编写一个测试脚本--test.sh# 创建脚本 $ vim tes…

RS(1)--10分钟了解什么是推荐系统

总第 110 篇文章,本文大约 3200 字,阅读大约需要 10 分钟2020 年第一篇技术文章,以一个新的系列开始--推荐系统(Recommend System),第一篇文章会简单介绍推荐系统的定义和应用,目录如下&#xf…

当搭配遇上个性化推荐

总第 111 篇文章,本文大约 3000 字,阅读大约需要 10 分钟今天介绍的是一篇个性化搭配推荐的论文,是 2017 年时候的论文,这也是比较早的开始结合搭配和个性化推荐的一个工作,基于度量学习和排序学习的方法。论文题目&am…

2020年周记(1/50)

总第 112 篇文章,本文大约 1200 字,阅读大约需要 3 分钟正如标题所言,希望 2020 年能写满 50 篇周记吧,刚好前两周没有发,所以希望接下来每周完成一篇。周记的内容主要是这几方面的内容:工作学习阅读&…

python版代码整洁之道

总第 113 篇文章,本文大约 8000 字,阅读大约需要 20 分钟原文:https://github.com/zedr/clean-code-pythonpython 版的代码整洁之道。目录如下所示:介绍变量函数1. 介绍软件工程的原则,来自 Robert C. Martins 的书--《…

MVC 3.0错误 HTTP 404您正在查找的资源(或者它的一个依赖项)可能已被移除,或其名称已更改,或暂时不可用。请检查以下 URL 并确保其拼写正确。...

MVC3.0框架开发项目: 有时在程序运行的时候会出现“HTTP 404。您正在查找的资源(或者它的一个依赖项)可能已被移除,或其名称已更改,或暂时不可用。请检查以下 URL 并确保其拼写正确。”的错误提示。 在这里我们以运行时打开登录页面&#xff…

2020年1月总结

总第 114 篇文章,本文大约 1300 字,阅读大约需要 4 分钟这是 2020 年的第一篇月总结,总结的内容和周记差不多,也还是从这几个方面进行总结:工作学习阅读&写作2月计划工作这个月的工作时间大概是2周多一点&#xff…

python技巧(1)--如何转换itertools.chain对象为数组

总第 115 篇文章,本文大约 900 字,阅读大约需要 3 分钟之前做1月总结的时候说过希望每天或者每2天开始的更新一些学习笔记,这是开始的第一篇。这篇介绍的是如何把一个 itertools.chain 对象转换为一个数组。参考 stackoverflow 上的一个回答&…

python技巧(2)--碾平列表和列表去重

总第 116 篇文章,本文大约 1000 字,阅读大约需要 3 分钟今天介绍和列表相关的两个小技巧:碾平列表(flatten list),也就是列表里的元素也带有列表的情况;列表去重,保留原始顺序和不保…

原来电脑并不需要重装系统才能恢复出厂设置,这个操作学起来!

前言 小伙伴们应该都知道手机上有恢复出厂设置的功能,如果想要把手机送给朋友或者卖给别人,就会先恢复出厂设置。 但换到Windows电脑上之后,如果出现同样的情况,就会第一时间想到重装系统。就好像Windows电脑上不存在恢复出厂设…

2020年周记(2/50)

总第 117 篇文章,本文大约 1400 字,阅读大约需要 5 分钟因为春节假期的延长,中间还是休息过长,少了两周的周记了,这是2020年的第二篇周记,内容还是这几个方面:工作学习阅读&写作其他下周计划…