NKS蜕变Ω
NKS蜕变
智商压制专修学院学生
关注数: 198
粉丝数: 20
发帖数: 5,341
关注贴吧数: 22
《魔域》开发商网龙子公司普罗米休斯收购Explain Everything 《魔域》开发商网龙近日宣布其全球领先的课堂技术子公司普罗米休斯,已基本完成对Explain Everything公司全部资产的收购。 Explain Everything是一个全球领先的数字白板平台,旨在创建有吸引力的课程、活动以及互动演示。通过此次战略收购,普罗米休斯将在产品组合中增加一个可带来收入的应用工具。此次收购也有利于普罗米休斯凭借创新技术开发更新、更好的解决方案,以完善教学、学习和协作需求,最终促进未来软件订阅收入的规模化增长。据此前网龙发布的财报显示,2022年上半年,网龙的教育业务实现营收24.1亿元,同比飙升71.2%,占收入比重达到了56.83%,实现了游戏与教育的双轮驱动。
魔域开发商网龙连续十年上榜互联网百强企业 近年来,网龙一直致力于推动国内网络游戏行业的发展,凭借网络游戏的研发核心技术、敏锐的市场洞察力和广阔的国际视野,成为中国民族网络游戏的领跑者和海外市场拓展的先行者,堪称是福建企业之光。
网龙《魔域》年度资料片“觉醒:再造不凡”现已重磅公测,所有职 除了全职业觉醒与竞技场升级,全新资料片也将带来85级神火与神火副本罪孽神墓、灰烬古堡、寂灭之都,更有全新养成系统神火曜环系统,玩家还可以通过在神火大世界里完成探索任务来揭开神火背后的未解之谜。除此之外,海量日常任务、职业一键觉醒等新体验也值得玩家感受玩味。
九大职业燃战觉醒,《魔域》勇士再造不凡!网龙《魔域》年度新资料片“觉醒:再造不凡”今日震撼公测,九大职业迎来潜能爆发,亚特大陆勇者畅享免费觉醒。与此同时,全新“狩神之地”竞技场的全新开放,更是带来充满变数的战斗乐趣。而神火也将迎来全新升级,玩家可以在新神火大世界里探索更多变强的奥秘。
华纳qyw2255 你爱自己吗?你会因为自己太矮,太胖或容貌而不喜欢自己吗? 我相信太多的人都会不满意自己的某一些缺点。可是,谁曾想过在你心里对自己不满时,而有一些人却在羡慕你拥有而她没有的一些东西。由此可见,每一个人都有优点。 所以,为什么我们要整天盯着自己的缺点。而忘却我们美好的一面。 为什么我们不多看美好的一面,让缺点变成一个可爱的小蓝点呢? 一生说短也短,说长也长,我们为什么不能好好爱自己呢? 你要知道,爱自己是你一生幸福的关键。 所以,去爱你自己吧!这样,你才能发现世界原来如此美好!爱自己,不光要爱自己的优点,同样请你也一定要爱你的缺点,因为它也是你的一部分。在你接纳它时,它也会变得可爱! 当你能对自己说,我爱你时,你会快乐! 当然,我也曾和大多数人一样,太在意自己不完美的一面,所以我觉得不开心,很累啊!直到后来,我开始接纳自己不完美的一面,爱我自己时,我觉得一切都那么美好1 大千世界,存在就是美! 如果每个人都一样完美,那该多么单调啊!而万紫千红,千娇百媚也将不复存在。 当你心里对自己有太多的不满意时,也许让你一下爱自己有点困难。这时,请你拿出镜子,认真的看镜子在中的自己,每天对自己说,我爱你,我接纳你的一切缺点。我相信你会慢慢爱你自己! 去爱自己,去发现美好!去过自己想要的生活!在你一生去经历你想要的! 这样,才能不负此生! 愿每个人都学会爱他自己!爱这个世界!
最强最全面的大数据 SQL 系列 本套 SQL 题的答案是由许多小伙伴共同贡献的,1+1 的力量是远远大于 2 的,有不少题目都采用了非常巧妙的解法,也有不少题目有多种解法。本套大数据 SQL 题不仅题目丰富多样,答案更是精彩绝伦! 注:以下参考答案都经过简单数据场景进行测试通过,但并未测试其他复杂情况。本文档的 SQL 主要使用 Hive SQL。 因内容较多,带目录的 PDF 查看是比较方便的: 最强最全面的大数据SQL经典面试题完整PDF版 一、行列转换 描述:表中记录了各年份各部门的平均绩效考核成绩。 表名:t1 表结构: a -- 年份b -- 部门c -- 绩效得分 复制代码 表内容: a b c2014 B 92015 A 82014 A 102015 B 7 复制代码问题一:多行转多列 问题描述:将上述表内容转为如下输出结果所示: a col_A col_B2014 10 92015 8 7 复制代码 参考答案: select a, max(case when b="A" then c end) col_A, max(case when b="B" then c end) col_Bfrom t1group by a; 复制代码问题二:如何将结果转成源表?(多列转多行) 问题描述:将问题一的结果转成源表,问题一结果表名为t1_2。 参考答案: select a, b, cfrom ( select a,"A" as b,col_a as c from t1_2 union all select a,"B" as b,col_b as c from t1_2 )tmp; 复制代码问题三:同一部门会有多个绩效,求多行转多列结果 问题描述:2014 年公司组织架构调整,导致部门出现多个绩效,业务及人员不同,无法合并算绩效,源表内容如下: 2014 B 92015 A 82014 A 102015 B 72014 B 6 复制代码 输出结果如下所示: a col_A col_B2014 10 6,92015 8 7 复制代码 参考答案: select a, max(case when b="A" then c end) col_A, max(case when b="B" then c end) col_Bfrom ( select a, b, concat_ws(",",collect_set(cast(c as string))) as c from t1 group by a,b)tmpgroup by a; 复制代码二、排名中取他值 表名:t2 表字段及内容: a b c2014 A 32014 B 12014 C 22015 A 42015 D 3 复制代码问题一:按 a 分组取 b 字段最小时对应的 c 字段 输出结果如下所示: a min_c2014 32015 4 复制代码 参考答案: select a, c as min_cfrom( select a, b, c, row_number() over(partition by a order by b) as rn from t2 )awhere rn = 1; 复制代码问题二:按 a 分组取 b 字段排第二时对应的 c 字段 输出结果如下所示: a second_c2014 12015 3 复制代码
架构师训练营第一周学习总结 4+1 视图模型 逻辑视图 开发视图 物理视图 场景视图 软件建模语言:10 种,常用 7 种 领域问题-->概念模型_-->系统需求-->解决方案 UML 可用来描述: 某个领域问题 构思中的软件设计 描述已经完成的软件实现 分类 动态图 静态图 7 种模型: 用例图 对象图 类图 组件图 包图 部署图 协作图 序列图 活动图 状态图 图好不好不重要,重在表达清楚 通用模型元素: 类 对象 状态 接口 元素间的关系: 组合 依赖 继承 实现 关联 集合 用例建模: 角色 用例 用例可以自顶向下不断精化,抽象出不同层次的用例图 知道什么时候该画什么图比会画图更重要 时序图三个阶段都可以使用 把泳道的概念建立起来 组件图都是今天关系,用组件时序图画 架构师能把握工作的过程,画的第一张图是部署图 需求分析阶段画图: 用例图 活动图 状态图 概要设计画图: 部署图 组件图 时序图 活动图 详细设计画图: 类图 方法的活动图 重要的是脑子里有没有设计,没有深度会很累,任何事情要沉下来。 是什么奠定了架构师的职场定位: 能把知识突破的点在哪里 积累的过程很痛苦很漫长 公司为什么让我们写代码,代码对公司没用,但是不得不用。 项目能否成功,非代码能决定。 知识不重要,重在掌握方法,思想。 要有悟性,为什么这样做而不是那样做 知识体系要串起来,能够领悟到为什么做,如何做。 不会讲任何技术应用,学解决思路 练一套思维方式,思路背后的关系 奠定架构师定位的是让别人依赖你写的代码 课外: 架构思维的本源比架构规则重要 架构设计,设计为先架构为魂,用架构的系统化和全局性思维来做设计 架构师的能力: 理需求的能力 读代码的能力 抽象系统的能力 心性修炼: 同理心的修炼,认同他人的能力 全局观的修炼,保持好奇心和学习的韧性 迭代能力的修炼,学会反思,学会在自我否定中不断成 名人名言: 徐式伟:掌控全局并不是无所不能,不是成为全栈,怎么做到掌控全局?和新在于对只是脉络的体系化梳理这是架构能力构建和全年提升的关键 机构是技能图谱:
【百度官方技术分享】百度智能小程序框架性能优化实践 导读:今天给大家讲的题目是《百度开源小程序框架架构演进和性能优化实践》。本次分享包含两部分,第一部分是百度智能小程序整体的框架及演进,主要讲百度小程序开发全流程概况、百度智能小程序框架,以及百度小程序多宿主运行保障;第二部分是百度小程序框架的性能优化,主要讲整个小程序的启动过程,以及从开发者角度,有哪些重要的优化点。 一、百度智能程序整体框架及演进 整个移动互联网一直是在 NA 和 H5 之间寻找权衡,NA 的性能好、能力强;H5 灵活性更高。我认为渲染分为两派,一派就是 NA 渲染派,一派叫做 H5 渲染派。NA 渲染派,比较有代表性的如 RN 、 Flutter ;Web 渲染派,比如百度的轻应用,以及之后做的小程序。 1. 开发全流程概览百度曾经做过的 Web 渲染派的三个代表产品,分别是轻应用、直达号和小程序。 轻应用,是 H5 + 端能力。它是一个标准的 H5,增加了一些 NA 的 API,比如定位等。 直达号,在技术层面跟轻应用是一样的。 小程序,本质上是一个受限的 H5 + 大量丰富的 API + UI 组件。现在我们给小程序提供的 API 有 300 多个,组件有 30 多个,组件是有界面的。比如,视频、地图 。 为什么小程序要受限,主要有两个原因: 保持体验的一致性。H5 太过灵活,JS 随时可以去改变界面。 安全考虑。因为我们提供了大量 API 和组件,且这些都是很底层的一些能力,比如电话号码、账号,肯定不能轻易开放给大家。 怎么受限,主要有两点: 编写语言,不再是直接写 HTML ,而是用自定义语言 swan 来编写 。 runtime 层有两个栈,一个是渲染栈,一个是 JS 执行栈,这两个栈从物理上隔离,以保障安全性。 2. 智能小程序框架 (1)开发运行全流程先简单介绍一下整个百度智能小程序的开发流程。 首先开发者用 swan 写布局; 接着通过开发者工具打包,上传到我们的小程序 B 端服务器; 然后是小程序的审核流程,有机审、人审; 最后是用户点击小程序时,客户端请求小程序 C 端服务器,C 端服务器再从 B 端服务器获取小程序包。整个过程都是加密传输,可以保证代码的安全。 (2)百度智能小程序框架 -SWAN上图是一个百度智能小程序的框架,我们内部命名为 SWAN。 分层结构如下: 最上层是开发者基础库,命名为 swan-js ,开发者直接和这层打交道。swan-js 负责两件事情:即 swan 代码转为 HTML,变成 WebView 可运行程序;客户端端能力的封装暴露。 再下一层是 swan-native。这里面最核心的是 API 和组件的 NA 实现。其中双栈管理也在这一层,另外标红的 Extension 用于开发者宿主自身能力扩展使用,比如,贴吧宿主期望增加个发帖能力,就可以通过此机制。 再下面这层叫 Porting Layer。这层是百度小程序为了实现开源,增加的一层与宿主的接口层。最下面这一层是宿主基础能力层。如果宿主没有这些能力,可以参考百度开源的参考实现,可直接集成到宿主使用。 3. 核心结构 (1)前端角度我们从前端的视角来看看双栈结构。一个宿主客户端可以运行多个小程序,并且在一段时间内保持存活状态,每个小程序都有一个 master 执行框架 JS 和小程序开发者的 JS,一个 master 对应多个 slave(slave 代表一个用户可见的界面)。 (2)客户端角度从客户端角度来看双栈结构,如上图所示,master 负责执行 JS,可以有两种实现方式,WebView 或 JS 引擎(V8/jscore),JS 引擎的效率更高;slave 的展现有 WebView,为了加快 WebView 的创建速度,设置 cache;master 和 slave 的通信通过消息总线。 master 不支持 BOM、DOM 和 WEB-API,小程序只能调用对外开放的能力。 (3)小程序 NA 组件与界面关系从体验上看,小程序的体验要好于 H5,其中有一点就是小程序会把一些 NA 的能力和 UI 融合到小程序里面去。小程序的主体渲染还是基于 H5 技术,接下来我们讲一下 NA 元素如何融入 UI 界面。 NA 元素与 H5 的关系有两种,贴片关系、同层关系。 贴片关系:NA 元素在 H5 不在同一层,NA 浮在 H5 之上,H5 所有元素都不可以放到 NA 元素之上。因为不在一层,就需要处理滚动联动,当检测到 WebView 滚动 n 个像素, NA 元素也需要滚动 n 个像素。 同层关系:NA 元素在 H5 这一层,H5 的原生可以压在 NA 元素之上。贴片、同层的界面层级树如上。我们举一个视频组件同层的例子。视频组件比较复杂,有 4 层。第 1 层是视频层,即原始视频的图像,第 2 层是弹幕层,第 3 层是用于视频控制的控件层(比如,开始、暂停按键),第 4 层 Slot 层,视频上面漂的 H5 元素将被放到这层。 同层处理机制,先在 H5(开发者写的 swan 会被转为 H5) 上打一个特殊的标记先占位,属性 inline;浏览内核把这个区域的 surface 拿出来给到 NA 层;然后,小程序框架会把这个区域 surface 塞给播放器,让播放器直接在 surface 上面绘制,达到同层。上面的弹幕、控件、 Slot ,都是 swanjs 层 H5 实现 ,Slot 层可以认为是一个容器,例如写一个 video,其所有的子元素都会放在 Slot。 NA 的组件同层的技术方案还不太一样,安卓和 iOS 也是有些区别的。像在 iOS 上,如果有些组件设置 over-flow ,它会天然支持这一套东西,但是安卓就需要浏览器内核来支持。 4. 小程序多宿主运行保障百度智能小程序是开放系统,可以运行在多宿主之上,那如何在多宿主上保证小程序运行体验的一致性呢? 各个宿主集成了我们的小程序框架后,首先要跑 CTS 测试,通过之后才可以拿到小程序列表进行分发。 对于可选能力部分,各个宿主不是所有的能力都需要实现。比如,一些 AI 能力、push 能力。 如果一个小程序用到了可选能力怎么办? 两个办法,一是小程序和宿主之间的双向选择机制,小程序可以选择我要分发到哪些平台,宿主也有权利选择要分发到哪些宿主。二是,小程序做兼容。
架构师技能图谱 架构师的主要职责与能力 职责 编写架构设计文档 开发编程框架 重构软件代码 设计系统架构 技术选型,解决技术应用中的问题 优化系统性能 模块分解与微服务的应用 大数据应用 技术创新 沟通管理 能力 编程能力 基础技术掌握能力 常用技术产品的理解和应用能力 性能优化和分析故障的能力 常用框架模式和框架的理解与应用能力 建模以及设计文档的方法和能力 业务理解以及功能和非功能模块的拆解能力 快速学习能力 沟通和领导能力 要具备这些能力,需要掌握的技术知识点可划分为 9 个方面,包括构架建模方法、软件设计原则、架构模式、软件技术方案和选型、xie.infoq.cn性能优化、微服务、安全与稳定、大数据、技术管理,详细的技术知识点如下图(图片看不清可下载放大):
安全逆向分析实战 本文记录了对某发行版 Linux 中一个安全模块(LSM)的逆向过程,该 LSM 对系统中待运行的程序进行安全校验,数据流穿越内核态与用户态,涉及系统内核及系统服务。此 LSM 对系统安全性的增强效果明显,其设计思路值得防守方研究学习,可于个人终端或服务器安全防护中应用。特此对逆向内容记录,希望能为读者在终端防护方面拓宽思路,同时欢迎感兴趣的师傅们交流学习。 一. LSM 框架简介 Linux 安全模块(Linux Security Module,xie.infoq.cn/ LSM)框架是 Linux 操作系统内核提供的一种安全机制,它通过内核扩展实现 hook 函数以完成多种安全检查,通常用于强制访问控制(Mandatory Access Control)。虽然被称作“模块”,但不同于 LKM,这些扩展并不是可加载的内核模块,而是和内核代码一起编译在内核文件(vmlinuz)中。可以通过如下命令查看本机启用的 LSM,cat /sys/kernel/security/lsm。常见的 LSM 包括 SELinux、Yama 等。 LSM 框架的 hook 点设置于内核访问关键对象前,通过调用 LSM 中实现的 hook 函数,判断是否可以进行访问。如果有多个 LSM,则会根据初始化的顺序依次判断,都允许才能进行访问。上述关键对象包括程序、进程、套接字、文件系统等,可在/usr/src/linux-headers-YOURSYSTEMVERSION/include/linux/lsm_hooks.h 中查看详细的 hook 说明。 这里以程序启动过程为例,简单说明 LSM 工作的机制。当通过 execve 系统调用执行一个新程序时,内核最终会执行到__do_execve_file 函数完成相关工作,在这里会调用 prepare_binprm 函数填充 struct linux_binprm,填充前会调用 security_bprm_set_creds 进行安全检查。security_bprm_set_creds 就是 LSM 框架提供的 hook,它会依次调用注册在这个钩子上的回调函数,完成安全检查。此流程上相关代码以及此钩子的说明如下。LSM 开发时,通过如下函数定义安全模块的 hook 函数,逆向时通过此函数可快速定位具体的 LSM 以及相关回调函数。2021最新整理网络安全/渗透测试/安全学习/100份src技术文档(全套视频、CTF、大厂面经、精品手册、必备工具包、路线)一>获取<一 二. 安全模块逆向分析2.1 分析准备 本次分析的对象为某发行版 Linux,此系统提供了可执行文件的签名校验功能,仅有签名的程序可以被执行,本次逆向的目标就是试图还原校验功能的框架和逻辑。由于此安全检查的存在,提权、后门等程序因为此机制的存在而无法直接运行,从某种程度提高了操作系统的安全性。编译一个 hello world 程序,运行,会提示无法通过系统安全校验目前不能运行。strace 跟一下,看到在 execve 时就被干掉了(如下图),可以肯定是在内核中完成这个动作的,大概率是通过内核扩展实现。下面尝试定位此内核扩展。lsmod 看一眼,没有发现明显相关的。查看此系统的所有 LSM,有一个名为 elfverify 的模块,通过此命名可以推断,就是此模块完成了程序的校验功能。由于 LSM 是被编译在内核中的,因此接下来需要获取到此系统的内核文件。系统的内核文件通常可以在/boot 目录中找到,其中 vmlinuz 的文件是压缩后的内核,可以通过 extract-vmlinux 工具提取未压缩的内核文件 vmlinux 进行分析。然而提取后的文件是没有符号的,符号信息存储在同目录下 System.map 文件中。这里推荐使用vmlinux-to-elf这个工具完成提取与符号修复工作,通过此工具可输出一个带符号的 ELF 文件,方便逆向分析。 2.2 LSM 分析 反编译查看恢复后的内核,在 elfverify 的初始化函数 elfverify_init()中,security_add_hooks()的第二个参数 count 为 5,即此 LSM 一共注册了 5 个 hook 函数;跟踪一下第一个参数 security_hook_list,可得到所有实现的回调函数,总结如下。可以看出此 LSM 还对套接字、设备挂载进行了安全校验,这里专注于程序运行的校验,锁定 hook_bprm_set_creds 进行进一步分析。 跟进 hook_bprm_set_creds,该函数首先对一些参数进行检查,接着判断是否开启了开发者模式(判断依据是某个文件的内容),如果符合条件则放行(返回 0),否则调用 access_verify 进行进一步判断。此函数的参数类型是 struct linux_binprm, 源码中此结构体被标记为__randomize_layout,这是 Linux 内核中的一项防御机制,有此标记的结构体其中的元素将作乱序排列,从而攻击者难以找到偏移具体对应的元素。因此通过静态分析,也暂时无法确定传递给 access_verify 的参数。在 access_verify 中,会将当前进程通过 add_wait_queue 挂起等待,并将 90-pid-UNKNOWN 信息写入某个设备中,这里的 90 应该是 LSM 开发者自定义的一个“魔数”(socket 校验中是 91),而第三段的内容由于结构体的随机化也暂时无法确定(之后分析会知道其实是程序路径)。之后再从此设备中读取信息,根据内容选择是否继续执行,并从队列中移除进程。 LSM 中关于 elf 校验的流程到这个函数基本就结束了,并不包含具体的校验逻辑,只是将待校验的进程信息写入某个设备并等待结果,可见校验应该是在另一处完成。经过跟踪分析,此 LSM 注册了一个杂项设备(在 register_elf_verifier_dev()中),名为 elf_verifier。下一步找到了谁从这个设备中读取信息,大概率就能定位到完成校验的具体代码。 然而,在内核中并没有找到相关的代码,下一步得去用户态的程序找找。 2.3 安全校验逻辑分析 查看一下系统进程,发现一条程序名为*-elf-verify,其 ppid 为 1,看了下是系统服务,推断这就是处理杂项设备中数据的程序了。为验证推断,将此服务停止,运行一个自己编译的程序,待运行的进程“僵死”,推断应该是 LSM 将进程送入等待队列后,没有从杂项设备中读到校验结果,就造成了一直挂起的局面。这也确认了正是这个程序处理设备数据并给与返回结果,校验逻辑应当就在其中。 对此系统服务程序进行分析与调试,在进行 ELF 文件安全校验时,它会循环的从/dev/elf_verifier 这个设备中读取内容,读取到的内容包括 PID 和完整的程序路径,并依据此信息进行校验,其主要检查如下两点: 判断此路径的程序是否在白名单或黑名单中 在 ELF 文件头的特殊节中提取签名(PKCS7),然后进行验证(证书在系统某路径中) 上述的黑白名单位于系统/usr 目录下,仅 root 用户可编辑。而 ELF 文件头中的特殊节在其他普通的 ELF 文件中不会出现,应当是在对 ELF 文件进行签名时加上的,而将签名信息添加到文件头中又不会对程序的正常运行造成影响。如果能够顺利读取到签名信息,则调用 openssl 的相关函数进行校验。校验完成后,将检验结果写入/dev/elf_verifier,通知内核进行后续动作;同时如果校验不通过,会通过 dbus 通知 GUI 弹出提示告知用户。 至此,一次校验完成,整个签名校验功能的脉络基本摸清了。在其中还有一些细节检查,例如文件格式、ELF 文件头等等,就不一一展开介绍了。 三. 总结 该系统的可执行程序签名校验功能,通过安全模块(LSM)elfverify、系统服务联合完成,二者通过杂项设备/dev/elf_verifier 传递数据,完成了用户态和内核态的交互。在内核中,通过实现 LSM 的 security_bprm_set_creds 钩子在程序运行前获取到待运行程序的完整路径,将进程暂时挂起,同时将信息写入设备中;用户态程序从设备中读取到信息后,判断此路径程序是否在黑白名单、程序是否是经过签名的 ELF 程序,并将判断结果写入设备;内核 LSM 根据返回的结果确定是否允许执行。
走完线上 BUG 定位最后一公里 一个小故事 周末 12 点的闹钟在回龙观均价 3000 的出租屋急促的响起,程序员小 A 慵懒的拿过手机,滑开手机通知栏,没有未接电话,点开手机的拦截信箱,没有报警短信,昨晚的发布一定很顺利。小 A 幸福的伸了个懒腰。戴上 3000 块的 BeatsSolo Pro,音乐逐渐响起来,小 A 缓缓的闭上了眼睛,正午的阳光从窗户漫进来,撒在小 A 稀疏的头发上。此时的小 A 正在脑海中勾勒着自己美好的未来。房东说:十年前住在这间屋的小 B,现在已经是某度的 T10 大佬,五年前住在这儿的小 T,现在已经在某条带领 200 人的团队,想到这儿,小 A 的嘴角微微上扬,那我也一定不会太差吧~ 嘀嘀..耳机里传来两声消息提示音,小 A 心里咯噔一声,刺骨的寒意弥漫开来,北京三月的阳光突然就不暖了。小 A 用力的微微睁开双眼,通知栏测试同学小 C 的头像一闪而过。 xx 线上 BUG 紧急修复群 小 C: “@小 A 昨晚上线的代码好像有点有问题,来公司看下?我在公司等你。” 点开群设置,老板的头像赫然在列。 怀着愧疚、徘徊、悔恨、无奈、愤怒的心情,小 A 翻身穿上他在路边买的价值 20 元的人字拖,坐上了前往西二旗的地铁十号线。 不久,西二旗某办公室传来了亘古不变的对话,“这段代码测试过,在我电脑上没问题啊”、"你重启下试试"、“是不是代码没上线”、“是不是谁把我代码冲掉了”、“你们测试数据是不是有问题呀”……于是一个下午过去了、一个晚上过去了、一个周末过去了、一个程序员的青春过去了、一个程序员本就不长的职业生涯过去了。 一个小总结 上面这个虚构的小故事只是想说明一个简单的现象,程序员的很多时间被线上 bug fix 占据。因为线上线下环境不一致、输入输出不一等等原因,很多 bug 定位起来效率低下,耗时巨长,导致很多时候程序员遇到线上 bug 总是头疼不已,不由自主的想要甩锅给外在因素,在确定是自己的问题的时候再排查问题。那么线上问题排查到底难在哪儿?首先来看看我们排查线上问题的一个基本步骤,这个步骤一般是排查大多数线上问题的步骤。 步骤 1:找到能复现问题的输入; 步骤 2:判断该输入能否在日常环境构造, 如果能,调到步骤 5。如果不能,继续步骤 3; 步骤 3:查看线上环境日志,看能否找到异常输入相关的异常日志,辅助排查问题; 步骤 4:初步推断问题原因,尝试修复并加上更多日志输出。然后打包、发布。重复步骤 3 直到定位根因; 步骤 5:日常构造相同输入,单点调试,定位问题; 实际的场景中,因为线上线下环境隔离的问题,线上的输入很多时候难以在日常环境中构造,大多数时候我们都在步骤 2、3、4 中循环,于是时间就在循环中慢慢的流逝了。 上面做这么多步骤其实对于查问题而言就是希望可以知道当某段代码执行不符合预期的时候,这段代码的输入是什么,输出是什么,抛出了什么异常,以及代码中每一行的具体执行情况。那么是否有一款产品可以让用户方便快捷的实现这个目标呢?答案是有的。
浅入浅出 MySQL 索引 索引是什么?为什么要有 mysql 索引,解决了什么问题,其底层的原理是什么?为什么使用 B+树做为解决方案?用其他的像哈希索引或者 B 树不行吗? 简单了解索引 首先,索引(Index)是什么?如果我直接告诉你索引是数据库管理系统中的一个有序的数据结构,你可能会有点懵逼。为了避免这种情况,我打算举几个例子来帮助你更容易的认识索引。 我们查询字典的时候可以根据字的部首、笔画来查找到对应的字,这样可以快速的找到对应的字所在页,在字典开头那玩意就叫索引 还有一本书的目录,可以帮我们快速的跳到不同的章节,此时这里的目录也是索引 甚至,景区的地图,会告诉你你现在在哪里,其他景点在哪儿,这个地图从某些方面来说也是索引 再结合开篇较专业的解释,你可能就能够理解索引是什么了。为什么需要索引 了解了索引的概念,我们就需要知道为什么我们需要索引?从刚刚的例子可以看出来,索引的存在的目的就是:字典中的索引帮助我们快速的找到对应的字 书的目录帮助我们快速的跳到我们需要看的章节 景区的地图帮助我们快速的找到想要去的景区的路 在数据库中,索引可以帮助我们快速的查询到对应的数据行,从而顺利的取出所有列的数据。这个过程必须要快,对于现在的 Web 应用来说,DB 如果响应慢,将会直接影响到整个请求的响应时间,而这对用户体验来说是灾难性的。 对于点个按钮,等个好几秒才有返回,那么之后用户大概率是不会再使用你开发的应用了。 MySQL 中的索引 首先,MySQL 和索引其实没有直接的关系。索引其实是 MySQL 中使用的存储引擎 InnoDB 中的概念。在 InnoDB 中,索引分为: 聚簇索引 非聚簇索引 对于聚簇索引,是 InnoDB 根据主键(Primary Key)构建的索引。你可以暂时理解为 key 为主键,value 则是整行数据。并且一张表只能有一个聚簇索引。当然,你可以不定义主键。但是正常情况下我们都会创建一个单调递增的主键,或者是通过统一的 ID 生成算法生成。如果没有定义任何主键,InnoDB 会有自己的兜底策略。InnoDB 会选择我们定义的第一个所有值的都不为空的唯一索引作为聚簇索引。不过实际的生产环境中,的确会有这样的 Corner Case。InnoDB 还有一个究极兜底,如果连仅剩的唯一索引也不符合要求,InnoDB 会自己创建一个隐藏的 6 个字节的主键 RowID,然后根据这个隐藏的主键来生成聚簇索引。 而对于非聚簇索引,是根据指定的列创建的索引,也叫二级索引(Secondary Index),一张表最多可以创建 64 个二级索引。key 为创建二级索引的列的值,value 为主键。换句话说,如果通过非聚簇索引查询,最终只能得到索引列本身的值 + 主键的值,如果想要获取到完整的列数据,还需要根据得到的主键去聚簇索引中再查询一次,这个过程叫回表。 这里说明一下,现在有很多的博客说,MySQL 使用 InnoDB 时,一张表最多只能创建 16 个索引,首先这是错的,明显是从其他的地方直接抄过来的,自己没有去做任何的验证。 在 MySQL 的官方文章中,明确的说明了,一张表最多可以创建 64 个非聚簇索引,而且创建非聚簇索引时,列的数量不能超过 16 个。 注意,是创建非聚簇索引的列不能超过 16 个!这也顺便提一下题外话,所谓的技术严谨,什么叫严谨?对你通过其他渠道获取到的知识,它最多叫作者的观点,我们持一种怀疑态度,并想办法自己去求证。求证后,它才会变成事实。 而不是对某些名词死记硬背,现在的新玩意层出不穷,但当你溯其根源,你会发现就那么回事。 索引底层原理 前面提到了 InnoDB 中索引的类型,简单的了解了其分类和区别,那 InnoDB 中的索引是如何做到加速查询的呢?其底层的原理是啥呢?InnoDB 中的索引的底层结构为 B+ 树,是 B 树的一个变种。先给大家看看 B+树到底长个什么鸟样,下图是一颗存储了数字「1-7」的 B+树。可以看到,B+树中,每个节点可以有多个子节点,而像我们平常熟悉的二叉树中,每个节点最多只能有 2 个。并且,B+树中,节点的存储数据是有序的,而有序的数据结构就可以让我们进行快速的精确匹配和范围查询。而且 B+树中的叶子结点之间有指向下一个节点的指针,而 B 树中的叶子节点是没有的。 在 MySQL InnoDB 的实际实现中,页节点之间其实是个双链表,存储了分别指向上一个、下一个节点的指针 下图是包含了整数「1-7」的 B 树,这个图应该会帮助你加深对两者区别的理解。并且,在 B+树中,除了叶子节点存储了真实的数据之外,其余的节点都只存储了指向下一节点的指针。换句话说,数据全部都在叶子节点上。而在 B 树中,所有的节点都可以存储数据,这是一个最主要的区别。 知道了 B 树和 B+树的基础结构长啥样之后,我们需要再深入了解 InnoDB 是如何利用 B+树来存储数据的。首先,MySQL 并不会把数据存储在内存中,内存只是作为运行时的一种优化,关于 InnoDB 内存架构相关的东西,之前已经写了一篇文章,感兴趣的可以先去看看。 InnoDB 会将数据存储在磁盘上,而当我们查询数据的时候,OS 会将存储在磁盘上的数据一页一页的加载到内存里。这里的页是 OS 管理内存的一种方式,当其加载数据到内存时,会将某个磁盘块上的数据按照页的大小加载。在这里,你可以理解为 B 树中每个节点就是一个磁盘块。 那既然 B 树和 B+树在查找的时候都需要进行 I/O 操作将需要的节点加载到内存,B+树相对于 B 树的优势到底在哪儿?个人认为主要有三点。 一是 B+树能够减少 I/O 的次数。为啥呢?凭啥数据结构长的差不多,B+树就能够减少 I/O 的次数?之前说到,单个节点就代表了一个磁盘块,而单个磁盘块的大小是固定的。B+树仅有叶子结点才存储值,相对于所有节点都存完整数据的 B 树而言,B+树中单个磁盘块能够容纳更多的数据。
一文带你深入了解 Java 字节码 1.1 什么是字节码? Java 在刚刚诞生之时曾经提出过一个非常著名的口号: “一次编写,到处运行(write once,run anywhere)”,这句话充分表达了软件开发人员对冲破平台界限的渴求。“与平台无关”的理想最终实现在操作系统的运用层上: 虚拟机提供商开发了许多可以运行在不同平台上的虚拟机,这些虚拟机都可以载入和执行同一种平台无关的字节码,从而实现了程序的“一次编写到处运行”。 各种不同平台的虚拟机与所有平台都统一使用的程序存储格式—字节码(ByteCode),因此,可以看出字节码对 Java 生态的重要性。之所以被称为字节码,是因为字节码是由十六进制组成的,而 JVM(Java Virtual Machine)以两个十六进制为一组,即以字节为单位进行读取。在 Java 中使用 javac 命令把源代码编译成字节码文件,一个 .java 源文件从编译成 .class 字节码文件的示例如图 1 所示:图 1 对于从事基于 JVM 对于语言的开发人员来说,比如: Java,了解字节码可以更准确、更直观的理解 Java 语言中有更深层次的东西,比如通过字节码,可以很直观的看到 volatile 关键字如何在字节码上生效。另外,字节码增强技术还有各种 ORM 框架、Spring AOP、热部署等一些应用中经常使用,深入理解其原理对于我们来说大有裨益。 由于 JVM 规范的存在,只要最终生成了符合 JVM 字节码规范的文件都可以在 JVM 上运行,因此,这个也给其它各种运行在运行, JVM 上的语言(如: Scala、Groovy、Kotlin)提供了一个机会,可以扩展 Java 没有实现的特性或者是实现一些语法糖。接下来就让我们一起看看这个字节码文件结构到底是什么样的。 1.2 Java 字节码结构 Java 源文件通过用 javac 命令编译后就会得到 .class 结尾的字节码文件,比如一个简单的 JavaCodeCompilerDemo 类如图 2 所示:图 2 编译后生成的 .class 字节码文件,打开后是一堆文件 十六进制 数,如图 3 所示:图 3 在上节提过,JVM 对于字节码规范是有要求的,打开编译后的字节码文件看似混乱无章,其实它是符合一定的结构规范的,JVM 规范要求每一个字节码文件都要由十部分固定的顺序组成的,接下来我们将一一介绍这部分,整体的组成结构如图 4 所示:图 4 (1)魔数(Magic Number) 每个字节码文件的头 4 个字节称为 魔数(Magic Number),它的唯一作用是确定这个文件是否为一个能被虚拟机接受的 Class 文件。很多文件存储标准中都使用魔数来进行身份识别,譬如图片格式,如 gif 或者 jpg 等在文件中都存有魔数。使用魔数而不是扩展名来进行识别主要是基于安全方面的考虑,因为文件扩展名可以随意改动。 魔数的固定值为: 0xCAFEBABE,魔数放在文件头,JVM 可以根据文件的开头来判断这个文件是否可能是一个字节码文件,如果是,才会进行之后的操作。 有趣的是,魔数的固定值是 Java 之父 James Gosling 制定的,为 CafeBabe(咖啡宝贝),而 Java 的图标为一杯咖啡。 (2)版本号(Version) 版本号为魔数之后的 4 个字节,前两个字节表示次版本号(Minor Version),后两个字节表示主版本号(Major Version),上图 3 中版本号为: “00 00 00 34”,次版本号转化为十进制为 0,主版本号转化为十进制 52(3 * 16^1 + 4 * 16^0 = 52),在 Oracle 官网中查询序号 52 对应的 JDK 版本为 1.8,所以编译该源代码文件的 Java 版本为 1.8.0。 (3)常量池(Constant Pool) 紧接着主版本号之后的字节是常量池入口。常量池中存储两种类型常量: 字面量和符号运用。字面量为代码中声明为 final 的常量值,符号引用如类和接口的全局限定名、字段的名称和描述符、方法的名称和描述符。常量池整体上分为两部分: 常量池计数器和常量池数据区,如图 5 所示:图 5 常量池计数器(constant_pool_count): 由于常量池的数量不固定,所以需要先放置两个字节来表示常量池容量计数值,图 2 示例代码的字节码的前十个字节如下图 6 所示,将十六进制的 17 转为十进制的值为 33 (1 * 16^1 + 7 * 16^0 = 33),排除下标 0,也就是说这个类文件里有 32 个常量。图 6 常量池数据区: 数据区是由(constant_pool_count - 1)个 cp_info 结构组成,一个 cp_info 的结构对应一个常量。在字节码中共有 14 种类型的 cp_info 每种类型的结构都是固定的,如图 7 所示:图 7 以 CONSTANT_Utf8_info 以它为例,它的结构如表 1 所示: 表 1 首先第一个字节 tag,它的取值对应图 7 中的 Tag,由于它的类型是 CONSTANT_Utf8_info,所以值为 01(十六进制)。接下来两个字节标识该字符串的长度 length,然后 length 个字节为这个字符串具体的值。从图 3 的字节码中摘取一个 cp_info 结构,将它翻译过来后,其含义为: 该常量为 utf8 字符串,长度为 7 字节,数据为: numberA,如图 8 所示:图 8 其它类型的 cp_info 结构在本文不再细说,和 CONSTANT_Utf8_info 结构大同小异,都是先通过 tag 来标识类型,然后后续的 n 个字节来描述长度和数据。等我们对这些结构比较了解了之后,我们就可以通过: javap -verbose JavaCodeCompilerDemo 命令查看 JVM 反编译后的完整常量池,可以看到反编译结果可以将每一个常量池 cp_info 结构的类型和值都很明确地呈现出来,如图 9 所示:图 9 (4)访问标志(access_flag) 常量池结束之后的两个字节,描述了该 Class 是类还是接口,以及是否被 Public、Abstract、Final 等修饰符修饰。JVM 规范规定了如下表 2 所示的 9 种访问标志。 需要注意的是,JVM 并没有穷举所有的访问标志,而是使用 按位或 操作来进行描述的,比如某个类的修饰符为 public final,则对应的访问修饰符的值为 ACC_PUBLIC | ACC_FINAL,即 0x0001 | 0x0010 = 0x0011。 表 2 (5)当前类名(this_class) 访问标志后的两个字节,描述的是当前类的全限定名。这两个字节保存的值为常量池中的索引值,根据索引值就能在常量池中找到这个类的全限定名。 (6)父类名称(super_class) 当前类名的后两个字节,描述父类的全限定名。这两个字节保存的值也是在常量池中的索引值,根据索引值就能在常量池中找到这个类的父类的全限定名。 (7)接口信息(interfaces) 父类名称后的两个字节,描述这个类的接口计数器,即: 当前类或父类实现的接口数量。紧接着的 n 个字节是所有的接口名称的字符串常量在常量池的索引值。 (8)字段表(field_table) 字段表用于描述类和接口中声明的变量,包含类级别的变量以及实例变量,但是不包含方法内部声明的 局部变量。字段表也分为两部分,第一部分是两个字节,描述字段个数,第二部分是每个字段的详细信息 field_info。字段表结构如图 10 所示:图 10 以图 3 中的字节码字段表为例,如下图 11 所示。其中字段的访问标志查表 2,002 对应为 Private,通过索引下标在图 9 常量池分别得到的字段名为: numberA,描述符为: I(在 JVM 中的 I 代表 Java 中的 int)。综上,就可以唯一确定出类别。 JavaCodeCompilerDemo 声明的变量为: private int numberA 。
假期后来一波干货:一文理清 JVM 和 GC 一、JVM 内存体系 其中方法区和堆被 JVM 中多个线程共享,比如类的静态常量就被存放在方法区,供类对象之间共享。 虚拟机栈、本地方法栈、程序计数器是每个线程独立拥有的,不会与其他线程共享。 所以 Java 在通过 new 创建一个类对象实例的时候,一方面会在虚拟机栈中创建一个对该对象的引用,另一方面会在堆上创建类对象的实例,然后将对象引用指向该对象的实例。对象引用存放在每一个方法对应的栈帧中。虚拟机栈:虚拟机栈中执行每个方法的时候,都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。 本地方法栈:与虚拟机栈发挥的作用相似,相比于虚拟机栈为 Java 方法服务,本地方法栈为虚拟机使用的 Native 方法服务,执行每个本地方法的时候,都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。 方法区:它用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据,方法区在 JDK1.7 版本及之前称为永久代,从 JDK1.8 之后永久代被移除。 堆:堆是 Java 对象的存储区域,任何 new 字段分配的 Java 对象实例和数组,都被分配在了堆上,Java 堆可使用 - Xms 和-Xmx 进行内存控制,从 JDK1.7 版本之后,运行时常量池从方法区移到了堆上。 程序计数器:指示 Java 虚拟机下一条需要执行的字节码指令。 二、JAVA8 之后的 JVM 从图中我们可以看出 JAVA8 的 JVM 用元空间取代了永久代三、GC 作用域四、常见垃圾回收算法引用计数法: JVM 的实现一般不采用这种方式缺点: 每次对对象赋值时均要维护引用计数器,且计数器本身也有一定的消耗; 较难处理循环引用;
图解堆排序,带你彻底了解清楚! 堆排序导图 1,堆排序概念 堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。 相关概念: 1.1,二叉树二叉树 特征:每个节点最多只有 2 个子节点(不存在度大于 2 的节点) 1.2,满二叉树满二叉树 满二叉树:叶子节点全部都在最底层;除叶子节点外,每个节点都有左右孩子; 1.3,完全二叉树完全二叉树 完全二叉树:叶子节点全部都在最底的两层;最后一层只缺少右边的叶子节点,左边一定有叶子节点;除了最后一层,其他层的节点个数均达到最大值; 1.4,最大堆和最小堆 最大堆:堆顶是整个堆中最大元素 最小堆:堆顶是整个堆中最小元素 2,算法思路 我们先搞清楚这个堆排序思想,先把逻辑搞清楚,不着急写代码。 我们首先有一个无序数组,比方说 int[] arr={4,2,8,0,5,7,1,3,9}; 复制代码 既然用到堆,肯定先要将数组构建成二叉堆。需要对数组从小到大排序,则构建成最大堆;需要对数组从小到大排序,则构建成最小堆。 2.1,第一个步骤,初始化堆 我们先来看看数组是如何存储二叉树的注意:这里考虑的当前节点,必须是完全二叉树的非叶子节点。并且节点的左孩子和右孩子必须小于数组长度,所以后面需要添加限制条件。 看到上图中的公式,我们明白了数组是可以存储完全二叉树,同时保留节点之间的关系。以上述数组为例数组存储完全二叉树 那么存储好之后,如何将二叉树构建成二叉堆呢?继续往下看完全二叉树 以这个图为例,我们以最大堆举例,若要构建二叉堆,则 A 要比 B 和 C 都大,B 要比 D 和 E 都大,C 要比 F 和 G 都大。也就是说父节点要比子节点都大才行。记录数组下标的二叉树 在这个图中,明显不满足最大堆的要求。我们先拿序号为 3,7,8 的三个节点来研究,i=3 的节点比 i=7 和 i=8 的节点都小,所以需要交换位置。注意此图是从 0 开始,也就是模拟数组下标从 0 开始。 怎么交换呢?很简单。我们看节点 0,设为当前节点index,那么它的lchild=index*2+1,它的rchild=index*2+2;注意下标从 0 开始。 //初始化堆public static void HeapAdjust(int[] arr,int index,int len){ //先保存当前节点的下标 int max = index; //保存左右孩子数组下标 int lchild = index*2+1; int rchild = index*2+2; //开始调整,左右孩子下标必须小于len,也就是确保数组不会越界 if(lchild<len && arr[lchild] > arr[max]){ max=lchild; } if(rchild<len && arr[rchild] > arr[max]){ max=rchild; } //若发生了交换,则max肯定不等于index了。此时max节点值需要与index节点值交换,上面交换索引值,这里交换节点值 if(max!=index){ int temp=arr[index]; arr[index]=arr[max]; arr[max]=temp; //交换完之后需要再次对max进行调整,因为此时max有可能不满足最大堆 HeapAdjust(arr,max,len); } } 复制代码 上面代码很好理解,中间的两个 if 语句就是交换节点索引值,只要有一个孩子节点大于index,就需要进行交换。若父节点index比两个孩子节点都大,那么就不需要交换了。 最后面的 if 语句是交换节点值,我们知道,只要index与lchild和rchild有交换,则index一定不等于max了! 那为什么最后的if语句里面还要加上一个递归写法呢?我们举个例子就明白了,还是以上述数组为例,先看看一轮交换之后的样子: 第一次交换,0 与 9 交换,此时 Index=9;0 与 9 交换 第二次交换,8 已经比 7 和 1 都大了,此时不需要交换; 第三次交换,2 与 9 交换,此时Index=9;2 与 9 交换 第四次交换,4 与 9 交换,此时Index=9,第一轮交换到此结束。4 与 9 交换 一轮结束后,有小伙伴儿已经发现问题了,虽然 9 成功的成为最大堆的堆顶元素,但是下面的其他元素并不满足最大堆的要求,比如说左下角的元素 2,元素 3,元素 0 等这种二叉树就不满足,元素 4,元素 2,元素 5 也不满足。 因此在交换节点值这个步骤里,我们需要进行递归操作,交换完之后再次对index进行堆调整: //若发生了交换,则max肯定不等于index了。此时max节点值需要与index节点值交换,上面交换索引值,这里交换节点值if(max!=index){ int temp=arr[index]; arr[index]=arr[max]; arr[max]=temp; //交换完之后需要再次对max进行调整,因为此时max有可能不满足最大堆 HeapAdjust(arr,max,len);} 复制代码 堆排序的测试: //堆排序 public static int[] HeapSort(int[] arr){ int len=arr.length; /** * 第一步,初始化堆,最大堆,从小到大 * i从完全二叉树的第一个非叶子节点开始,也就是len/2-1开始(数组下标从0开始) */ for(int i=len/2-1;i>=0;i--){ HeapAdjust(arr,i,len); } //打印堆顶元素,应该为最大元素9 System.out.println(arr[0]); return arr; } 复制代码 上述代码就是从完全二叉树的第一个非叶子节点开始调换,还顺便打印堆顶元素,此时应为 9; 至此,第一个步骤,初始化堆完成,最后的结果应该为下图:初始化堆结束 2.2,第二个步骤,堆排序 堆排序的过程就是将堆顶元素(最大值或者最小值)与二叉堆的最末尾叶子节点进行调换,不停的调换,直到二叉堆的顺序变成从小到大或者从大到小,也就实现了我们的目的。 我们这里以最大堆的堆顶元素(最大元素)为例,最后调换的结果就是从小到大排序的结果。 第一次交换,我们直接将元素 9 与元素 0 交换,此时堆顶元素为 0,设当前节点index=0;0 与 9 交换 这时,我们需要将剩下的元素(排除元素 9)进行堆排序,直到下面这个结果:除元素 9 以外剩下的元素排序 代码: /** * 第二步,交换堆顶(最大)元素和二叉堆的最后一个叶子节点元素。目的是交换元素 * i从二叉堆的最后一个叶子节点元素开始,也就是len-1开始(数组下标从0开始) */for(int i=len-1;i>=0;i--){ int temp=arr[i]; arr[i]=arr[0]; arr[0]=temp; //交换完之后需要重新调整二叉堆,从头开始调整,此时Index=0 HeapAdjust(arr,0,i);} 复制代码 注意:这里有个小细节问题,前面我们写的初始化堆方法有三个参数,分别是数组arr,当前节点index以及数组长度len,如下: HeapAdjust(int[] arr,int index,int len) 复制代码 那么,为何不直接传入两个参数即可,数组长度直接用arr.length表示不就行了吗?初始化堆的时候是可以,但是后面在交换堆顶元素和末尾的叶子节点时,在对剩下的元素进行排序时,此时的数组长度可不是arr.length了,应该是变化的参数i,因为交换之后的元素(比如 9)就不计入堆中排序了,所以需要 3 个参数。 我们进行第二次交换,我们直接将元素 8 与元素 2 交换,此时堆顶元素为 2,设当前节点index=2;8 与 2 交换 这时,我们需要将剩下的元素(排除元素 9 和 8)进行堆排序,直到下面这个结果:除元素 9 和 8 以外剩下的元素排序 到这个时候,我们再重复上述步骤,不断调换堆顶和最末尾的节点元素即可,再不断地对剩下的元素进行排序,最后就能得到从小到大排序好的堆了,如下图所示,这就是我们想要的结果:最终排序结果:从小到大 3,完整代码import java.util.Arrays; public class Head_Sort { public static void main(String[] args) { int[] arr={4,2,8,0,5,7,1,3,9}; System.out.println("排序前:"+Arrays.toString(arr)); System.out.println("排序后:"+Arrays.toString(HeapSort(arr))); } //堆排序 public static int[] HeapSort(int[] arr){ int len=arr.length; /** * 第一步,初始化堆,最大堆,从小到大。目的是对元素排序 * i从完全二叉树的第一个非叶子节点开始,也就是len/2-1开始(数组下标从0开始) */ for(int i=len/2-1;i>=0;i--){ HeapAdjust(arr,i,len); } /** * 第二步,交换堆顶(最大)元素和二叉堆的最后一个叶子节点元素。目的是交换元素 * i从二叉堆的最后一个叶子节点元素开始,也就是len-1开始(数组下标从0开始) */ for(int i=len-1;i>=0;i--){ int temp=arr[i]; arr[i]=arr[0]; arr[0]=temp; //交换完之后需要重新调整二叉堆,从头开始调整,此时Index=0 HeapAdjust(arr,0,i); } return arr; } /** *初始化堆 * @param arr 待调整的二叉树(数组) * @param index 待调整的节点下标,二叉树的第一个非叶子节点 * @param len 待调整的数组长度 */ public static void HeapAdjust(int[] arr,int index,int len){ //先保存当前节点的下标 int max = index; //保存左右孩子数组下标 int lchild = index*2+1; int rchild = index*2+2; //开始调整,左右孩子下标必须小于len,也就是确保index必须是非叶子节点 if(lchild<len && arr[lchild] > arr[max]){ max=lchild; } if(rchild<len && arr[rchild] > arr[max]){ max=rchild; } //若发生了交换,则max肯定不等于index了。此时max节点值需要与index节点值交换,上面交换索引值,这里交换节点值 if(max!=index){ int temp=arr[index]; arr[index]=arr[max]; arr[max]=temp; //交换完之后需要再次对max进行调整,因为此时max有可能不满足最大堆 HeapAdjust(arr,max,len); } }} 复制代码 运行结果:运行结果 4,算法分析4.1,时间复杂度 建堆的时候初始化堆过程(HeapAdjust)是堆排序的关键,时间复杂度为O(log n),下面看堆排序的两个过程; 第一步,初始化堆,这一步时间复杂度是O(n); 第二步,交换堆顶元素过程,需要用到 n-1 次循环,且每一次都要用到(HeapAdjust),所以时间复杂度为((n-1)*log n)~O(nlog n); 最终时间复杂度:O(n)+O(nlog n),后者复杂度高于前者,所以堆排序的时间复杂度为 O(nlog n); 4.2,空间复杂度 空间复杂度是O(1),因为没有用到额外开辟的集合空间。 4.3,算法稳定性 堆排序是不稳定的,比方说二叉树[6a,8,13,6b],(这里的 6a 和 6b 数值上都是 6,只不过为了区别 6 所以这样)经过堆初始化后以及排序过后就变成[6b,6a,8,13];可见堆排序是不稳定的。 版权声明: 本文为 InfoQ 作者【程序员的时光】的原创文章。 原文链接:【http://tieba.baidu.com/mo/q/checkurl?url=https%3A%2F%2Fxie.infoq.cn%2Farticle%2F987e23cc63083bdc9998b4e2f&urlrefer=76f2727bdc9ff1c5f82579e81d65c71c】。文章转载请联系作者。
GMSV186剑豪现版本存在的BUG以及理想未来技改方案
KMST冒险家重制-刀飞三次技改汇总 整体增强不少
全职业泰山100%的时代来临了(包含冒险家) 冒险家重制第三次修改也增加了泰山
KMST冒险家重制二次修改! 哎,果然英雄的追怪技能加冷去了!具体如下
想做一名数据分析师,有什么好的学习平台推荐吗?朋友说的极客时 生活在一个大数据的年代,用数据来分析问题并且做决策确实是很多行业的刚需,极客时间里边有很多数据分析的教程专栏我觉得都挺不错的,陆陆续续也从中学到了很多,尤其是里边陈旸老师的《数据分析实战 45 讲》我现在已经二刷了,他清晰地把数据分析拆解成三个部分:数据采集、数据可视化和数据挖掘,而且有大量实战。整个专栏的授课逻辑我也非常喜欢,从思维到实践,数据分析能力真的有提升很多。
极客时间里边的SQL教程怎么样啊?老师讲的好吗? SQL作为一门专门为数据查询所涉及的编程语言,是所有数据分析师都必须掌握的基本功。在网上做了点儿攻略后报名了极客时间的SQL学习专栏,在老师深入浅出的讲解后突然觉得SQL作为高度结构化的语音其实学起来也没那么难了,现在跟着老师学习掌握了一些基本语句以及一些函数,已经可以基本上掌握80%以上的数据查询需求了,总的来说极客时间上边干货真的不少,值得一学。
极客时间里边的Python教程怎么样啊?学完可以自己进行数据处理吗 我的数据分析入门就是通过极客时间里边的课程进行的,有一说一里边很多的精品课是真的不错,讲的很透彻。这段时间在进行这几年最火的数据分析工具Python的学习,有了前期在极客时间进行的大量基础算法和函数的学习基础加上老师的深入浅出的讲解,上手Python的学习还是比较容易的。现在我在极客时间里边的Python 进阶训练营已经学习了一段时间了,基本上可以独立做一个从数据处理到建模调优的完整数据项目了,老师也会进行定期的答疑讲解,极客时间还是比较值得报名的。
极客时间里边的数据分析课适合小白入门吗? 我觉得还是不错的,当时是看到了极客时间的新闻就报名了他们家的那个从零开始学大数据,老师讲的深入浅出,小白学起来完全没有压力。学了一段时间后我现在基本上就可以熟练使用Excel做数据处理并且随心所欲的做各种数据图表了,现在正在进行SQL的学习,总的来说极客时间对于小白的入门还是很友好的。
极客时间里边的IT培训课程怎么样? 总的来说极客时间里边课程可供选择性挺多的,适合各种需求的学生或者是从业人员学习。里边的大多数课程老师都是经过精挑细选的业内知名人士,有实战经验更有理论功底,讲出课来深入浅出更容易接受。像我最近在看的《MySQL难点解析》就很不错,老师对MySQL的应用、索引分类、优化方法等方向都会结合实践进行深入讲解,干货满满。其实我们这一代可以通过这样的平台和一些业内的大神接触真的是一件很幸运的事情,这些大神的观点是在任何书上或是职场上都很难靠自己领悟到的,不得不说很多课真的很值。
极客时间里边的课值得购买吗? 我觉得还是挺值的,总比一些培训班强,学的是一样的内容线下培训班简直贵得要死。像里边的很多图文音频都可以利用碎片时间来学习,效率比较高。我在里边买了几门课程觉得收获还挺高的,像吴咏炜老师的C++课程讲的就很不错。但是注意报了课程一定要仔细听课!只要认真学的话绝对能让自己的能力有一个质的提升。学无止境,正打算再买几门课提升提升呢。
极客时间上边的课程怎么样啊? 极客时间里边有上百位顶级技术和行业专家分享学习内容,看大神的经验分享真的是一种享受,受教颇多。在上边买了蛮多课程了,每天定时的学习确实让自己的综合能力提升了不少,我现在已经可以称得上是我们团队的扛把子了。像之前买的《数据结构与算法之美》,课程设计编排合理深入浅出,数据结构的算法以及很多编程思想都渗透的很不错,如果认真跟着学的话他所带来的价值真的不是买它时的那些票价所能比拟的,真的物超所值。
极客时间上边的IT课程适合新手入门吗? 极客时间是超过 100 万程序员在用的 IT 知识学习平台,里边的课程资源相当丰富,可以帮助各阶段 IT 从业者学有所成。我刚入门的那段时间是跟着他们的零基础学 Python学习,老师讲的还是蛮容易理解的,每天跟着认真的学习、记笔记收获确实挺多的。他可以帮你夯实技术基础,锻炼实操技能,因为它不仅仅是只空讲理论还会提供一些实际案例辅助理解,所以说学了以后对于实际工作上手还是蛮快的,后来又跟着上边学了不少的课程,工作技能直线上升,不得不说极客时间的课程质量确实比较有保证。
极客时间上边有什么值得一看的课程? 说实话在此之前我一直是对付费专栏不怎么感冒的,觉得里边好多东西都是智商税。但是在朋友跟我再三安利后我试着看了看上边很有名的《MySQL45讲》,这个专栏被这么多人推真的是有原因的,阅读下来相当流畅真的有停不下来的感觉。专栏里边图文并茂,作者大大实力爆表,文章思路清晰易懂,干货满满很适合反复阅读!极客时间里边还有很多大神的课都讲的很好,可以时不时的多去逛逛,收货真的很多。
IT从业者有必要学习极客时间上边的课程吗? 现在IT行业发展的太快了,不学习就只能等着被淘汰了,极客时间里边的课程涉及面挺广泛的,覆盖了前端、后端、架构、大数据、人工智能、云计算等数十个技术领域。从基础到进阶,从工程师到管理者, 可以满足IT人才全方位成长需求。刚入门的时候我也只是会基础的会框架、写CSS而已,现在跟着里边的课程学了不少东西,在我们的团队里边的地位也越来越高了,薪资也涨了不少。学习永无止境,我觉得适当的学习还是挺有必要的。
该不该买极客时间上边的IT培训课? 因人而异吧,我觉得在上边买的几节课都挺值的,老师讲的比较透彻而且讲的会更容易理解一点。之前在朋友圈也看到过别的IT课程推送,试听了两节觉得讲的太枯燥了听不下去。但是极客时间里边的大神讲师会结合他们在工作过程中的实践经历来讲,代入感更强而且实用性和可操作性都会更强一点,在里边确实也是实打实的学到了不少东西。我觉得可以多筛选筛选看看课程是不是符合自己的心意,里边的老师讲课质量都是不错的。
极客时间里边的专栏怎么样? 算起来极客时间是第一个我心甘情愿为知识付费的平台,里边汇集了很多我所仰慕的业界的大佬,听大佬讲知识学到的东西真的不是一般的课能比的。陆陆续续也买了不少专栏,里边的东西都是讲师们多年工业实践的积累,实战性确实很强.。像我最近在看的陈旸老师写的《数据分析实战45讲》,深入浅出,而且老师会直接提供项目数据让你上手练习,这也可以让你在简历上完善项目经历,不得不说确实很值。
在极客时间里边真的可以学到东西吗? 极客时间作为一个一站式 IT 知识学习平台,里边的 学习资源还是相当丰富的,我报过他们的Python名师训练营,老师讲课还是很有意思的,学下来确实是收获了不少的东西。名师训练营里边采用的是陪伴式学习服务,通过还原真实的教育情境让人拥有沉浸式的学习体验,学习效果感觉还是蛮好的,很容易的可以跟上老师的思路。而且老师还会针对学员高频疑问及热点问题进行阶段性的答疑讲解,我觉得收获还是很多的。
极客时间上边的 IT培训课程怎么样啊? 陆陆续续在极客时间里边买了不少的课程了,当时是被广告吸引的,但是现在仍然还在里边学习真的是全凭兴趣,里边的很多课都是满满的干货而且学起来也比较容易上手。我觉得里边的老师真的不愧是专业的,讲起课来真的是深入浅出,理解性很强。像左耳朵耗子、王争、、winter 等这样的顶级技术和行业专家分享的课程真的要需要的话值得去一听,学到的不仅仅是专业方面的知识,从他们的讲解中所感悟产生的思维中的转变才是从中学到的最宝贵的东西。
没什么基础可以去极客时间做编程培训吗? 可以的,你可以先去学习计算机基础,等基础打扎实了再开始学习编程培训。一步步的慢慢提升。老师们讲的课程都很好,很容易理解,就算没什么基础一点点学习也都能学会的。重要的还是坚持,从零开始都需要坚持啊,先去报一个计算机基础吧。
极客时间Java讲的怎么样? 可以去试听哦,官网有免费的试听课程,还能自己先了解一下要学习的是什么,还有总体的课程安排等等。就我的感觉而言,讲的挺不错的。我年纪不大,接受能力挺强的,因为游戏开始喜欢编程,课程都很喜欢,现在也有不小的收获。
每天都是碎片化时间,可以去极客时间学习编程培训吗? 可以的。你可以去极客时间的官网上看看,学习的方式有很多种。像是专栏,视频课程,微课等等。碎片化的时间这些也都可以看,当然还是推荐微课,较短,适合碎片化时间。里面的课程种类也很多,可以根据自己的需求挑选合适的。
IT技术培训去极客时间行吗? 可以,极客时间的课程老师都很好,我就是在极客时间参加的IT技术培训。老师们很有耐心,一步步的引导你,教给你怎么做怎么才能更有效率。我的能力提高的也特别快,打算一直学下去,技多不压身,哪天也许还能换个工作。
你在极客时间学过IT技术培训吗?怎么样? 我做过的,现在能力很强。买了很多对我很有帮助的课程。课程内容通俗易懂,老师的讲解也是深入浅出,非常合适新手入门,刚刚接触IT技术培训的人。我学习的时候感受颇深,以前也找过资料,但是没有极客时间那么容易理解。
极客时间编程培训怎么样? 挺好的,至少我自己看着特别好。老师讲的很细致,都有一些个例子,有理论知识也有实践活动。结合起来不枯燥而且容易学进去,容易学懂学成自己的知识。而且他不光是有理论知识,最主要的还是提高你的操作能力,很实用。
极客时间IT技术培训可以吗? 可以的,我就学习了不少极客时间的课程。都是用碎片化的时间,去看的视频教程。感觉很容易理解。而且我很喜欢研究这些东西,也会自己多多操作,现在能自己做些小程序了,我觉得多学一些就可以找工作了,也是在为换个工作环境而努力。
极客时间C++课程怎么样? 我学过极客时间的C++,C++在编程里也算是很难理解的,自学的话估计学不会,还是推荐去极客时间学习的。主要是老师们讲的很通俗易懂,再自己实践一下理解就不难。要是自己琢磨着搜资料,很难学会。一边在极客时间学习,一边自己动手操作是学的最快的方法。
极客时间的编程培训有哪些值得买的课程? 大家都说,极客时间上面的课程特别好,极客时间上课程种类繁多,有哪些课程是针对我们个人,真正值得购买的呢?在这里,我推荐几个图文专栏,一是《左耳听风》,二是编译原理。极客时间上面的课程都很专业,大家根据自己需求选择即可。
极客时间的编程培训适合纯小白学习吗? 极客时间作为编程培训的首选平台,当然适用于新手学习啦。如果说,作为一个纯小白,选择极客时间进行学习的话,推荐选择就业班,选择学习路径,学习过后需要提交作业,可以邀请讲师进行作业批注解答。新手还可以在评论区结交大神级朋友,获得经验传授。
极客时间的每日一课怎么样? 极客时间的每日一课都是比较零散的点,通过视频讲授专业程度比较高的一些知识。比较适合利用生活和学习中的碎片时间进行学习,比如说通勤时间。每日一课可以用来作为有基础的学员拓展充实自己知识面的渠道,也可以作为拓展知识的指导大纲,促进更加专业的学习。你知道了了吗。
极客时间上的微信小程序开发课程怎么样? 极客时间有推出过9小时搞定微信小程序开发的课程,课程从分析微信小程序的背景入手,结合实战经验,讲授组件以及程序开发。可以掌握小程序开发的基础理论详解,掌握小程序开发的核心技能,轻轻松松,从零开始,构建一款微信小程序。
极客时间Java讲的怎么样? 可以去试听哦,官网有免费的试听课程,还能自己先了解一下要学习的是什么,还有总体的课程安排等等。就我的感觉而言,讲的挺不错的。我年纪不大,接受能力挺强的,因为游戏开始喜欢编程,听的是Java业务开发常见错误100例,朱晔老师讲的很仔细,特别适合有一定基础的的Java开发者,尤其是偏业务型项目的开发人员,肯定会有有不小的收获。
极客时间C语言怎么样?听得懂吗? 能,我就听得懂,而且现在写C语言也写的挺好。不光能懂,还能编写。老师讲的真的还挺好的。我打算学完了C语言,再去学点别的课程。Java啊还有Go等等,我觉得都挺有用的,对我的工作也很有帮助,你可以去极客时间试听一下,真的挺好的。
每天都是碎片化时间,可以去极客时间学习编程培训吗? 可以的。你可以去极客时间的官网上看看,学习的方式有很多种。像是专栏,视频课程,微课等等。碎片化的时间这些也都可以看,当然还是推荐微课,较短,适合碎片化时间。平时玩手机的时间都可以利用起来,里面的课程种类也很多,我就是从程序员的数学基础课开始学的,很实用很赞。
IT技术培训去极客时间行吗? 可以,极客时间的课程老师很好的,我就是在极客时间参加的IT技术培训。老师们很有耐心,一步步的引导你,我就是属于基础不好的,从趣谈网络协议课程开始的,常见的TCP/IP、HTTP,网络协议的基础内容学起。我的能力提高的也特别快,打算一直学下去,技多不压身,哪天也许还能换个好点的工作。
你在极客时间学过IT技术培训吗?怎么样? 我做过的,现在能力很强。买了很多对我很有帮助的课程。例如赵成的运维体系管理课,侧重于区块链的深入浅出区块链课程内容通俗易懂,老师的讲解也是深入浅出,非常合适新手入门,刚刚接触IT技术培训的人。我学习的时候感受颇深,以前也找过资料,但是没有极客时间那么容易理解。
极客时间编程培训怎么样? 挺好的,至少我自己看着特别好。老师讲的很细致,都有一些个例子比如说侧重于云计算的SRE实战手册,有理论知识也有实践活动。结合起来不枯燥而且容易学进去,容易学懂学成自己的知识。而且他不光是有理论知识,最主要的还是提高你的操作能力,很实用。
极客时间IT技术培训可以吗? 可以的,我就学习了不少极客时间的课程。都是用碎片化的时间,去看的视频教程,有线上培训、线上媒体、线下培训。多种课程可以先选择,感觉很容易理解。而且我很喜欢研究这些东西,也会自己多多操作,现在能自己做些小程序了,我觉得多学一些就可以找工作了,也是在为换个工作环境而努力。
极客时间C++课程怎么样? 我学过极客时间的C++,C++在编程里也算是很难理解的,自学的话估计学不会,还是推荐去极客时间学习的像课程罗剑锋的C++实战笔记吴老师的现代C++实战30讲都很实用。主要是老师们讲的很通俗易懂,再自己实践一下理解就不难。要是自己琢磨着搜资料,很难学会。一边在极客时间学习,一边自己动手操作是学的最快的方法。
极客时间《高并发系统设计40问》课程怎么样? 机会是留给有准备的人的,为了避免遇到并发问题时手忙脚乱我们必须储备足够多的高并发知识,已具备随时应对可能出现的高并发需求场景的能力。这门课通过场景、原理、实践结合的方式讲解,可以帮助我们更深入的理解和消化,掌握高并发系统设计的“套路”,理解基本的系统设计思想,突破技术的瓶颈,养成一个优秀架构师的资质,我觉得收获真的蛮大的。
极客时间《数据结构与算法之美》课程怎么样? 《数据结构与算法之美》是我买的第一个专栏,也是花时间最多的一个专栏。算法的书籍我买了很多,但是他们大多数都是理论为主很枯燥,学习的时间也不连续,很难静下心来慢慢理解消化,而这个专栏用图文并茂的形式讲解理论知识,更容易理解一些,回过头再来看看书也更加轻松自在。老师还会通过把工程中遇到的一些问题作为引子,慢慢的引入到对应的数据结构和算法的理论中,基本上以后再看到的话印象也比较深,总的来说是挺不错的一门课。
极客时间的《Vue开发实战》这门课怎么样? 不得不承认,Vue越来越受欢迎了。可以说对于任何一个前端工程师来说,掌握Vue更像是一门“必修课”。我觉得《Vue开发实战》课程体系比较完整,基本上从Vue基础、到生态、再到项目实战覆盖了各个知识点,而且原理和实战相辅相成是这个课的特色之一。学完这门课程感觉不仅掌握了Vue的技术应用,也对其底层原理有了一定了解,具备了独立负责Vue前端项目的能力,挺好的一门课。
极客时间《微信小程序全栈开发实战》课程怎么样? 从当初的一夜成名,到今天火爆的市场占有率,从数据看,小程序互联网已经爆发,百度、阿里、头条等大厂都相继推出自己的小程序平台,在未来小程序的开发热度必定还将水涨船高。李艺老师的《微信小程序全栈开发实战》这门课不仅学习的是技术,课程设计的小程序开源项目具有一定的行业通用性,稍加改造还可以修改成知识付费小程序、本地商城小程序等多类项目,在生产生活中实用性极高,还是比较值得推荐的。
极客时间《程序员的数学基础课》怎么样? 就目前的IT职场环境来说,但凡想再往上走一步、做任何一点带有创新性的技术,往往都逃不开数学。不论是数据结构与算法还是程序设计,底层原理和思路都源于数学,在大数据和智能化的时代学好数学更是门槛本身。市面上数学资料看了不少,但不是太难就是太抽象,能深入浅出的不多,《程序员的数学基础课》就是一个。跟着学下来,让我把算法和数学模型都串联起来,还清理了之前的一些盲点,有时间一定二三刷。
极客时间的小马哥的 Java 项目实战营怎么样? 挺好的,我的一个同事就是刚学完,听他反馈,讲师小马哥经验丰富,之前是Spring Cloud Alibaba 架构师、前阿里云原生架构师。课程围绕围绕互联网项目实战,兼顾广度和深度,系统探讨技术和架构,掌握互联网业务和系统架构演进过程,打造学员全栈工程师必备素养,增强核心研发力和跨界竞争力。听了课程豁然开朗,平时花很久时间琢磨的问题老师一句话就解决了,从一知半解到熟练的话听这个课程帮助老大了,朋友这样说。
极客时间的算法训练营怎么样? 我给你讲讲我学习的情况吧,我是做这一行的想提升一下算法方面的知识,就报名了极客时间的算法训练营,老师结合每周知识点精选大厂面试题,学完就练习,而且全部题目均基于大厂面试真题改编,无需设置环境,提交后立即反馈执行结果,每道题匹配相关知识点,特别适合想快速掌握算法,提升自己的,清楚算法的重要性,但找不到学习方法的学员。
首页
1
2
3
下一页