0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

常用的算法技巧总结

算法与数据结构 来源:未知 作者:李倩 2018-11-07 10:38 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

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

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) 的递归,是有很多重复计算的。如

就有很多重复计算了。这个时候我们要考虑状态保存。例如用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). 是否可以采取递推的方法来自底向上做,减少一味递归的开销。

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

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 算法
    +关注

    关注

    23

    文章

    4761

    浏览量

    97154
  • 代码
    +关注

    关注

    30

    文章

    4941

    浏览量

    73156

原文标题:一些常用的算法技巧总结

文章出处:【微信号:TheAlgorithm,微信公众号:算法与数据结构】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    干货总结:室内定位技术的3大常用算法

    的定位模式。室内定位种类虽然 多 ,但是 室内定位 算法很多都是通用的,总结起来有下面 3 种常用算法。 一、 近邻法 最简单的方式,直接选定那个信号强度最大的 AP 的位置,定位结
    的头像 发表于 07-27 10:50 1.2w次阅读
    干货<b class='flag-5'>总结</b>:室内定位技术的3大<b class='flag-5'>常用</b><b class='flag-5'>算法</b>

    数字图像基本处理算法小结

    初学图像处理,在常用算法方面,二白实在是有些头疼。就在昨天,亲爱的小白师兄,对迷茫的二白伸出了援手,为二白送来了一大份干货——图像处理常用算法总结
    的头像 发表于 01-29 14:46 1679次阅读

    吴鉴鹰总结的单片机常用算法

    本帖最后由 吴鉴鹰 于 2014-9-28 13:32 编辑 吴鉴鹰总结的单片机常用算法QQ群1号:332164870【1000人群】(已满)QQ群2号:194314772【1000人群
    发表于 05-27 08:26

    单片机C语言常用算法PDF下载

    灰常好哈···值得拥有{:5:}资深工程师总结的单片机C语言常用算法.pdf 2015-7-25 13:57 上传 点击文件名下载附件 494.04 KB, 下载次数: 12
    发表于 07-19 04:30

    单片机C语言常用算法

    【实用】资深工程师总结的单片机C语言常用算法,希望能帮助到大家!
    发表于 04-10 09:06

    嵌入式单片机程序架构之时间片轮询法

    littlevGLhttps://mp.weixin.qq.com/s/0Ym65yv4w7UEq72QvTDK3w3、单片机常用算法总结https://mp.weixin.qq.com/s/OU...
    发表于 12-20 06:13

    常用贴片电阻阻值总结

    本人是关于常用贴片电阻阻值总结的资料,有需要的可自行下载。
    发表于 07-03 17:32 0次下载
    <b class='flag-5'>常用</b>贴片电阻阻值<b class='flag-5'>总结</b>

    路由器常用基础知识总结

    路由器常用基础知识总结路由器常用基础知识总结路由器常用基础知识总结
    发表于 10-30 18:08 0次下载

    高手总结java常用API(免费下载)

    高手总结java常用API(免费下载)。
    发表于 11-06 11:27 0次下载

    MATLAB常用函数总结(表格)

    MATLAB常用函数总结,MATLAB函数速查手册,方便应用MATLAB函数
    发表于 01-21 14:31 0次下载

    redis常用命令总结

    本文是对redis常用命令总结
    发表于 02-09 11:25 1810次阅读

    Adaboost算法总结

    集成学习的Boosting算法通过结合多个弱学习器组成强学习器,AdaBoost算法是Boosting算法中的一种,本文详细的总结了AdaBoost
    的头像 发表于 12-29 16:08 3465次阅读
    Adaboost<b class='flag-5'>算法</b><b class='flag-5'>总结</b>

    机器学习算法常用指标汇总

    机器学习性能评价标准是模型优化的前提,在设计机器学习算法过程中,不同的问题需要用到不同的评价标准,本文对机器学习算法常用指标进行了总结
    的头像 发表于 02-13 15:09 5809次阅读
    机器学习<b class='flag-5'>算法</b><b class='flag-5'>常用</b>指标汇总

    vc++-CDC常用函数总结

    vc++-CDC常用函数总结(电源技术论坛)-该文档为vc++-CDC常用函数总结讲解文档,是一份还算不错的参考文档,感兴趣的可以下载看看,,,,,,,,,,,,,,,,,
    发表于 09-27 15:40 2次下载
    vc++-CDC<b class='flag-5'>常用</b>函数<b class='flag-5'>总结</b>

    图像处理常用算法总结

    同图像灰度不同,边界处一般会有明显的边缘,利用此特征可以分割图像。需要说明的是:边缘和物体间的边界并不等同,边缘指的是图像中像素的值有突变的地方,而物体间的边界指的是现实场景中的存在于物体之间的边界。有可能有边缘的地方并非边界,也有可能边界的地方并无边缘,因为现实世界中的物体是三维的,而图像只具有二维信息,从三维到二维的投影成像不可避免的会丢失一部分信息;另外,成像过程中的光照和噪声也是不可避免的重要因素。正是因为这些原因,基于边缘的图像分割仍然是当前图像研究中的世界级难题,目前研究者正在试图在边缘提取中加入高层的语义信息。
    的头像 发表于 04-27 11:10 1775次阅读
    图像处理<b class='flag-5'>常用</b><b class='flag-5'>算法</b><b class='flag-5'>总结</b>