数据结构+算法(第02篇):玩扫雷就是优化算法

news2024/11/26 8:58:11

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析

阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

引言

上篇文章介绍了算法的本质和基本概念《算法+数据结构(第01篇)走下神坛吧!算法》,这次我们用实际的问题来做算法实战。

假设如下场景:

公司新年晚会进行夺宝游戏,奖品是最新款的智能手机、VR游戏机、便携电脑三件套。

游戏规则如下:

当主持人宣布游戏开始的时候,每位员工的手机上会同时收到两组数字(数组中的每个数字都是正整数且两两不等)和一个目标正整数。

员工需要在两组数字中分别取两个数字相加,使得相加的结果与目标正整数最接近。哪位员工先做出结果,那么奖品就归谁。

为了使赢率最高,请问应该采用什么样的策略或者方法?

显然,这是在对一个特定问题找方法。那么根据上篇文章所讲到的,这就是在求算法。

那么如何算法求解呢?

答案就在上篇文章提到的“朴素而广泛的方法论”中。这个方法论其实就是算法求解的套路。

套路第一步:重新定义问题,结构化描述

原问题是生活场景,要转换成结构化问题描述。结构化描述分为如下两步:数据与规则抽取、数据结构选择与转化。

数据与规则抽取

数据的来源: 数据一般在原问题描述中以名词、量词形式出现

数据的摘取:并不是所有的名词和量词都是有效数据。很明显,只有和问题求解相关的名词和量词才有意义。“问题求解”是动作,与动词相关。

那么是不是所有的动词都有效呢?也不是。只有和规则相关的动词才是有效的。

规则的发掘:规则就是抵达结果的条件。

根据上面的定义, 不难看出

数据是:两组数字(数组中的每个数字都是正整数且两两不等)、一个目标整数

规则是:从两组数字中分别取两个数字相加,相加的结果必须与目标正整数最接近

数据结构选择与转化

上篇文章已经讲到了:算法的依托是数据结构。如果把算法看做设计域的话,那么数据结构就是连接问题域到设计域的桥梁。那么如何选取合适的数据结构呢?

答案是:对上一步摘取的数据进行类型联想、关联。

上一步中,我们已经摘取了数据——两组数和一个正整数。很明显,这里涉及到两个类型:数组和整数。

而这两个属于基础数据结构类型,至此数据结构选择问题解决了。接下来就是要对摘取的数据,基于选择的数据结构进行转化——“重整化”:

两组数字(数组中的每个数字都是正整数且两两不等)=> int A[]; int B[];

目标正整数 => int c;

聪明的你,一定会问一个问题:数据结构的选择仅仅就在这一步决定吗?

答案是否定的。数据结构的选择会贯穿整个算法设计,是一个不断迭代的过程。后面部分会详细阐述。

套路第二步:问题归类

算法问题的基本类型:搜索、排序、规划、计算。回到当前问题,根据问题描述,显然属于搜索类型。

套路第三步:经验匹配

现在我们来翻看已有的搜索算法,看看有没有能与当前问题匹配的。

理论上有3种情况:

第1种情况,100% 匹配,此时“直接拿来主义”;

第2种情况,部分匹配,此时可在已有算法基础上进行调整、组合或者改良;

第3种情况,完全不匹配,此时需要我们根据已有知识(甚至是跨学科知识,比方说数学、生物等),创新性地开发新算法。

针对搜索问题,我们有一个万能算法——“暴力搜索”,即遍历每一种可能性,直到找到答案。

但是这个算法要穷尽所有可能性,所以带来的时间和空间开销通常都是巨大的,用上篇文章的术语来讲,就是计算复杂度贼高。

为了给大家一个量化感觉,先用“暴力搜索”算法来解答这个题。

暴力搜索算法

对于数组A中的每一个元素进行遍历:

设当前元素为A[i],则:

遍历数组b中的每一个元素B[j]

(i)计算A[i]+B[j]的值,将所求的值记为t;

(ii) 计算t-c的绝对值|t-c|,记为k;

(iii) 如果当前的k比历史的k小(k的初值可以设成一个极大值)。

那么: 将 {A[i], B[j]}取代之前的候选结果,作为新的候选结果,待所有的遍历结束,最终的候选结果就是所要求的解。

上面的算法有两重循环,所以暴力搜索时间复杂度为O(La x Lb)

其中La表示数组a中元素的个数,Lb表示数组b中元素的个数。

随着La和Lb的增大,复杂度以两者乘积速度上升。那么如何对暴力算法进行优化呢?

关于复杂度的计算,我会在下篇文章中详细介绍。

套路第四步:算法优化三步走

步骤1:

找到算法性能瓶颈源头,稍微分析一下,就明白:上述暴力搜索算法的开销在于穷尽了所有元素。

步骤2:

对源头进行改造,那么是否可以避免穷尽所有元素而得到结果呢?换言之,是否可以只比较部分元素、其他元素就自然被排除了呢?

要得到这样的效果,显然我们需要一种性质——这种性质必须是容易获得的:要么可以直接从当前数据中获取,要么可以通过已有方法(算法)获取。

最容易想到的就是有序性,这种性质可以通过排序算法获取。我们可以用快速排序算法对A数组和B组数进行排序,将排序后的元素按照下图放置:

(为了方便表示,我们假设A数组是10个元素,B数组是12个元素)

上图中的每个方格就是用来存放相加结果的。很显然,暴力搜索就是对上图中的每个方格都做了计算。

现在我们要做的,就是利用有序性,避开尽可能多的方格。

我们从右上角方格[A10, B1]开始遍历:

s[A10, B1] = A10 + B1,则:

(i) 如果s[A10, B1] == 目标正整数c,那么元素对{A10, B1}即为所求解

(ii) 如果s[A10, B1] < 目标正整数c, 那么所有与[A10,B1]在同一排的方格都不用计算了

原因如下:因为A1<=A2<=...<=A9<=A10,所以s[A1, B1] <= s[A2, B1] <= ... <= s[A10, B1],从而这些s距离c都比s[10, B1]远,都不是所求解。

(iii) 类似地,如果s[A10, B1] >目标正整数c,那么所有与A[10, B1]在同一列的方格都不用计算了,显然,按照对角线方向来遍历,每遍历一个方格,就可以避开一排或者一列的方格,感觉就像在玩扫雷游戏:)

步骤3:验证

现在我们来验证一下优化后的算法的复杂度,整个算法分成两部分:

第1部分是快速排序。快速排序算法的时间复杂度是O(nlogn),所以这部分的时间复杂度是 O(MAX(LalogLa, LblogLb))

第2部分是扫雷遍历。这部分最坏的情况就是走完整个对角线。此时共遍历La+Lb个方格,时间复杂度是O(La+Lb)

两者相加得到最坏情况下的整体时间复杂度为:O(MAX(LalogLa, LblogLb)+La+Lb)

好啦,就写到这里了,后续 连载 会持续更新…

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

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

相关文章

网安渗透攻击作业(2)

sql注入第一关 一、部署 1、环境安装 &#xff08;1&#xff09;下载phpstudy&#xff0c;下载链接&#xff1a;小皮面板(phpstudy) - 让天下没有难配的服务器环境&#xff01; &#xff0c;安装过后打开软件进入如下界面&#xff0c;接着我们开启nginx和mysql 注意&#x…

Linux--Shell基础

学习笔记&#xff0c;记录以下课程中关于Linux的Shell基础知识。 黑马程序员linux入门到精通&#xff08;下部分&#xff09;_哔哩哔哩_bilibili 目录 1.编写规范 2.变量 2.1 变量的含义 2.2 变量的定义和使用 2.3 只读变量&#xff08;了解&#xff09; 2.4 接收用户输入…

Linux 命令 —— top

Linux 命令 —— top 相对于 ps 是选取一个时间点的进程状态&#xff0c;top 则可以持续检测进程运行的状态。使用方式如下&#xff1a; 用法&#xff1a; top [-d secs] | [-p pid] 选项与参数&#xff1a; -d secs&#xff1a;整个进程界面更新 secs 秒。默认是 5 5 5 秒。…

遇到ubuntu设置交叉编译环境的问题

今天交叉编译器一直没安装成功&#xff0c;环境变量也配置了还是不对&#xff0c;最后发现Ubuntu是64位的要装 然后就好了 另外在进行嵌入式Linux开发的时候&#xff0c;要把主机、虚拟机、以及开发板设置在同一网段下&#xff0c;虚拟机一般设成临时的就可以&#xff0c;但是…

力扣之2648.生成 斐波那契数列(yield)

/*** return {Generator<number>}*/ var fibGenerator function*() {let a 0,b 1;yield 0; // 返回 0&#xff0c;并暂停执行yield 1; // 返回 1&#xff0c;并暂停执行while(true) {yield a b; // 返回 a b&#xff0c;并暂停执行[a, b] [b, a b]; // 更新 a 和 …

使用Pycharm在本地调用chatgpt的接口

目录 1.安装环境 2.建立多轮对话的完整代码&#xff08;根据自己使用的不同代理需要修改端口&#xff08;port&#xff09;&#xff09; 3.修改代码在自己的Pycharm上访问chagpt的api并实现多轮对话&#xff0c;如果不修改是无法成功运行的。需要确定秘钥和端口以保证正常访…

使用pygame建立一个简单的使用键盘方向键移动的方块小游戏

import pygame import sys# 初始化pygame pygame.init()# 设置窗口大小 screen_size (640, 480) # 创建窗口 screen pygame.display.set_mode(screen_size) # 设置窗口标题 pygame.display.set_caption("使用键盘方向键移动的方块的简单小游戏")# 设置颜色 bg_colo…

CHS_08.2.3.6_1+生产者-消费者问题

CHS_08.2.3.6_1生产者-消费者问题 问题描述问题分析思考&#xff1a;能否改变相邻P、V操作的顺序&#xff1f;知识回顾 在这个小节中 我们会学习一个经典的进程同步互斥的问题 问题描述 并且尝试用上个小节学习的p v操作 也就是信号量机制来解决这个生产者消费者问题 问题的描…

基于GAN-CNN-CNN的鲁棒笔迹识别方法(三)

上一篇文章提出了一个用于笔迹识别(鉴别)的三段式模型,同时也提出了一个新数据集HTID_1,本文主要针对模型的最后一部分--笔迹识别,在HTID_1上进行实验. 数据集 实验前先介绍一下HTID_1. HTID_1是用于笔迹识别的数据集,是基于本文提出的模型制作而成的.将互联网上收集的740人笔…

Unity Shader 滚动进度条效果

Unity Shader 滚动进度条效果 前言项目场景布置导入图片修改场景设置修改图片尺寸即可调整进度 ASE连线 前言 UI要实现一个滚动进度&#xff0c;于是使用Shader制作一个。 项目 场景布置 导入图片 修改一下导入图片的格式&#xff0c;这样才能循环起来 WrapMode改为Repea…

2024/2/1学习记录

echarts 为柱条添加背景色&#xff1a; 若想设置折线图的点的样式&#xff0c;设置 series.itemStyle 指定填充颜色就好了&#xff0c;设置线的样式设置 lineStyle 就好了。 在折线图中倘若要设置空数据&#xff0c;用 - 表示即可&#xff0c;这对于其他系列的数据也是 适用的…

Windows11 用 HyperV 安装 Ubuntu-16.04 虚拟机

Windows11 用 HyperV 安装 Ubuntu-16.04 虚拟机 1. 确保已经开启HyperV2. 准备Ubuntu16.04镜像&#xff08;推荐64位的&#xff09;3. HyperV ->快速创建 -> 更改安装源 选刚刚下载的镜像&#xff08;.iso&#xff09;文件就好 -> 创建虚拟机[^1] 前提&#xff1a;VMw…

(硬核中的硬核)链路追踪落地过程中的挑战与解决方案

&#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是蓝胖子&#x1f947; ☁️博客首页&#xff1a;CSDN主页蓝胖子的编程梦 &#x1f304;每日一句&#xff1a;编程中我们会遇到多少挫折&#xff1f;表放弃&#xff0c;沙漠尽头必是绿洲。 大家好&#xff0c;我是蓝胖子…

【Qt加密播放器】登录窗口功能补充

输入框小设计 目的&#xff1a;实现鼠标点击输入框时的聚焦效果。 首先在LoginForm构造函数中为账号和密码输入框添加事件过滤器。关于事件过滤器的具体介绍可以参考这篇博文&#xff1a;Qt消息机制和事件 ui->nameEdit->installEventFilter(this); ui->pwdEdit->…

uniapp+微信小程序+nodejs问题记录

一、前言 通过uniapp进行微信小程序调试。服务端使用NodeJs。 二、报错统计 1、本地调试时微信小程序报错&#xff1a;request:Cannot send network request to localhost 解决方法&#xff1a; 【微信小程序开发平台】-【本地设置】-勾选“不校验合法域名、web-view、TLS版本…

Python完善APC netbotz 250报告功能实现主动式运维。

首先介绍一下APC netbotz 250, 这是施耐德推出的一款机架式监控主机&#xff0c;能够对所有IT环境进行经济有效而且灵活的监控&#xff0c;号称APC史上性价比最高的环境监测方案&#xff0c;这可不是我吹的&#xff0c;是APC官网的介绍&#xff0c;可参考下面的官网截图。 我们…

Visual Studio 2022编译错误 Error MSB8041-此项目需要MFC库解决方案

Visual Studio 2022编译错误 Error MSB8041-此项目需要MFC库 错误原因&#xff1a; Visual Studio 2022安装的MFC库不够。 解决方案&#xff1a; 安装Visual Studio 2022 需要的MFC 库就可以了。 安装方法&#xff1a; 第一步&#xff0c;打开vs2022 工具 ->获取工具和功…

数据可视化Tableau

目录 一.第一次实验课内容 1、熟悉Tableau Desktop的工作环境。 2、熟悉数据导入、维度和度量的区分以及不同数据字段类型的标识符。 3、熟悉工作表的基本操作&#xff0c;主要包括行列功能区&#xff0c;标记卡&#xff0c;筛选器&#xff0c;智能推荐的使用。 4、作业--…

两个链表的第一个重合节点

法一&#xff1a;蛮力法 即顺序遍历第一个链表的每个节点&#xff0c;每遍历到一个节点&#xff0c;就在第二个链表中顺序遍历每个节点&#xff0c;比较是否有节点相同。该方法的时间复杂度是O&#xff08;mn&#xff09;&#xff0c;空间复杂度是O&#xff08;1&#xff09;。…

Java进击框架:Spring-综合(十)

Java进击框架&#xff1a;Spring-综合&#xff08;十&#xff09; 前言Rest ClientsWebClientRestTemplateHTTP接口 JMS (Java消息服务)使用Spring JMS发送消息接收消息注释驱动的侦听器端点 JMXEmail任务执行和调度Spring TaskExecutor 抽象Spring TaskScheduler 抽象支持调度…