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() 函数并执行相加时的情况。

栈在表达式求值中的应用

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

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

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

栈在括号匹配中的应用

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

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

  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:

总结

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

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

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

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

  1. 漫画:如何用栈实现队列

    转载自  漫画:如何用栈实现队列 栈的特点是先入后出,出入元素都是在同一端(栈顶): 入栈: 出栈: 队列的特点是先入先出,出入元素是在不同的两端(队头和队尾): 入队: 出队: 既然我们拥有两个栈, ...

  2. java中怎么创建栈_这个题如何用栈解呢?

    每日温度 今天又给大家挑了一道十分经典的题目,也是一道面试常考题目,所以大家记得打卡啊,我们先来看一下题目描述,题目很容易理解,而且用暴力法也很容易实现,因为这个题目出现了我们的栈的模块,大家能不能用 ...

  3. 怎么用python启动谷歌浏览器_如何用python控制浏览器

    selenium从2.0开始集成了webdriver的API,提供了更简单,更简洁的编程接口.selenium webdriver的目标是提供一个设计良好的面向对象的API,提供了更好的支持进行web ...

  4. 栈与队列的对决:如何用栈实现队列?

    本篇博客会讲解力扣"232. 用栈实现队列"的解题思路,这是题目链接. 先来审题: 以下是输出示例: 以下是提示和进阶: 栈是一种后进先出的数据结构,而队列是一种先进先出的数据结构 ...

  5. php h5支付没有返回app中,很多人都不知道的监听微信、支付宝等移动app及浏览器的返回、后退、上一页按钮的事件方法...

    在实际的应用中,我们常常需要实现在移动app和浏览器中点击返回.后退.上一页等按钮实现自己的关闭页面.调整到指定页面或执行一些其它操作的 需求,那在代码中怎样监听当点击微信.支付宝.百度糯米.百度钱包 ...

  6. php 微信监听返回键,JavaScript_JS监听微信、支付宝等移动app及浏览器的返回、后退、上一页按钮的事件方法,在实际的应用中,我们常常需 - phpStudy...

    JS监听微信.支付宝等移动app及浏览器的返回.后退.上一页按钮的事件方法 在实际的应用中,我们常常需要实现在移动app和浏览器中点击返回.后退.上一页等按钮实现自己的关闭页面.调整到指定页面或执行一 ...

  7. selenium操作浏览器的前进和后退

    前进关键字:driver.forward() 后退关键字:driver.back() 测试对象:1.https://www.baidu.com/ 2.https://www.sogou.com/ 实例 ...

  8. 08 | 栈:如何实现浏览器的前进和后退功能?

    栈 后进者先出,先进者后出,这就是典型的"栈"结构.栈是一种"操作受限"的线性表,只允许在一端插入和删除数据. 为什么要使用到"栈"这种操作 ...

  9. 区块链浏览器_如何用区块链浏览器实现链上数据追踪?

    "支付宝转个账还能有交易回执,京东白条被盗刷了还能去找平台,交易没有任何国家信用背书的虚拟货币的用户,是如何确认自己完成了交易的?在没有第三方平台的情况下,如何实现链上数据追踪?" ...

最新文章

  1. 局域网网速带宽测试软件,网管的经验 教你如何测试局域网的网速
  2. java 正则匹配 sql星号,正则表达式匹配星号和换行符之间的字符串
  3. htcvr设备计算机配置,VR对电脑配置要求高吗?HTC Vive电脑配置要求
  4. Linux 的 Out-of-Memory (OOM) Killer
  5. angularjs post返回html_Python 爬虫网页解析工具lxml.html(二)
  6. 如何简化卷积神经网络_卷积神经网络:简化
  7. 本地与服务器文件同步软件哪个好,同步软件哪个好,亲身体验的3款免费同步软件介绍...
  8. 全新微头像V2.1.8版全套iApp源码分享
  9. python长沙_python 长沙
  10. mysql mpm_mysql mpm
  11. 爆火的羊了个羊背后暗含的广告变现逻辑是什么?
  12. [App Bundle]Android动态化技术实例
  13. mysqlclient和pymysql如何选择?_gevent_waiter的使用
  14. Golden Software BLN文件格式
  15. 【科技视野】微信小程序真的是App的终结者?
  16. Lenovo R7000 win11无线网卡无法连接5Gwifi
  17. Java Web之JSP技术
  18. I am a lazy bone
  19. 王者荣耀服务器能不能注销,王者荣耀游戏账号能永久注销吗 永久删除后还能恢复吗...
  20. 笔记本二合一计算机,【二合一笔记本电脑】二合一笔记本电脑缺陷_二合一笔记本电脑测评...

热门文章

  1. 如何在mac系统下搭建git服务器
  2. 定制化WordPress后台的6个技巧
  3. TP5: 日志记录改造——4
  4. jsoncpp之初体验
  5. 动态样式计算 动态样计算 <span :style=“{‘left‘:`${(l+1)*16 - 6}`+‘px‘}“></span>
  6. React开发(117):ant design 新方式
  7. 前端学习(3276):js中this的使用
  8. 前端学习(2964):element-ui的制作
  9. [vue-cli] vue-cli中你经常的加载器有哪些?
  10. 前端学习(2835):小程序中使用less