1.搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例 1:输入: [1,3,5,6], 5
输出: 2
示例 2:输入: [1,3,5,6], 2
输出: 1
示例 3:输入: [1,3,5,6], 7
输出: 4
示例 4:输入: [1,3,5,6], 0
输出: 0
解一:
还是indexOf开头,如果存在返回索引值,否则就push一下给它生米煮成熟饭,再排序,重新返回索引值。
解二:
老实方法用for遍历,存在(=)和不存在(>)两种情况可以一起处理。
解三:
使用二分法,也是这道题真正想考察的方法。
二分法理解起来很简单,小学就学过。但是实际编码时,边界防范、左右中位数的取舍、while中的条件以及是否+1何处+1可以说是要处处设防。
2.. 外观数列
「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。前五项如下:
1
11
21
1211
111221
1 被读作 "one 1" ("一个一") , 即 11。
11 被读作 "two 1s" ("两个一"), 即 21。
21 被读作 "one 2", "one 1" ("一个二" , "一个一") , 即 1211。
给定一个正整数 n(1 ≤ n ≤ 30),输出外观数列的第 n 项。
注意:整数序列中的每一项将表示为一个字符串。
示例 1:输入: 1
输出: "1"
解释:这是一个基本样例。
示例 2:输入: 4
输出: "1211"
解释:当 n = 3 时,序列是 "21",其中我们有 "2" 和 "1" 两组,"2" 可以读作 "12",也就是出现频次 = 1 而 值 = 2;类似 "1" 可以读作 "11"。所以答案是 "12" 和 "11" 组合在一起,也就是 "1211"。
解一:
通过正则合并相同元素完成累加
解二:
这种类型的题目用字典不是特别爽吗????
3. 最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
解题思路:
示例: [a, b , c, d , e]
解答这类题目, 省略不掉遍历, 因此我们先从遍历方式说起
通常我们遍历子串或者子序列有三种遍历方式
以某个节点为开头的所有子序列: 如 [a],[a, b],[ a, b, c] ... 再从以 b 为开头的子序列开始遍历 [b] [b, c]。
根据子序列的长度为标杆,如先遍历出子序列长度为 1 的子序列,在遍历出长度为 2 的 等等。
以子序列的结束节点为基准,先遍历出以某个节点为结束的所有子序列,因为每个节点都可能会是子序列的结束节点,因此要遍历下整个序列,如: 以 b 为结束点的所有子序列: [a , b] [b] 以 c 为结束点的所有子序列: [a, b, c] [b, c] [ c ]。
第一种遍历方式通常用于暴力解法, 第二种遍历方式 leetcode (5. 最长回文子串 ) 中的解法就用到了。
第三种遍历方式 因为可以产生递推关系, 采用动态规划时, 经常通过此种遍历方式, 如 背包问题, 最大公共子串 , 这里的动态规划解法也是以 先遍历出 以某个节点为结束节点的所有子序列 的思路
对于刚接触动态规划的, 我感觉熟悉第三种遍历方式是需要抓住的核心
因为我们通常的惯性思维是以子序列的开头为基准,先遍历出以 a 为开头的所有子序列,再遍历出以 b 为开头的...但是动态规划为了找到不同子序列之间的递推关系,恰恰是以子序列的结束点为基准的,这点开阔了我们的思路。
我在网上看不少解答时,直接阅读其代码,总是感觉很理解很吃力,因为好多没有写清楚,一些遍历到底代表什么意思,看了许久仍不知所以然,下面的代码中摘录了 维基中的解释,感觉比较清楚,供大家理解参考。
代码:
第二块代码和 第一块代码 思路实现是完全一样的,但是如果第一次看到这类题目,直接阅读 第二块代码,理解起来很难,尤其是 如果改成 if (sum > 0 ) 对于刚接触的这题目的比较不好理解。
4. 最后一个单词的长度
给定一个仅包含大小写字母和空格 ' ' 的字符串 s,返回其最后一个单词的长度。如果字符串从左向右滚动显示,那么最后一个单词就是最后出现的单词。
如果不存在最后一个单词,请返回 0 。
说明:一个单词是指仅由字母组成、不包含任何空格字符的 最大子字符串。
示例:输入: "Hello World"
输出: 5
解一:使用trim
解法二:遍历
从右往左最后一个字符位置开始,求最后一个单词的开头和结尾字符索引位置,相减即为所求
结尾字符记录索引为end,初始化为字符串结尾字符索引
遇到空字符,end--
否则
end < 0,越界,字符串为空或不存在,返回0
end > 0
继续end--判断字符串是否为空
不为空,则说明找到了最后一个单词的结尾字符索引位置
开头字符初始化为 start = end,依旧向前遍历
不为空,则 end--
为空,则说明找到了最后一个单词的首字符位置 start
结果
end - start
5. 加一
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。示例 2:
输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。
解法一:使用es10 bigint
已知传入参数必定为数值型数组
将参数转换为字符型(Array.prototype.join())
由字符型变为BigInt型(BigInt构造函数))
进行专属于BigInt类型的数学计算
计算结果再次变为字符型(String构造函数)
将字符型数值变更为数组(String.prototype.split())
以下是相应的代码实现:
方法二:小学生加法
我们将问题简化为第i位(0 <= i < n)加一或加0。
而加一的结果分为两种情况:
加一后需要进位
加一后不需要进位
对于第二种情况,我们直接返回digits就可以。
对于第一种情况,我们将指针移到前面一位。继续同样处理。
而边界情况即,首位也产生进位,首位产生进位只会是999....9999这种情况,我们也很容易得出结果。
6. 二进制求和
给你两个二进制字符串,返回它们的和(用二进制表示)。
输入为 非空 字符串且只包含数字 1 和 0。
示例 1:输入: a = "11", b = "1"
输出: "100"
示例 2:输入: a = "1010", b = "1011"
输出: "10101"
提示:
每个字符串仅由字符 '0' 或 '1' 组成。
1 <= a.length, b.length <= 10^4
字符串如果不是 "0" ,就都不含前导零。
解法一:利用BigInt解决溢出的问题
解法二:小学生加法
先对两个字符串按照最长长度,在前面用'0'补全长度
然后对字符串从后向前遍历,对两个字符串相同位置的字符进行求和
如果和 >=2,说说明发生了进位,保存进位状态为true,下一次求和要 + 1
如果和 < 2,说明没有发生进位,保存进位状态为false,下一次求和要 + 0
遍历结束后,如果进位状态为 true, 说明第一位字符串仍然发生了进位,则拼上一个1
时间复杂度O(n)
代码:
7. x 的平方根
实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
示例 1:输入: 4
输出: 2
示例 2:输入: 8
输出: 2
说明: 8 的平方根是 2.82842...,
由于返回类型是整数,小数部分将被舍去。
解一:
现成的sqrt函数来一个。
解二:
最耿直的自增暴力解。
解三:
牛顿法,只用知道迭代公式就好了(原理在数学规划课上学得差不多了):
8. 爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入:2
输出:2
解释:有两种方法可以爬到楼顶。
1 阶 + 1 阶
2 阶
示例 2:
输入:3
输出:3
解释:有三种方法可以爬到楼顶。
1 阶 + 1 阶 + 1 阶
1 阶 + 2 阶
2 阶 + 1 阶
解法:动态规划
本问题其实常规解法可以分成多个子问题,爬第n阶楼梯的方法数量,等于 2 部分之和
爬上 n-1n−1 阶楼梯的方法数量。因为再爬1阶就能到第n阶
爬上 n-2n−2 阶楼梯的方法数量,因为再爬2阶就能到第n阶
所以我们得到公式 dp[n] = dp[n-1] + dp[n-2]dp[n]=dp[n−1]+dp[n−2]
同时需要初始化 dp[0]=1dp[0]=1 和 dp[1]=1dp[1]=1
时间复杂度:O(n)O(n)
9. 删除排序链表中的重复元素
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例 1:
输入: 1->1->2
输出: 1->2
示例 2:输入: 1->1->2->3->3
输出: 1->2->3
思路
标签:链表
指定 cur 指针指向头部 head
当 cur 和 cur.next 的存在为循环结束条件,当二者有一个不存在时说明链表没有去重复的必要了
当 cur.val 和 cur.next.val 相等时说明需要去重,则将 cur 的下一个指针指向下一个的下一个,这样就能达到去重复的效果
如果不相等则 cur 移动到下一个位置继续循环
时间复杂度:O(n)O(n)
10. 合并两个有序数组
给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。
说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3输出: [1,2,2,3,5,6]
解一:
数组拼接后sort排序。缺点是没有利用nums1和nums2本身是有序数组的优势。
解法二:双指针 + 从后向前
思路
两个数组从小到大排序
且题目要求 修改nums1为合并排好序的nums1+nums2
双指针
两个分别指向两个数组尾部的指针
从后向前
比较两指针位置的值
大的一定是结果数组的最大值
一一填充到 nums1的末尾
遍历完后
当 n > 0 时
说明 nums2 中还有剩余没有比较的数字
将其插入替换 nums1 数组前面n个数字即可