offer刷题分类汇总
数学运算
位运算的总结

非(NOT)操作。NOT
a结果为 a 的反转(即反码)<< (左移):类似于进位,抛弃高位向左,低位补0
>> (有符号右移)
>>> (无符号右移)
注意:
- 左移相当于在乘以2倍,右移====除以2
不用加减乘除做加法
题目:
- 写一个函数,求两个整数之和,要求在函数体内不得使用
+、-、*、/四则运算符号。
思路:
- 将加法拆解成三步:
- 1.不进位相加
- 2.计算进位
- 3.进位与不进位结果进行相加
- 重复这三步,直到进位值为0
使用位运算来计算二进制:
- 二进制异或操作和不进位相加得到的结果相同
(1^1=0 0^1=1 0^0=0) - 二进制与操作后左移和进位结果相同
(1&1=1 1&0=0 0&0=0)
数组
,用于按顺序存储元素的集合。但是元素可以随机存取,因为数组中的每个元素都可以通过数组索引来识别。插入和删除时要移动后续元素,还要考虑扩容问题,插入慢。
1 双指针
- 上面链表中提到的一类题目,主要是利用两个或多个不同位置的指针,通过速度和方向的变换解决问题。注意这种技巧经常在排序数组中使用。
1.1 和为S的两个数字
题目
- 输入一个递增排序的数组和一个数字
S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
思路
- 设置两个索引标志left,right,分别从左和右开始靠近,
- 如果s>sum right–
- s<sum left++
- 返回当前的节点
1 | function FindNumbersWithSum(array, sum) |
1.2 调整数组的顺序时奇数在前面,偶数在后面
题目
- 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分
思路
- 分别保存起来
- 在拼接
1 | function reOrderArray(array) |
1.3 和为s的连续正整数序列
题目
输入一个正数S,打印出所有和为S的连续正数序列。
例如:输入15,有序1+2+3+4+5 = 4+5+6 = 7+8 = 15 所以打印出3个连续序列1-5,5-6和7-8
思路
- 双指针法
- 先定义两个指针,start = 1,end=2(有序的数组)
- 判断条件start<end
- curSum>sum->start++,反之end++
- 相等的情况下,将start到end的值保存到输出数组序列中,

1 | function FindContinuousSequence(sum) |
2 数组之和
2.1 两数之和
题目
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
思路
使用一个
map将遍历过的数字存起来,值作为key,下标作为值。对于每一次遍历:
- 取
map中查找是否有key为target-nums[i]的值 - 如果取到了,则条件成立,返回。
- 如果没有取到,将当前值作为
key,下标作为值存入map
时间复杂度:
O(n)空间复杂度
O(n)- 取
代码
1 | function twoSum(nums, target) { |
2.2 三数之和(leetcode)
题目
给定一个包含 n 个整数的数组nums,判断 nums 中是否存在三个元素a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意
答案中不可以包含重复的三元组。
思路
- 1.为了方便去重,我们首先将数组排序
- 2.对数组进行遍历,取当前遍历的数
nums[i]为一个基准数,遍历数后面的数组为寻找数组 - 3.在寻找数组中设定两个起点,最左侧的
left(i+1)和最右侧的right(length-1) - 4.判断
nums[i] + nums[left] + nums[right]是否等于0,如果等于0,加入结果,并分别将left和right移动一位 - 5.如果结果大于0,将
right向左移动一位,向结果逼近 - 5.如果结果小于0,将
left向右移动一位,向结果逼近
1 | function threeSum(numbers) { |
2.3 四数之和
题目
给定一个包含 n 个整数的数组nums,判断 nums 中是否存在四个元素a,b,c,d ,使得 a + b + c + d = 0 ?找出所有满足条件且不重复的四元组。
答案中不可以包含重复的四元组。
思路
- 和三数之和的思路是一样的
代码
1 | function fourSum(nums, target) { |
3 数据统计
如何对数组进行更高效额统计计算
3.1数组中出现次数超过数组一半的数字
题目
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
思路
目标值的个数比其他所有值加起来的数多
记录两个变量 1.数组中的某个值 2.次数
1.当前遍历值和上一次遍历值相等?次数+1 : 次数-1。
2.次数变为0后保存新的值。
3.遍历结束后保存的值,判断其是否复合条件
事件复杂度O(n) 不需要开辟额外空间 , 逻辑稍微复杂

代码
1 | function MoreThanHalfNum_Solution(numbers) |
3.2连续子数组的最大值
题目
输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值,要求时间复杂度为O(n)
例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)
思路
记录一个当前连续子数组最大值
max默认值为数组第一项记录一个当前连续子数组累加值
sum默认值为数组第一项1.从数组第二个数开始,若
sum<0则当前的sum不再对后面的累加有贡献,sum = 当前数2.若
sum>0则sum = sum + 当前数3.比较
sum和max,max = 两者最大值
1 | function FindGreatestSumOfSubArray(array) |
3.3 第一个只出现一次的字符9
遍历字符串,比较每个字符第一次和最后一次出现的位置是否相同。
1
indexOf`的时间复杂度为`O(n)`,所以整体的时间复杂度为O(n2),空间复杂度为`0
1 | function PrintMinNumber(numbers) |
3.4 扑克牌顺子
题目
扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。
2-10为数字本身,A为1,J为11...大小王可以看成任何数字,可以把它当作0处理。
思路
- 1.数组排序
- 2.遍历数组
- 3.若为
0,记录0的个数加1 - 4.若不为
0,记录和下一个元素的间隔 - 5.最后比较
0的个数和间隔数,间隔数>0的个数则不能构成顺子 - 6.注意中间如果有两个元素相等则不能构成顺子
代码
1 | function IsContinuous(numbers) |
4 二维数组
建立一定的抽象建模能力,将实际中的很多问题进行抽象
4.1 构建乘积数组
题目
给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法
思路
- 可以先计算下面的值,在循环乘以上三角的值

代码
1 | function multiply(array) |
4.2 顺时针打印矩阵
题目
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。

思路
打印拆分四部
- 第一步:从左到右打印一行
- 第二步:从上到下打印一列
- 第三步:从右到左打印一行
- 第四步:从下到上打印一列
最后一圈很有可能出现几种异常情况,打印矩阵最里面一圈可能只需三步、两步、甚至一步
所以在每一行打印时要做好条件判断:
能走到最后一圈,从左到右必定会打印
结束行号大于开始行号,需要从上到下打印
结束列号大于开始列号,需要从右到左打印
结束行号大于开始行号+1,需要从下到上打印
code
1 | function printMatrix(matrix) |
5 其他
5.1 把数组排成最小的数
题目
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
例如输入数组
{3,32,321},则打印出这三个数字能排成的最小数字为321323
思路
通过sort函数,
1 | function PrintMinNumber(numbers) |
5.2 在排序数组中查找数字
题目
统计一个数字在排序数组中出现的次数
思路
- 1.直接遍历数组,判断前后的值是否相同,找到元素开始位置和结束位置,时间复杂度
O(n) - 2.使用二分查找找到目标值,在向前向后遍历,找到所有的数,比上面略优,时间复杂度也是
O(n) - 3.使用二分查找分别找到第一个目标值出现的位置和最后一个位置,时间复杂度
O(logn)
1 | function binaryTree(data, arr, start, end) { |
5.3 数组中的逆序对
题目
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。
思路
使用暴力法:从第一个数开始,依次和后面每一个数字进行比较记录逆序对的个数,时间复杂度O(n2)
使用分治的细想:
若没了解过归并排序,建议先熟悉归并排序算法再来看本题。
直接将归并排序进行改进,把数据分成N个小数组。
合并数组 left - mid , mid+1 - right,合并时, 若array[leftIndex] > array[rightIndex] ,则比右边 rightIndex-mid个数大
1 | count += rightIndex-mid |
注意和归并排序的区别: 归并排序是合并数组数从小数开始,而本题是从大数开始。
时间复杂度O(nlogn)
空间复杂度O(n)
代码
1 | function InversePairs(data) { |
堆
- 堆的顶层是一棵完全二叉树,可以用数组实现
- 最大堆:每一个元素的节点值不小于其子节点
- 最小堆:每个元素的节点值不大于其子节点
- 应用:堆在处理某些特殊场景时可以大大降低代码的时间复杂度,例如在庞大的数据中找到最大的几个数或者最小的几个数,可以借助堆来完成这个过程。
1 堆的基本操作
1.1 堆的基本结构

- 堆的底层实际上是一棵完全二叉树。
- 可以用数组实现
- 每个的节点元素值不小于其子节点 - 最大堆
- 每个的节点元素值不大于其子节点 - 最小堆
1.2 堆的构建
堆存储在下标为0开始的数组中,因此在堆中给定下标为i的结点时:
(1) 如果i=0,结点i是根节点,没有双亲节点;否则结点i的双亲结点为 结点(i-1)/2
(2) 如果2 * i + 1 <= n - 1,则结点i的左孩子为结点2 * i + 1,否则结 点i无左孩子
(3)如果2 * i + 2 <= n - 1,则结点i的右孩子为结点2 * i + 2,否则结 点i无右孩

1.2.1 大顶堆
对于第一个非叶子节点开始依次对数组中的元素进行下沉操作
- 和孩子节点的最大值max比较
- 大于max的—不需要在下沉
- 小于max—和max交换位置继续下一层孩子节点的比较
1 | //1.大顶堆的创建 |
1.2.2 小顶堆
从第一个非叶子节点开始依次对数组中的元素进行下沉操作
- 和孩子节点的最小值
min比较 - 小于
min— 不需要在下沉 - 大于
min— 和min交换位置(下沉) - 继续和下一层孩子节点比较,直到队列末尾
1 | //小顶堆的创建 |
1.3 堆的插入(先进先出)
- 由于堆属于优先队列,只能从末尾添加
- 添加后有可能破坏堆的结构,需要从下到上进行调整
- 如果元素小于父元素,上浮
小顶堆为例
1 | //堆的插入(小顶堆)(优先队列只能从末尾插入元素) |
1.4 堆的移出
- 由于堆属于优先队列,只能从头部移除
- 移除头部后,使用末尾元素填充头部,开始头部下沉操作(调用小顶堆的调整)
1 | //小顶堆(堆的移出只能从头部移出,用最后一位填充,在调整) |
1.5 堆的封装
1 | function Heap(type = 'min') { |
2 练习题
2.1最小的k个数
题目
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4
思路
思路1:
先排序,再取前k个数,最小时间复杂度nlogn。
思路2:
1.把前k个数构建一个大顶堆
2.从第k个数开始,和大顶堆的最大值进行比较,若比最大值小,交换两个数的位置,重新构建大顶堆
3.一次遍历之后大顶堆里的数就是整个数据里最小的k个数。
时间复杂度nlogk,优于思路1。
2.2 数据流中的中位数
题目
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。
如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数
分析
- 这种题目可以通过对来解决太复杂了
- 直接插入排序后通过计算奇偶性来解决
1 | var arr = [] |
链表
1 链表的基础
用一组任意存储的单元来存储线性表的数据元素。一个对象存储着本身的值和下一个元素的地址。
需要遍历才能查询到元素,查询慢。
插入元素只需断开连接重新赋值,插入快。
2 基本应用
2.1 从尾到头打印链表
题目
- 输入一个链表,按链表值从尾到头的顺序返回一个
ArrayList。
分析

代码
1 | /*function ListNode(x){ |
2.2 反转链表
题目
- 输入一个链表,反转链表后,输出新链表的表头
分析
- 以链表的头部节点为基准节点
- 将基准节点的下一个节点挪到头部作为头节点
- 当基准节点的
next为null,则其已经成为最后一个节点,链表已经反转完成
1 | function ListNode(x){ |
2.3 删除链表中重复的节点
题目
- 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
思路
- 链表是排好顺序的,所以重复元素都会相邻出现 递归链表:
- 1.当前节点或当前节点的next为空,返回该节点
- 2.当前节点是重复节点:找到后面第一个不重复的节点
- 3.当前节点不重复:将当前的节点的next赋值为下一个不重复的节点
1 | function deleteDuplication(pHead) |
- 时间复杂度O(n)
- 空间复杂福O(1)
2.4 复杂链表的复制
题目
- 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
1 | function RandomListNode(x){ |
3 环类链表
环类题目即从判断一个单链表是否存在循环而扩展衍生的问题
3.1 环形链表找出环的入口节点
题目
- 给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
思路
- 首先判断是不是有环(设置fast->2,low->1)最终相遇则说明有环
- 当fast或fast.next不存在,return null(排除了只有一个元素的情况)
- 设置其中一个元素从开始节点出发,另一个从相遇点出发(fast->1,low->1)二者最终会相遇在环的入口与的位置
1 | /*function ListNode(x){ |
- 时间复杂度:O(n)
3.2 圆圈中最后剩下的数字(约瑟夫环的问题)
题目
0,1,...,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。
1 | function LastRemaining_Solution(n, m) |
4 双指针
- 双指针的思想在链表和数组中的题目都经常会用到,主要是利用两个或多个不同位置的指针,通过速度和方向的变换解决问题。
- 两个指针从不同位置出发:一个从始端开始,另一个从末端开始;
- 两个指针以不同速度移动:一个指针快一些,另一个指针慢一些。
对于单链表,因为我们只能在一个方向上遍历链表,所以第一种情景可能无法工作。然而,第二种情景,也被称为慢指针和快指针技巧,是非常有用的。
4.1 两个链表的公共节点
题目
- 输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
思路:
- 获取两个列表的长度,找到差值interval
- 让长的列表先走interval,从同样的长度起点开始
- 当节点存在,且不相等直接next,相等返回当前节点
1 | function FindFirstCommonNode(pHead1, pHead2) |
4.2 链表中倒数第k个节点
题目
- 输入一个链表,输出该链表中倒数第k个结点。
分析
简单思路: 循环到链表末尾找到 length 在找到length-k节点 需要循环两次。
优化:
设定两个节点,间距相差k个节点,当前面的节点到达终点,取后面的节点。
前面的节点到达k后,后面的节点才出发。
代码鲁棒性: 需要考虑head为null,k为0,k大于链表长度的情况。
1 | function FindKthToTail(head, k) |
5 其他
5.1 二叉搜索树与双向链表
题目
- 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
1 | function Convert(pRootOfTree) |
5.2 合并两个有序链表
题目
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
思路
链表头部节点比较,取较小节点。
小节点的next等于小节点的next和大节点的较小值。
如此递归。
返回小节点。
考虑代码的鲁棒性,也是递归的终止条件,两个head为null的情况,取对方节点返回。
1 | function Merge(pHead1, pHead2) |
二叉树
1 打印二叉树(层次遍历)
1.1 从上往下打印二叉树(层次遍历)
题目
从上往下打印出二叉树的每个节点,同层节点从左至右打印
思路
- 借助队列(先进先出)
1 | /* function TreeNode(x) { |
1.2 把二叉树打印成多行
题目
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
思路
- 在单行打印的基础上设置变量当前行节点数和子节点数,并保存每行的暂存节点数
1 | /* function TreeNode(x) { |
1.3 按之字形顺序打印二叉树
题目
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
分析
- 采用两个栈分别保存奇数行和偶数行
- 奇数从左到右
- 偶数从右到左
- 边出栈边保存打印顺序
1 | /* function TreeNode(x) { |
哈希表
哈希是通过哈希函数来进行映射检索地址,哈希是一种有效的检索技术,会出现同义词冲突
如何设计哈希函数以及如何避免冲突就是哈希表的常见问题。 好的哈希函数的选择有两条标准:
- 1.简单并且能够快速计算
- 2.能够在址空间中获取键的均匀分布
当用到哈希表时我们通常是要开辟一个额外空间来记录一些计算过的值,同时我们又要在下一次计算的过程中快速检索到它们,例如上面提到的两数之和、三数之和等都利用了这种思想。
1 最小的k个数
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
1 | function GetLeastNumbers_Solution(input, k) |
栈和队列
在上面的数组中,我们可以通过索引随机访问元素,但是在某些情况下,我们可能要限制数据的访问顺序,于是有了两种限制访问顺序的数据结构:栈(先进后出)、队列(先进先出)
1 栈和队列的互相实现
1.1 用两个栈实现队列
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
思路
栈1:
用于入队列存储
栈2:
出队列时将栈1的数据依次出栈,并入栈到栈2中
栈2出栈即栈1的底部数据即队列要出的数据。
注意:
栈2为空才能补充栈1的数据,否则会打乱当前的顺序。
1 | //定义两个栈,一个用于进,一个用于出(先进先出) |
1.2 用两个队列实现栈
1 | // 用两个队列实现栈(先进后出) |
2 练习题
2.1包含min函数的栈
题目
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))
思路
- 定义两个栈,一个用于存储数据,一个用于存储最小值
- 每次进栈的元素和栈顶最小元素比较,将最小值存入
- 数据栈出栈,最小值也出栈
- 这样最小值的栈顶永远是当前栈的最小值

代码
1 | var dataStack=[] |
2.2 滑动窗口的最大值
题目
给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口 k 内的数字。滑动窗口每次只向右移动一位。 返回滑动窗口最大值。
分析

代码
1 | function maxInWindows(num, size) |
2.3 栈的压入,弹出序列
题目
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
思路

- 借助一个中间栈temp来完成
- 借助指针j->popV,i->pushV
- 当最后的元素等于popV时,j++
- 反之,pushV的[i]入栈temp,i++右移(前提是i<pushV.length)
1 | function IsPopOrder(pushV, popV) |
2.4 数据流中的中位数
题目
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。
如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
思路
1.维护一个大顶堆,一个小顶堆,数据总数:
- 小顶堆里的值全大于大顶堆里的;
- 2个堆个数的差值小于等于1
2.当插入数字后数据总数为奇数时:使小顶堆个数比大顶堆多1;当插入数字后数据总数为偶数时,使大顶堆个数跟小顶堆个数一样。
3.当总数字个数为奇数时,中位数就是小顶堆堆头;当总数字个数为偶数时,中位数数就是2个堆堆顶平均数
代码
1 | var arr = [] |
字符串
1 练习题
1.1 替换空格
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
代码
1 | function replaceSpace(str) |
1.2 正则表达式匹配
题目
请实现一个函数用来匹配包括’.’和’‘的正则表达式。 模式中的字符’.’表示任意一个字符,而’‘表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。 例如,字符串”aaa”与模式”a.a”和”abaca”匹配,但是与”aa.a”和”ab*a”均不匹配
注意
- 这里的*和点
.的含义和正则表达式中一致
1 | //s, pattern都是字符串 |
1.3 字符串的排列(动态规划,递归,字符串)?????(有问题)
题目
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
分析
- 通过回朔法(不断做决策)最后的跳出条件不是很懂

1 | // 考察的是回溯法,全排列 |
1.4 翻转字符串
题目
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student.",则输出"student. a am I"。
思路
- 直接调用api
1 | function ReverseSentence(str) { |
1.5 左旋转字符串
题目
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
1 | function LeftRotateString(str, n) { |
1.6 字符流中第一个不重复的字符
题目
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符”go”时,第一个只出现一次的字符是”g”。当从该字符流中读出前六个字符“google”时,第一个只出现一次的字符是”l”。
分析
- 借助map来保存当前的值和次数
1 | var map; |
1.7 表示数值的字符串
题目
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。 例如,字符串”+100”,”5e2”,”-123”,”3.1416”和”-1E-16”都表示数值。 但是”12e”,”1a3.14”,”1.2.3”,”+-5”和”12e+4.3”都不是。
思路
正则表达式的解法
考虑所有的情况
- .只能出现数字、符号位、小数点、指数位
- 2.小数点,指数符号只能出现一次、且不能出现在开头结尾
- 3.指数位出现后,小数点不允许在出现
- 4.符号位只能出现在开头和指数位后面
正则表达式的解析
1 | //正则 |
1 | 1.正则表达式的解法 |
1 | 2.考虑所有的情况 |
其他算法
1 回溯算法
解决一个回溯问题,实际上就是一个决策树的遍历过程。你只需要思考 3 个问题:
1、路径:也就是已经做出的选择。
2、选择列表:也就是你当前可以做的选择。
3、结束条件:也就是到达决策树底层,无法再做选择的条件。
1.1 机器人的运动范围(回溯)
题目
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。
例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
思路
1.2 剪绳子(贪心算法)
题目
给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[1],…,k[m]。请问k[1]x…xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
1.3 矩阵中的路径(回溯和DFS)
本文链接: https://sparkparis.github.io/2020/06/06/%E5%89%91%E6%8C%87offer/
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!