刷题一个4ms的程序,代码如何优化到3ms再到2ms?
前言
你在打王者荣耀的时候,是否经常会遇到这种情况:和对面同位置对线的时候,自己也没有太大失误,但是为啥对面经济比我高?能够压着我打?——是我太菜了 这可能就是你们细节上的差距,别人可能对兵线、技能、英雄机制搞得更清楚,每一步都清清楚楚,刷题也是一样,同样的方法,为啥别人的比你快很多,也需要注意一下细节。
笔者最近再刷LeetCode,对于正常一道题来说,时间的耗费有两个差距:
时间复杂度的差距
时间复杂度上的差距,因为很多题正常的暴力是O(n2)甚至更慢的时间复杂度,这些方法就算能过但是时间耗费很长,如果你发现你的算法过的时间在后30%那就说明你的方法不对了。在复杂度的差距差的可能几百毫秒,几十毫秒,而快的可能是几毫秒。
细节上的差距 很多题可能大家能够想到的比较好的方法的时间复杂度可能差不多,自己也是几毫秒但是自己的排名依然在50%-60%左右,这到底是为什么呢?是因为你的细节还需要优化,你整体复杂度虽然掌握了,但是你可能多算了几次循环,几次运算。所以当条件允许你需要静下来思考下怎么样才能让自己的程序跑在前90%以上。怎样去优化这个时间。
具体
笔者就拿今天刷的这道力扣题来讲讲,力扣的第11题,思路很清晰就是从两边向中间动态压缩区间,是一个O(n)的时间复杂度。
笔者第一次使用这个写法是4ms:
public int maxArea2(int[] height) {
int max = 0;
int left = 0;
int right = height.length - 1;
while (left < right) {
max = Math.max(max, Math.min(height[left], height[right]) * (right - left));
if (height[left] > height[right])// 右侧更小
right--;
else {
left++;
}
}
return max;
}
但是我在研究这段代码时候发现以下几点问题可以优化:
- 使用Math.max()判断最大值最小值的时候,下面在判断是左指针右移还是右指针左移动重复判断了,我们可以手动比较大小重复利用这次计算去完成相同的操作。
public int maxArea3(int[] height) {
int max = 0;
int left = 0;
int right = height.length - 1;
while (left < right) {
if (height[left] < height[right]) {
max = Math.max(max, height[left] * (right - left));
left++;
} else {
max = Math.max(max, height[left] * (right - left));
right--;
}
}
return max;
}
这样的一部优化之后时间来到了稳定的3ms:
还能继续优化嘛?当然能:
- Math.max()返回double类型转成int需要一定成本,然而我们数据本身就是int类型可以直接计算。
- 对数组取值的时候,比较取一次(两个值),计算取一次(一个值),而我们知道数组其实在内存中我们通过0号位置计算得到我们对应位置的数值,所以我们可以把3次计算减少成2次,用两个空间leftvalue和rightvalue记录左右数组位置的值。
通过上面的优化得到下面的代码:
public int maxArea4(int[] height) {
int max = 0;
int left = 0;
int right = height.length - 1;
int team = 0;
int leftvalue=0;int rightvalue=0;
while (left < right) {
leftvalue=height[left];rightvalue=height[right];
if (leftvalue < rightvalue) {
team = leftvalue * (right-left);
if(max<team) {max=team;}
left++;
} else {
team = rightvalue * (right-left);
if(max<team) {max=team;}
right--;
}
}
return max;
}
成功步入2ms大军。
还能到1ms嘛?
- 我是暂时不能了,,各位大佬请便!
结语
虽然这些优化并没有得到质的改善,并且可能也比较初级,但是刷题的同时通过这种不断优化能够增加对计算机执行和原理的理解:哇,原来是这样。并且如果有时间养成这样的好习惯,相信不久以后,未来的调优专家就是你了!
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 面试官:你精通多少种语言的Hello World?
- Flutter 裁剪类组件 最全总结
- Flutter Form表单控件超全总结
- 你知道吗,Flutter内置了10多种Button控件
- Flutter 日期时间DatePicker控件及国际化
- 强大的Flutter App升级功能
- 你知道吗,Flutter内置了10多种show
- 还记得第一个看到的Flutter组件吗?
- 150多个Flutter组件详细介绍送给你
- Flutter 学习路线图
- Flutter 强大的MediaQuery控件
- 基于web的图书管理系统设计与实现
- 《Flutter 动画系列一》25种动画组件超全总结
- 网工小白升级打怪篇(三)ssh实现远程管理
- 贪吃蛇的使命 | 零基础入门贪吃蛇游戏(附源码、演示地址)