让你可以装逼的算法技巧总结
今天和大家讲讲,在做算法题时常用的一些技巧。对于平时没用过这些技巧的人,或许你可以考虑试着去看看在实践中能否用的上这些技巧来优化问题的解。
1. 巧用数组下标
数组的下标是一个隐含的很有用的数组,特别是在统计一些数字,或者判断一些整型数是否出现过的时候。例如,给你一串字母,让你判断这些字母出现的次数时,我们就可以把这些字母作为下标,在遍历的时候,如果字母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);}}}
利用数组下标的应用还有很多,大家以后在遇到某些题的时候可以考虑是否可以巧用数组下标来优化。
2. 巧用取余
有时候我们在遍历数组的时候,会进行越界判断,如果下标差不多要越界了,我们就把它置为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;
}
3. 巧用双指针
对于双指针,在做关于单链表的题是特别有用,比如“判断单链表是否有环”、“如何一次遍历就找到链表中间位置节点”、“单链表中倒数第 k 个节点”等问题。对于这种问题,我们就可以使用双指针了,会方便很多。我顺便说下这三个问题怎么用双指针解决吧。
例如对于第一个问题
我们就可以设置一个慢指针和一个快指针来遍历这个链表。慢指针一次移动一个节点,而快指针一次移动两个节点,如果该链表没有环,则快指针会先遍历完这个表,如果有环,则快指针会在第二次遍历时和慢指针相遇。
对于第二个问题
一样是设置一个快指针和慢指针。慢的一次移动一个节点,而快的两个。在遍历链表的时候,当快指针遍历完成时,慢指针刚好达到中点。
对于第三个问题
设置两个指针,其中一个指针先移动k个节点。之后两个指针以相同速度移动。当那个先移动的指针遍历完成的时候,第二个指针正好处于倒数第k个节点。
你看,采用双指针方便多了吧。所以以后在处理与链表相关的一些问题的时候,可以考虑双指针哦。
4. 巧用移位运算。
有时候我们在进行除数或乘数运算的时候,例如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();
)
具体的一些运算技巧,还得需要你们多在实践中尝试着去使用,这样用久后就会比较熟练了。
5. 设置哨兵位
在链表的相关问题中,我们经常会设置一个头指针,而且这个头指针是不存任何有效数据的,只是为了操作方便,这个头指针我们就可以称之为哨兵位了。
例如我们要删除头第一个节点是时候,如果没有设置一个哨兵位,那么在操作上,它会与删除第二个节点的操作有所不同。但是我们设置了哨兵,那么删除第一个节点和删除第二个节点那么在操作上就一样了,不用做额外的判断。当然,插入节点的时候也一样。
有时候我们在操作数组的时候,也是可以设置一个哨兵的,把arr[0]作为哨兵。例如,要判断两个相邻的元素是否相等时,设置了哨兵就不怕越界等问题了,可以直接arr[i] == arr[i-1]?了。不用怕i = 0时出现越界。
当然我这只是举一个例子,具体的应用还有很多,例如插入排序,环形链表等。
6. 与递归有关的一些优化
(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) 的递归,是有很多重复计算的。如
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y27eRe46-1581345309177)(http://pizj3hugp.bkt.clouddn.com/FovVyDC38Z1MmG9BUUDpoiF6g3O3)]
就有很多重复计算了。这个时候我们要考虑状态保存。例如用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). 是否可以采取递推的方法来自底向上做,减少一味递归的开销。
兄dei,如果觉得我写的不错,不妨帮个忙
1、关注我的原创微信公众号「帅地玩编程」,每天准时推送干货技术文章,专注于写算法 + 计算机基础知识(计算机网络+ 操作系统+数据库+Linux),听说关注了的不优秀也会变得优秀哦。
2、给俺点个赞呗,可以让更多的人看到这篇文章,顺便激励下我,嘻嘻。
作者简洁
作者:大家好,我是帅地,从大学、自学一路走来,深知算法,计算机基础知识的重要性,所以申请了一个微星公众号『帅地玩编程』,专业于写这些底层知识,提升我们的内功,帅地期待你的关注,和我一起学习。 转载说明:未获得授权,禁止转载
让你可以装逼的算法技巧总结相关推荐
- 【带你装逼带你飞】吐血总结了这五大常用算法技巧,让你在同事/面试官面前惊艳全场!
对于算法技巧,之前的文章也写过一些算法技巧,不过相对零散一些,今天我把之前的很多文章总结了下,并且通过增删查改,给大家总结一些常用的算法解题技巧,当然,这些也不是多牛逼的技巧,不过可以让你的代码看起来 ...
- 【算法技巧】位运算装逼指南
位算法的效率有多快我就不说,不信你可以去用 10 亿个数据模拟一下,今天给大家讲一讲位运算的一些经典例子.不过,最重要的不是看懂了这些例子就好,而是要在以后多去运用位运算这些技巧,当然,采用位运算,也 ...
- 【技巧总结】位运算装逼指南
位算法的效率有多快我就不说,不信你可以去用 10 亿个数据模拟一下,今天给大家讲一讲位运算的一些经典例子.不过,最重要的不是看懂了这些例子就好,而是要在以后多去运用位运算这些技巧,当然,采用位运算,也 ...
- CAD的那些装逼技巧!
作者:活力网柏林 版权声明: 此文章由活力网柏林编写,转载请务必在文章开头附上作者以及原文链接,否则将追究责任! Hi,大家好!我是活力网柏林,今天给大家分享一些CAD的装逼技巧. 当我们看到同事或者 ...
- 计算机病毒装逼桌面,3个Win10神秘装逼小技巧
电脑很多人每天都在用,不少人也觉得自己对于电脑非常的熟悉.但电脑中还是隐藏着一些非常实用的小技巧,这些鲜为人知的功能不但能提高你的工作效率,还能让你在好友面前小小的装一把哦.今天小编针对Win10电脑 ...
- 前端装逼技巧 108 式(一)—— 打工人
你在拼多多到处找人砍价,他在滴滴打车求人助力,我在电子厂拧螺丝拧到凌晨,我们都有光明的未来!早安,打工人! 楔子 作为一名拥有钢铁般意志的前端打工人,装逼是不可能的,这辈子都不可能装逼.如果真要装逼, ...
- html按钮超链接到微信,技巧|教你给微信聊天文字加上超链接,装逼小技能!...
Ladys and 乡亲们 你们好! 首先科普一个小知识 什么是超链接 小知识 超链接简单的说就是一座搭建在不同网络内容之间的一座桥.整个网络是一个共享性网络.有了超链接,我们可以从一段文字.图片跳转 ...
- 我的世界服务器展示自定义图片,我的世界装逼小技巧 怎么在自制地图中显示自己的头像...
我的世界装逼小技巧 怎么在自制地图中显示自己的头像.那下面给大家分享的是我的世界玩家教你怎么在你的地图中显示的是你的头像,希望大家喜欢. 游戏园我的世界官方群:325049520或256070479欢 ...
- html中微信添加好友超链接,技巧|教你给微信聊天文字加上超链接,装逼小技能!...
Ladys and 乡亲们 你们好! 首先科普一个小知识 什么是超链接 小知识 超链接简单的说就是一座搭建在不同网络内容之间的一座桥.整个网络是一个共享性网络.有了超链接,我们可以从一段文字.图片跳转 ...
最新文章
- centos 7.6安装java_Docker安装zabbix5.0LTS教程和优化
- python a any_Python any() 函数
- Win7环境下VS2010配置Cocos2d-x-2.1.4最新版本的开发环境(亲测)
- 安检x光机原理计算机实现,安检x光机成像原理介绍
- 数据库升级后,准备使用原有数据文件启动数据库
- HDU - 4687 Boke and Tsukkomi(一般图最大匹配-带花图)
- 编写一个简单的spring MVC程序
- java 图片阴影_Java 为 PPT 中的图形添加阴影效果
- Redis系列教程(三):如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
- IntelliJ IDEA 如何导出安卓(Android)apk文件 详细教程
- easyui 扩展loading
- 国密算法分类及介绍(sm系列密码算法)
- 需求开发应用部署“一条龙”,平安云如何加速容器场景落地
- 【内网安全-通讯上线】通讯上线基础知识
- tiny6410 适用于win7 64bit的dnw 的USB下载驱动
- 【CSPJ】CSPJ小学组
- lemming games 1!! hdlbits
- 黑群晖安装Transmission和emby搭建家庭影音娱乐中心服务器(上)
- 腾讯要建超级大脑,用三张网实现AI in All万物互联
- STM32 IAP 在线升级详解
热门文章
- 华创期货:期货IPO第一股的到来推动行业做大规避骗局亏损
- webug 2、布尔注入
- 使用MNIST数据集并显示一些图片
- iOS Core Animation + Foundation + UIKit
- 趣来宝机器人_王嘉尔上演真人版智能机器人舞台帅炸,但都不及他的说话艺术炸...
- C语言 日程管理系统
- 人工客服 计算机英语怎么说,客服用英语怎么说
- 电能计量自动化系统在用电管理上的应用
- Visual Studio2017自动生成的#include“stdafx.h”详解及解决方案
- html文字两端对齐 w3school,CSS实现两端对齐效果