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

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,一经查实,立即删除!

相关文章

iOS开发Objective-C基础之──多态

Objective-C语言是面向对象的高级编程语言,因此,它具有面向对象编程所具有的一些特性,即:封装性、继承性和多态性。 今天介绍一下Objective-C中的多态性。 一、什么是多态 多态:不同对象以自己的方式响应相同的消息的能…

react学习(7)----react转换值同render

{title: 状态,dataIndex: status,render: (text, row) > {let arr [, 未开始, 进行中, 已结束, 已作废];return <span>{arr[text]}</span>;},},

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

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

删除Autorun.inf的方法

你的电脑的每个分区根目录都有一个autorun.inf的文件夹&#xff0c;查看属性是只读隐藏&#xff0c;且无法删除、无法取得权限!点进去&#xff0c;却显示的是控制面板的内容? 其实这个不是病毒&#xff0c;而是用来防病毒&#xff0c;一些系统封装工具本身就自带。下面教你删…

react学习(8)----数组方法fliter简介

filter() 方法创建一个新的数组&#xff0c;新数组中的元素是通过检查指定数组中符合条件的所有元素。 注意&#xff1a; filter() 不会对空数组进行检测。 注意&#xff1a; filter() 不会改变原始数组。

几个有趣的python技巧

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

react学习(9)----react生命周期

react生命周期1.1.constructor() constructor()中完成了React数据的初始化&#xff0c;它接受两个参数 &#xff1a;props和context&#xff0c;当想在函数内部使用这两个参数时 &#xff0c;需使用super()传入这两个参数。 注意&#xff1a;只要使用了constructor()就必须写su…

TOP 命令

转自&#xff1a;top命令.http://www.cnblogs.com/wangkangluo1/archive/2012/04/18/2454993.html#stat.2013-05-25 top命令是Linux下常用的性能分析工具&#xff0c;能够实时显示系统中各个进程的资源占用状况&#xff0c;类似于Windows的任务管理器。下面详细介绍它的使用方法…

20191215周学习总结

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

玩转12306之系统登录

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

react学习(10)----react数组定义 从0开始 直接加个0下标空

{title: 状态,dataIndex: status,render: (text, row) > {let arr [, 未开始, 进行中, 已结束, 已作废];return <span>{arr[text]}</span>;},},

Nginx快速搭建和基本使用

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

对于mysql的用户权限管理

转载链接&#xff1a;http://www.cnblogs.com/ymy124/archive/2012/05/23/2514196.html 1.新建用户 //登录MYSQL>mysql -u root -p>密码//创建用户mysql> insert into mysql.user(Host,User,Password) values(‘localhost’,jeecn’,password(‘jeecn’));//刷新系统权…

第二期周总结

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

react学习(12)-moment插件设置日期格式

<p>{moment(boothDetails.startTime).format(YYYY-MM-DD)}到{moment(boothDetails.endTime).format(YYYY-MM-DD)}</p>

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

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

oracle分区

oracle 分区技术&#xff1a;使用分区技术&#xff0c;Oracle允许把一个大表分成几个部分&#xff0c;每部分叫一个分区&#xff0c;然后把每个部分放在不同的物理磁盘&#xff0c;以提高整个数据库的性能。分区技术的优点&#xff1a;1.分区技术使数据库的可管理性变得更加容易…

react学习(13)-moment中 isRangePicker 控制类型

<Col span{8} key{index}><Form.Item label{item.label} {...formItemLayout}>{getFieldDecorator(${item.paramsName}, {})(item.isRangePicker ? (<RangePickershowTime{item.showTime ? item.showTime : false}style{{ width: 100% }}allowClear/>) : (…

Linux 定时执行shell 脚本

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

react学习(14)-getTime是对时间进行处理的方法

{name: data?.name,startTimeLong: new Date(data?.code[0]).getTime(),endTimeLong: new Date(data?.code[1]).getTime(),},() > {this.getFrameList();}