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

一、巧用数组下标

数组的下标是一个隐含的很有用的数组,特别是在统计一些数字,或者判断一些整型数是否出现过的时候。例如,给你一串字母,让你判断这些字母出现的次数时,我们就可以把这些字母作为下标,在遍历的时候,如果字母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.对于可以递归的问题考虑状态保存

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

问题:一只青蛙一次可以跳上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];}}
}
复制代码

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

2.考虑自底向上

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

不过,有时候当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). 是否可以采取递推的方法来自底向上做,减少一味递归的开销。

欢迎大家加入粉丝交流群:963944895,群内免费分享Spring框架、Mybatis框架SpringBoot框架、SpringMVC框架、SpringCloud微服务、Dubbo框架、Redis缓存、RabbitMq消息、JVM调优、Tomcat容器、MySQL数据库教学视频及架构学习思维导图

写在最后:

秃顶程序员的不易,看到这里,点了关注吧! 点关注,不迷路,持续更新!!!

转载于:https://juejin.im/post/5c80d83b51882542704dccc7

【本人秃顶程序员】分享一些数据结构与算法常用的算法技巧总结相关推荐

  1. 【本人秃顶程序员】高级 Java 必须突破的 10 个知识点!

    ←←←←←←←←←←←← 快!点关注!!! 工作多少年了,还在传统公司写if / for 等简单的代码?那你就真的要被社会淘汰了,工作多年其实你与初级工程师又有多少区别呢?那么作为一个高级Java攻城 ...

  2. 【本人秃顶程序员】Redis 这么火,它都解决了哪些问题?

    ←←←←←←←←←←←← 快!点关注 先看一下Redis是一个什么东西.官方简介解释到: Redis是一个基于BSD开源的项目,是一个把结构化的数据放在内存中的一个存储系统,你可以把它作为数据库,缓存 ...

  3. 【本人秃顶程序员】我简历上的Java项目都好low,怎么办?

    ←←←←←←←←←←←← 快!点关注 这篇文章我们来聊一聊,在系统设计和项目经验这两块,应该如何充分的准备,才能拿出有技术含量的项目经验战胜跟你同台竞技的其他工程师,征服你的面试官,收获各种心仪的of ...

  4. 【本人秃顶程序员】使用Spring Cloud Stream和RabbitMQ实现事件驱动的微服务

    ←←←←←←←←←←←← 快!点关注 让我们展示如何使用Spring Cloud Stream来设计事件驱动的微服务.首先,Spring Cloud Stream首先有什么好处?因为Spring AM ...

  5. java程序员秃顶,【本人秃顶程序员】使用Azure Function + Cognitive Services 实现图片自动化审核...

    ←←←←←←←←←←←← 快!点关注 假定我们正在运行某个应用程序,此应用程序需要用户在应用程序中提交大量图片文件,那么对于系统管理员来说手动审核这些图片是很消耗时间的,并且对于图片的审核也许并不是即 ...

  6. 【本人秃顶程序员】美女程序员观点:程序员最重要的非编程技巧

    ←←←←←←←←←←←← 快!点关注 这是来自一位美女程序员Ali Spittel的观点,至少可以看看美女喜欢和怎样的男程序猿打交道: 当我想与我希望的程序员合作时,我更多地考虑非技术技能,而不是技术 ...

  7. 【本人秃顶程序员】图解分布式架构的演进过程

    ←←←←←←←←←←←← 快!点关注!!! 一.什么是分布式架构 分布式系统(distributed system) 是建立在网络之上的软件系统. 内聚性: 是指每一个数据库分布节点高度自治,有本地的 ...

  8. 【本人秃顶程序员】求求你别再写 bug 了,秃顶程序员告诉你避免空指针的 5 个案例!

    ←←←←←←←←←←←← 快!点关注 空指针是我们 Java 开发人员经常遇到的一个基本异常,这是一个极其普遍但似乎又无法根治的问题. 本文,栈长将带你了解什么是空指针,还有如何有效的避免空指针. 什 ...

  9. 【本人秃顶程序员】中小型互联网公司微服务实践-经验和教训

    ←←←←←←←←←←←← 我都秃顶了,还不点关注! 在开始之前我们先介绍一下几个概念,什么是微服务,它的特点是什么? Spring Boot/Cloud都做了那些事情?他们三者之间又有什么联系? 技术 ...

最新文章

  1. ★核心关注点_《信息系统项目管理师考试考点分析与真题详解》
  2. 用WORD2010写博客,并发送至博客园
  3. 4、C语言面试笔试--内存操作-指针
  4. Python:进阶操作(1)
  5. 那些有趣的电子漫画合集
  6. 如何把WAV格式音频转换为MP3格式
  7. C++运行界面一闪而过解决
  8. linux nmon 进程io,linux监控占用磁盘IO进程的工具
  9. 神了,一次解决Java所有痛难点!
  10. 写一个function,清除字符串前后的空格。(兼容所有浏览器)
  11. 解决eclipse中git中cannot open git-upload-pack(无法打开Git上传包)问题
  12. Java语言中:switch语句经典习题
  13. jflash添加芯片_【原创】巧用J-Link+J-Flash给Kinesis烧写序列号
  14. 这里有几位数据分析方向的大佬
  15. ZipEntry的使用
  16. ALFA | 临床基因组课程 人群频率数据库 新成员
  17. grep的常用和次常用选项
  18. YOLOX: Exceeding YOLO Series in 2021
  19. 安装应用提示 该文件包与具有同一名称的现有文件包存在冲突。 无法安装
  20. solr mysql 导入命令_Solr 07 - Solr从MySQL数据库中导入数据 (Solr DIH的使用示例)

热门文章

  1. 阿里云服务器型号大全及机型如何选择(建议收藏)
  2. Codeforces Round #807 (Div. 2)A~E个人题解
  3. 2021年中国城市财力半年报 Top 30
  4. 手机网页图片自适应大小 background-size css 图片全屏 背景尺寸设置
  5. Flowable入门系列文章47 - 电子邮件任务
  6. 论文阅读:《POI: Multiple Object Tracking with High Performance Detection and Appearance Feature 》
  7. 91 Three.js Texture纹理属性详解
  8. 计算机毕设之 餐厅点餐app
  9. 《堂食点餐》APP代码前后端全部免费开源啦!
  10. 学习数据库视频笔记4