常见算法思路及技巧总结一
常见算法及技巧总结一
- 1.前言
- 2.整数运算
- 技巧一:类快速幂的加法
- 技巧二:判断二进制数中'1'的位数
- 技巧三:利用位与运算判断字符串中相同的字母
- 技巧四:使用异或运算
- 3.数组
- 技巧一:有序数组的二分法
- 技巧二:结合双指针的滑动窗口
- 技巧三:先排序再运算
- 技巧四:前缀和
- 4.字符串
- 技巧一:结合双指针的滑动窗口
- 技巧二:使用数组作为哈希表保存字符串各字母数量
- 技巧三:使用标识标识字符串
- 5.链表
- 技巧一:快慢指针
- 技巧二:递归遍历链表
- 6.单调栈
- 思路
- 单调栈模板
- 7.二分查找
- 思路一:有序数组
- 思路二:遍历某个范围内的数据找到符合条件的一个值
- 二分查找模板
- 技巧一:Z字形查找
1.前言
这篇算是我自己对《剑指offter》的总结吧,外加一些其他的算法。
2.整数运算
关于整数运算,常涉及的知识点有整数溢出的处理,位运算,十进制与二进制。
技巧一:类快速幂的加法
问题:计算两数相除的商,但不能使用乘法与除法或者取余。
可以使用类似于快速幂的加法技巧。
举个例子16除以2,暴力思路是用2累加并计算累加次数,直至累加和大于或等于16,计算次数为8次。
使用类似于快速幂的加法则为:2加2等于4,此时累加次数为2 。再使用4进行加法计算4加4等于8,注意此时累加次数为2加2等于4,最后8加8为16,累加次数为4加4等于8 ,计算次数为3次。计算结束16除以2的商为8。
力扣链接:
两数相除
技巧二:判断二进制数中’1’的位数
利用 x=x&(x-1) 。 具体原理为,先把数字二进制的最后一位’1’变为0,如果此时数字数值不为0,则继续下去,直至数字数值为0,在此过程的同时保存’1’的位数。
具体代码:
int main(void) {int tmp = 16;int count = 0;while (tmp) {tmp = tmp & (tmp - 1);count++;}cout << "tmp二进制中'1'的位数为" << count << endl;system("pause");return 0;
}
其实很好理解,一个数二进制中只含有一个1,如 0000 1000 。那么它减一为 0000 0111 。
0000 1000
0000 0111
再进行位与运算为0 。
技巧三:利用位与运算判断字符串中相同的字母
问题:如果有很多字符串(只包含小写字母),那么在比较其中两个字符串时如何确定它们之中存在相同的字母。
可以利用32位整数的掩码表示整个字符串。比如如果一个字符串中有a字母,那么表示该字符串的整数的掩码的第一位则置为1,如果还有c字母,则第三位也置为1,以此类推。不存在的字母掩码位都置为0。这样把所有字符串都用一个整数表示后,它们在两两判断是否存在相同字母时只需要这两个整数进行位与运算,如果结果不为0,则存在相同字母,为0,则不存在相同字母。
力扣链接:
最大单词长度乘积
技巧四:使用异或运算
首先明确异或运算的一些运算性质:
摘自力扣:
- 任何数与0做异或运算结果都是它本身。
- 任何数与自身做异或运算结果都是0。
- 异或运算满足交换律与结合律。
力扣链接:
只出现一次的数字
3.数组
在处理数组时,常能用到的思路与技巧有有序数组的二分法,双指针法,滑动窗口,前缀和
技巧一:有序数组的二分法
对于已经有序的数组,查找某个数,最先应该想到的可能也是效率最高的方法就是二分法。
技巧二:结合双指针的滑动窗口
对于一个已经存在的数组,如果题目要你求满足条件的子数组或子数组长度(注意子数组与子序列的区别,子数组需连续,子序列不需要是连续的),可以考虑滑动窗口结合双指针去做。
力扣链接:
长度最小的子数组
一般思路是,利用双指针维护一个滑动窗口,移动滑动窗口遍历整个数组,在移动的过程中根据题目要求扩张或者缩短滑动窗口。直至遍历完数组,返回滑动窗口或者滑动窗口大小。
关于滑动窗口,有一种思路是对于确定的左窗口或者右窗口,是否能找到唯一确定的满足题目要求的右窗口或左窗口。以数组中的每一个元素作为左窗口,找到所有符合条件的右窗口,遍历完数组,就找到了所有的解。同理以右窗口开始也是一样的。
关于滑动窗口,还有一种经典题型是看题目中是否出现了最大或最长等词。如果有,可能只需要一开始确定一个符合条件的滑动窗口,在遍历所有数据的过程中单调的扩增滑动窗口,直至遍历完所有数据,就得到了符合条件的最大的数据集合。
如果是求最小或最短的数据集合或数据集合长度,则可能是在一开始先确定一个符合条件的,再依次找出所有符合条件的同时保存最小的那个数据集合,直至遍历完整个数据集合,就得到了最小或最短的。
关于滑动窗口与双指针的应用情景有很多,这里只是说了大致的思路,还有很多不同的情况,需要具体问题具体分析。
技巧三:先排序再运算
对于暴力解法需要遍历数组中每一个数,让这个数与数组中其他数依次比较的情况。先排序可以极大的提高效率。
看这题: 最小时间差
技巧四:前缀和
当题目中要求在某些限制条件下求一个数据集合的和或者题目要求可以转换成求数据集合的和时,可以考虑使用前缀和。
前缀和的核心在于使用哈希表分别保存数据集合第一个数到之后各个数的和,然后通过连续作差来得出中间连续子数组的和。
举个例子,有一个数组,其中元素分别为1,3,5,6,8,10 。分别以第一个元素到各个元素的和作为哈希表的键值,并分别设置其哈希值为1 。
为
【1,1】,
【1+3,1】,
【1+3+5,1】,
【1+3+5+6,1】,
【1+3+5+6+8,1】,
【1+3+5+6+8+10,1】 ,即
【1,1】,
【4,1】,
【9,1】,
【15,1】,
【23,1】,
【33,1】,
现在要求该数组中是否存在一个连续的子数组,使得和为24 。我们可以使用各键值分别减去24,如果有一个哈希值为1 。则存在一个连续的子数组和为24 。最后我们可以发现使用33-24为9,以9为键值的哈希值为1(33为所有元素的和。) 。
其含义是什么呢?即该数组第一个元素到最后一个元素的和减去24为9,以9为键值的哈希值为1,说明存在从第一个元素到第n个元素的和为9,也说明存在从第n个元素到最后一个元素的和为24。所以原数组中存在一个连续子数组的和为24,即【6,8,10】。
力扣链接:
和为k的子数组
4.字符串
常涉及的知识点有,字符串匹配问题,双指针法,滑动窗口,
关于字符串匹配问题,一般有两种情况,一种是匹配排列。即有两个字符串,一个更长,要在更长的字符串中找到更短字符串的一种排列(字符串的排列是指把原字符串字母顺序打乱重新组成的新的字符串)。关于这个问题,可以看这题: 字符串的排列
还有一种是严格匹配的(即短字符串不能变化),即在更长字符串中找到更短字符串。这种一般是用KMP算法。
技巧一:结合双指针的滑动窗口
不仅在处理数组时能用到这种方法,在处理字符串时也能用到。
技巧二:使用数组作为哈希表保存字符串各字母数量
如果字符串中只存在小写字母,则只需要定义一个大小为26的数组,使用字母的ascll码值作为键值同时作为数组的下标,而数组值表示的是该字母在字符串中出现的次数。
看下面代码:
int main(void) {string str = "helloworld";int map[26] = { 0 }; //作为哈希表的数组for (auto& ch : str) {++map[ch - 'a']; //字母计数加一}for (int count = 0; count < 26; ++count) { //输出字符串中每个字母的个数cout << (char)(count+'a') << "=" << map[count] << endl;}system("pause");return 0;
}
注意:如果字符串中含有所有字符,那就把数组大小改为128 。
技巧三:使用标识标识字符串
以字符串作为哈希表的键值时,未必一定要直接以字符串作为键值,换一种思路。用一个可以唯一标识字符串的标记也能代替字符串作为键值。(本质上就是哈希)
在这里有两种不同的情况,一是字符串所含字母及字母数量都相同的两个字符串使用相同的标识,如abc与cba就是所含字母及字母数量都相同,所以它们的标识是一样的。
一:这种情况可以使用上一个技巧提到的使用数组作为哈希表的方法。
二:也可以使用乘积和的方法,即不同的字母使用不同的数字表示,而整个字符串就用这些数字的乘积和表示。不过这种方法可能要考虑哈希冲突与整数溢出的问题。
如果只是为了判断两个字符串是否含有相同字母及字母数量的话,只需要将字符串排序后再比较是否相同就行了。关于这个问题,可以看这题:字母异位词分组
还有一种情况是严格匹配的,即必须字符串所含字母,字母数量,字母顺序都相同才能使用相同标识。如上文的abc与cba在这种情况下就不能使用相同标识。
关于这个问题,目前我只知道一种方法。即不同字母用不同数字表示,字母的顺序用进位来表示。
举个例子,如a b c d e f 分别用数字1 2 3 4 5 6 表示。则字符串abcde用数字12345表示。字符串fabe用数字6125表示。
这种方法在某种情况下能极大的提高效率,感兴趣的可以看我这篇博客: 对于Rabin-Karp算法的一些理解
5.链表
链表作为一种最基本数据结构,掌握链表要求我们能够独立实现一个具有完善功能的链表,这个完善的功能包括链表的创建,增加元素,插入元素,删除元素,按顺序输出元素。
技巧一:快慢指针
即使用两个指针,一个在循环中一次向链表尾走一步,另一个一次走两步。直至其中一个走到链表尾为止。
常用来判断链表是否成环,或用于取链表中点。
力扣链接:
环形链表
技巧二:递归遍历链表
关于递归遍历链表值得一提的一点是可以使用一个外部指针,当递归至链表尾开始回升时,外部指针同时往下遍历。这样处理链表问题时可以两路进行,即递归的逆序遍历与外部指针的正向遍历。在某些情况下很好用。
力扣链接:回文链表
6.单调栈
思路
关于单调栈,难的不在于如何使用单调栈,而在于判断题目是否能使用单调栈来优化。
如果题目要求需要找到数组中每一个元素第一个大于或小于当前元素的数。从暴力思路出发当然是对于每一个元素从当前位置出发,直至找到第一个大于或小于当前元素的数。但这种情况往往能使用单调栈优化。
力扣链接:
每日温度
柱状图中最大矩形
接雨水
单调栈模板
单调栈里可以存数组值,也可以存数组值的下标。具体存什么看题目要求,涉及滑动窗口的一般就存数组值的下标。
stack<int> sta;for(int count=0;count<k;++count){while(sta.size() && nums[count]>=nums[sta.top()]){nums[sta.top()]=count;sta.pop();}sta.push(count);}
7.二分查找
二分查找的代码实现也不难,关键是要判断什么时候可以使用二分查找,这里提供两种思路。
思路一:有序数组
对于有序的数组,查找某个数,首先考虑二分查找。
思路二:遍历某个范围内的数据找到符合条件的一个值
如果需要从一个限定的范围(这个范围是连续的)内找到一个符合条件的值,暴力思路是只能一个一个试看符不符合条件,那么可能可以使用二分查找优化。
可以看看这题:爱吃香蕉的珂珂
二分查找模板
while (l < r) {int mid = l + (l + r) / 2; //取中值if (nums[mid] < target ) l = mid + 1; //往左边找else if(nums[mid] == target ) return left; //找到了要找的元素,返回else r = mid; //往右边找
}
或者这种形式
while (l < = r) { //不同int mid = l + (l + r) / 2; //取中值if (nums[mid] < target ) l = mid + 1; //往左边找else if(nums[mid] == target ) return left; //找到了要找的元素,返回else r = mid - 1; //往右边找 不同
}
技巧一:Z字形查找
对于一个二维数组,如果它的行与列的元素都是按升序或者降序排列的。那么在这样的数组中查找一个元素,可以使用 Z 字形查找。以充分利用行与列都是有序的特性。
给个力扣链接:搜索二维矩阵 ||
常见算法思路及技巧总结一相关推荐
- 常见算法基础题思路简析(六)-字符串篇
2019独角兽企业重金招聘Python工程师标准>>> 常见算法基础题思路简析(六)-字符串篇 标签: algorithms [TOC] 本文对和 字符串 有关的常见算法基础题思路分 ...
- 语言高精度算法阶乘_JavaScript中的算法(附10道面试常见算法题解决方法和思路)...
https://juejin.im/post/6844903811505455118 Introduction 面试过程通常从最初的电话面试开始,然后是现场面试,检查编程技能和文化契合度.几乎毫无例外 ...
- 常见算法之Flood Fill算法
常见算法之Flood Fill算法 算法介绍 基本作用:寻找连通块 基本方法:BFS搜索 适用题目:需要找出分类块的题目/一些聚类问题 顾名思义,Flood Fill算法就是像洪水泛滥一样去寻找周围符 ...
- 实际项目中的常见算法
实际项目中的常见算法 作者 水羽哲 发布于 十一月 30, 2013 | 讨论 [编者按]本文原始内容来源于stackexchange,遵循cc-wiki协议: 近日Emanuele Viola在St ...
- 推荐系统[二]:召回算法超详细讲解[召回模型演化过程、召回模型主流常见算法(DeepMF/TDM/Airbnb Embedding/Item2vec等)、召回路径简介、多路召回融合]
搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排精排重排混排).系统架构.常见问题.算法项目实战总结.技术细节以及项目实战(含码源) 专栏详细介绍:搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排 ...
- 【数据结构Note5】- 树和二叉树(知识点超细大全-涵盖常见算法 排序二叉树 线索二叉树 平衡二叉树 哈夫曼树)
文章目录 5.1 树和二叉树引入 5.1.1 树的概念 5.1.2 树的表示 5.1.3 树中基本术语 5.2 二叉树 5.2.1 概念 5.2.2 二叉树的性质 5.2.3 特殊的二叉树 5.2.4 ...
- 互联网大厂有哪些分库分表的思路和技巧?
分库分表 分库分表是随着业务的不断发展,单库单表无法承载整体的数据存储时,采取的一种将整体数据分散存储到不同服务器上的不同数据库中的不同数据表的存储方案.分库分表能够有效的缓解数据的存储压力,分库分表 ...
- abaqus python二次开发攻略_Abaqus有限元分析常见问题解答与实用技巧 12天后上架...
Abaqus有限元分析常见问题解答与实用技巧已印刷完毕,1-2天后上架,先睹为快.现在某些网站上的售卖信息,不靠谱.温馨提示:封底无防伪标均为盗版! 序 言 Abaqus是是国际上先进的大型通用非线 ...
- etc的常见算法_(转)8种常见机器学习算法比较
机器学习算法太多了,分类.回归.聚类.推荐.图像识别领域等等,要想找到一个合适算法真的不容易,所以在实际应用中,我们一般都是采用启发式学习方式来实验.通常最开始我们都会选择大家普遍认同的算法,诸如SV ...
最新文章
- mysql种编译码写在哪_深入理解Java虚拟机(程序编译与代码优化)
- PCL1.8.0+VS2013+Win10 x64的配置教程
- 局域网计算机维护工具,教你用“小浣熊局域网维护小工具”,从此解脱烦恼!...
- oppo面经-java开发
- Node接口也定义了一些所有节点类型都包含的特性和方法
- python叮当猫代码_详细介绍一个利用html+css实现叮当猫的实例代码
- 队列的基本概念介绍以及典型应用示例
- Linux上的errno和strerror
- Spring之Bean后处理器——InstantiationAwareBeanPostProcessor的使用与源码解析
- 一句话证明你在阿里待过!(这才是最真实的阿里)
- matlab画立体星星教程,抖音星空画的人怎么画 制作教程完整视频步骤分享
- Nett源码剖析ServerBootstrap的设置2021SC@SDUSC
- 【面经】陌陌-2017年8月28日,散招实习生
- Qt编写地图综合应用57-跨平台(win、linux、mac、uos、kylin等)
- CRM系统和OA系统是否可以共用一个系统,如何操作?
- 小米(绿米联创)39元 无线开关破解(NXP JN5169 zigbee 3.0开发实战)
- ov7670 linux源码,STM32 迷你照相机 OV7670 摄像头 - 源码下载|嵌入式/单片机编程|微处理器(ARM/PowerPC等)|源代码 - 源码中国...
- c语言算数运算,C语言:算数运算符
- IBM最好的办法不应该是卖掉PC - 我对联想收购IBM的看法。
- 使用axios发送请求(不带参数和带参数)和使用mockjs
热门文章
- 编程中大量数据的优化技巧
- 君正Zeratul开发(2)——uboot启动分析
- 文言编程语言/wenyan-lang
- RK3568+鸿蒙教学实验平台鸿蒙认证设计方案
- 不禁网页的浏览器_明明浏览“不良网站”危险,为啥手机浏览器却不强制禁止访问?...
- 北邮CSAPP第三章之数据格式与程序编码
- uni-id入门(三)---初始化uni-id(创建uni-id实例)
- 生产物料计划部的岗位工作职责(参考用)
- IDEA控制台换行方法
- 基于群成员贡献分的群推荐系统