程序员如何巧妙学习算法技巧?(文末有彩蛋)

2024-02-21 17:30

本文主要是介绍程序员如何巧妙学习算法技巧?(文末有彩蛋),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

今天和大家讲讲,在做算法题时常用的一些技巧。对于平时没用过这些技巧的人,或许你可以考虑试着去看看在实践中能否用的上这些技巧来优化问题的解。

 

                                                                                        巧用数组下标

 

数组的下标是一个隐含的很有用的数组,特别是在统计一些数字,或者判断一些整型数是否出现过的时候。

例如,给你一串字母,让你判断这些字母出现的次数时,我们就可以把这些字母作为下标,在遍历的时候,如果字母a遍历到,则arr[a]就可以加1了,即arr[a]++。

通过这种巧用下标的方法,我们不需要逐个字母去判断。

我再举个例子:

问题:给你n个无序的int整型数组arr,并且这些整数的取值范围都在0-20之间,要你在 O(n) 的时间复杂度中把这n个数按照从小到大的顺序打印出来。

对于这道题,如果你是先把这n个数先排序,再打印,是不可能O(n)的时间打印出来的。

但是数值范围在0-20。我们就可以巧用数组下标了。把对应的数值作为数组下标,如果这个数出现过,则对应的数组加1。

代码如下:

 

public void f(int arr[]) {

        int[] temp = new int[21];
        for (int i = 0; i < arr.length; i++) {
            temp[arr[i]]++;
        }
        //顺序打印
        for (int i = 0; i < 21; i++) {
            for (int j = 0; j < temp[i]; j++) {
                System.out.println(i);
            }
        }
    }

利用数组下标的应用还有很多,大家以后在遇到某些题的时候可以考虑是否可以巧用数组下标来优化。

 

                                                                                            巧用取余

 

有时候我们在遍历数组的时候,会进行越界判断,如果下标差不多要越界了,我们就把它置为0重新遍历。特别是在一些环形的数组中,例如用数组实现的队列。往往会写出这样的代码:

 

for (int i = 0; i < N; i++) {
        if (pos < N) {
         //没有越界
         // 使用数组arr[pos]
        else {
          pos = 0;//置为0再使用数组
           //使用arr[pos]
         }
        pos++;
   }

实际上我们可以通过取余的方法来简化代码。

 

for (int i = 0; i < N; i++) {
   //使用数组arr[pos]   (我们假设刚开始的时候pos < N)
   pos = (pos + 1) % N;
}

 

 

                                                                                          巧用双指针

 

对于双指针,在做关于单链表的题是特别有用,比如“判断单链表是否有环”、“如何一次遍历就找到链表中间位置节点”、“单链表中倒数第k个节点”等问题。

对于这种问题,我们就可以使用双指针了,会方便很多。我顺便说下这三个问题怎么用双指针解决吧。

例如对于第一个问题,我们就可以设置一个慢指针和一个快指针来遍历这个链表。

慢指针一次移动一个节点,而快指针一次移动两个节点,如果该链表没有环,则快指针会先遍历完这个表,如果有环,则快指针会在第二次遍历时和慢指针相遇。

对于第二个问题,一样是设置一个快指针和慢指针。慢的一次移动一个节点,而快的两个。

在遍历链表的时候,当快指针遍历完成时,慢指针刚好达到中点。

对于第三个问题,设置两个指针,其中一个指针先移动k个节点。之后两个指针以相同速度移动。

当那个先移动的指针遍历完成的时候,第二个指针正好处于倒数第k个节点。

你看,采用双指针方便多了吧。所以以后在处理与链表相关的一些问题的时候,可以考虑双指针哦。

 

                                                                                        巧用移位运算

 

有时候我们在进行除数或乘数运算的时候,例如n / 2,n / 4, n / 8这些运算的时候,我们就可以用移位的方法来运算了,这样会快很多。

例如:

  • n / 2等价于n >> 1;

  • n / 4等价于n >> 2;

  • n / 8等价于n >> 3。

这样通过移位的运算在执行速度上是会比较快的,也可以显得你很厉害的样子,哈哈。

还有一些&(与)、|(或)的运算,也可以加快运算的速度。例如判断一个数是否是奇数,你可能会这样做:

 

if(n % 2 == 1){
  dosomething();
}

不过我们用与或运算的话会快很多。例如判断是否是奇数,我们就可以把n和1相与了,如果结果为1,则是奇数,否则就不会。即:

 

if(n & 1 == 1){
  dosomething();
)

具体的一些运算技巧,还得需要你们多在实践中尝试着去使用,这样用久后就会比较熟练了。

                                                                                          设置哨兵位

 

在链表的相关问题中,我们经常会设置一个头指针,而且这个头指针是不存任何有效数据的,只是为了操作方便,这个头指针我们就可以称之为哨兵位了。

例如我们要删除头第一个节点是时候,如果没有设置一个哨兵位,那么在操作上,它会与删除第二个节点的操作有所不同。

但是我们设置了哨兵,那么删除第一个节点和删除第二个节点那么在操作上就一样了,不用做额外的判断。当然,插入节点的时候也一样。

有时候我们在操作数组的时候,也是可以设置一个哨兵的,把arr[0]作为哨兵。

例如,要判断两个相邻的元素是否相等时,设置了哨兵就不怕越界等问题了,可以直接arr[i] == arr[i-1]?了。不用怕i = 0时出现越界。

当然我这只是举一个例子,具体的应用还有很多,例如插入排序,环形链表等。

 

                                                                                  与递归有关的一些优化

 

  • 对于可以递归的问题考虑状态保存

当我们使用递归来解决一个问题的时候,容易产生重复去算同一个子问题,这个时候我们要考虑状态保存以防止重复计算。例如我随便举一个之前举过的问题

问题:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法?

这个问题用递归很好解决。假设f(n)表示n级台阶的总跳数法,则有f(n) = f(n-1)+f(n - 2)。

递归的结束条件是当0 <= n <= 2时, f(n) = n。因此我们可以很容易写出递归的代码:

 

    public int f(int n) {
        if (n <= 2) {
            return n;
        } else {
            return f(n - 1) + f(n - 2);
        }
    }

不过对于可以使用递归解决的问题,我们一定要考虑是否有很多重复计算。显然对于f(n) = f(n-1) + f(n-2)的递归,是有很多重复计算的。如:

就有很多重复计算了。这个时候我们要考虑状态保存。例如用hashMap来进行保存,当然用一个数组也是可以的,这个时候就像我们上面说的巧用数组下标了。

可以当arr[n] = 0时,表示n还没计算过,当arr[n] != 0时,表示f(n)已经计算过,这时就可以把计算过的值直接返回回去了。因此我们考虑用状态保存的做法代码如下:

 

//数组的大小根据具体情况来,由于int数组元素的的默认值是0
    //因此我们不用初始化
    int[] arr = new int[1000];
    public int f(int n) {
        if (n <= 2) {
            return n;
        } else {
            if (arr[n] != 0) {
                return arr[n];//已经计算过,直接返回
            } else {
                arr[n] = f(n-1) + f(n-2);
                return arr[n];
            }
        }
    }

这样,可以极大提高算法的效率。也有人把这种状态保存称之为备忘录法。

  • 考虑自底向上

对于递归的问题,我们一般都是从上往下递归的,直到递归到最底,再一层一层着把值返回。

不过,有时候当n比较大的时候,例如当n = 10000时,那么必须要往下递归10000层直到n <=2才将结果慢慢返回,如果n太大的话,可能栈空间会不够用。

对于这种情况,其实我们是可以考虑自底向上的做法的。例如我知道:

  • f(1)=1;

  • f(2)=2。

那么我们就可以推出f(3)= f(2)+f(1)=3。从而可以推出f(4),f(5)等直到f(n)。

因此,我们可以考虑使用自底向上的方法来做。

代码如下:

 

public int f(int n) {
        if(n <= 2)
            return n;

        int f1 = 1;
        int f2 = 2;
        int sum = 0;

        for (int i = 3; i <= n; i++) {
            sum = f1 + f2;
            f1 = f2;
            f2 = sum;
        }
        return sum;
    }

我们也把这种自底向上的做法称之为递推。

 

                                                                                            总结一下

 

当你在使用递归解决问题的时候,要考虑以下两个问题。

(1)是否有状态重复计算的,可不可以使用备忘录法来优化。

(2)是否可以采取递推的方法来自底向上做,减少一味递归的开销。

今天就先讲到这里,之后有时间再来多谢一些其他的。如果觉得不错,不妨点个赞。

且慢, 说到这里还没完, 我们不仅有知识技能的见解, 小编更是历经辛苦帮大家收集到一套关于算法一系列全套完整的视频讲解课程, 废话不多说,直接上干货 至于视频的含金量大家自己看了自然会知道, 部分资源截图如下:

 

 

 

 

(仅展示部分资源截图)

占用空间23G, 考虑到如果是压缩文件,大家还得需要下载下来才能观看, 所以

直接就是视频资源啦,大家保存到网盘后就可以直接观看了,就是这么任性!!! 有知识点的剖析, 更有配套视频资源讲解,大家还怕学不会吗? 保存到网盘后你们就可以反复观看了,总之关注小编公众号是绝对不会让大家失望的, 后续更多精彩视频文章陆续更新中ing...,赠人玫瑰,手留余香,希望大家分享给身边有需要的人,感谢大家的支持!大数据视频教程也在整理中, 后续会陆续和大家见面, 感谢大家的支持!

关注微信公众号 [ java大数据修炼之道 ]

在微信公众号后台回复关键字: 算法 获取上述全套视频讲解资源

 

注:本公众号纯属个人公众号!不存在任何培训机构招生信息!更不存在任何收费项目!纯属公益分享!为的就是不让大家走弯路能有一个明确的学习方向!请大家分享给有需要的人!赠人玫瑰,手有余香!

推荐往期作品

  ●  浅谈Java工程师的职业规划

  ●  Java学不进,没头绪?是不是没摸清阶段顺序?

  ●  面试系列String,StringBuffer,StringBuilder三者区别

  ●  Java日志性能那些事

  ●  学习Java需吃透这些基本概念

  ●  Java中的String为什么是不可变的?以及源码分析

  ●  BAT 大企内部面试题泄密

  ●  SpringBoot入门到高级视频资源全套,确定不领取下?

  ● 某平台3980元大数据/机器学习课程免费下载,仅此1次

  ●  java学习路线全攻略(资料、视频、源码、项目实战)

                                                                                          ·end·

                                                             —写文不易,你的转发就是对我最大的支持—


 

                                                                           推荐程序员必备微信号 

                                                                                         ▼

 

                                                                                  Java大数据修炼之道

                                                                            微信号: gh_9119f24d3793

 

推荐理由:

在这里,我们分享程序员相关技术,职场生活,行业热点资讯。不定期还会分享IT趣文和趣图、各种java大数据学习视频。这里属于我们程序员自己的生活,工作和娱乐空间。

 

                                                                          ▼长按下方↓↓↓二维码识别关注

                                                                                


 

这篇关于程序员如何巧妙学习算法技巧?(文末有彩蛋)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/732535

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]