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嘛?

  • 我是暂时不能了,,各位大佬请便!

结语

虽然这些优化并没有得到质的改善,并且可能也比较初级,但是刷题的同时通过这种不断优化能够增加对计算机执行和原理的理解:哇,原来是这样。并且如果有时间养成这样的好习惯,相信不久以后,未来的调优专家就是你了!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32定时器有多种实现方式,其中最常用的是基于定时器中断的方式。在这种方式下,我们可以使用定时器的计数器来产生一个固定时间间隔的中断,从而实现定时功能。 对于定时2ms的需求,可以使用STM32定时器的计数器和自动重载寄存器来实现。假设我们使用TIM3作为定时器,其时钟频率为72MHz,则可以通过设置TIM3的预分频器和自动重载值来实现2ms的定时。 具体实现方式如下: 1. 设置TIM3的预分频器,将时钟频率分频到1MHz(72MHz / 72 = 1MHz)。 2. 设置TIM3的自动重载值,使得计数器每计数到2000时产生一次中断(1MHz / 2000 = 500Hz)。 3. 使能TIM3的中断,并在中断服务函数中处理相关逻辑。 以下是示例代码: ``` // 定义中断服务函数 void TIM3_IRQHandler(void) { // 处理定时器中断逻辑 // ... // 清除中断标志位 TIM_ClearITPendingBit(TIM3, TIM_IT_Update); } int main(void) { // 初始化TIM3 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; TIM_TimeBaseInitStruct.TIM_Period = 2000 - 1; // 自动重载值 TIM_TimeBaseInitStruct.TIM_Prescaler = 72 - 1; // 预分频器 TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct); // 使能TIM3中断 NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); // 启动TIM3 TIM_Cmd(TIM3, ENABLE); while(1) { // 主程序逻辑 // ... } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值