1.两数之和
题目
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
解法1 时间复杂度o(n²) 空间复杂度o(1)
外层遍历数组, 内层从外层遍历到的数组的下一个数开始遍历到尾部,找到相加和为target的两个数
解法2 时间复杂度o(n) 空间复杂度o(n)
先遍历一遍, 将值和位置保存在哈希数组里面(key是值 value是位置)
再遍历一遍, 寻找target - 当前遍历到数字的值 在哈希数组里面存在 并且不是自己 返回(这题数组内的值不会重复)
2. 两数相加
题目
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
解法 时间复杂度o(n+m)
因为是逆序的 直接最简单从两个列表头结点遍历相加即可,使用一个变量保存进位信息
最后再遍历比较长的那个连表把剩下的值加上去即可(使用一个指针变量指向还不为空的链表指针而不是对两个指针做while操作 因为走到这一步一定最多只有一个链表没有遍历完)
3.无重复字符字符串
题目
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
解法 时间复杂度o(n) 空间复杂度o(n)
利用一个哈希数组记录所有字符串出现信息, key是字符, value是字符出现的最后位置
遍历这个字符串,每次都更新字符串的出现位置,同时一个变量记录当前计算子串的开始计算位置
如果当前字符出现过(在哈希数组里存在且出现位置大于等于这一轮子串开始位置),判断更新子串最大长度即可,新的字符子串位置则为当前字符上一次出现字符的位置+1
4.寻找两个有序数组的中位数
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。时间复杂度为log(m+n)
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0
解法 时间复杂度log(m+n) 空间复杂度o(1)
分析
这题比较麻烦 最终状态 假定有这么一条线将这两个数组分割为两半
A[0] A[1] A[2] ... A[i-1] | A[i] A[i+1] ... A[n]
B[0] B[1] B[2] ... B[j-1] | B[j] B[j+1] ... B[m]
左边数组元素个数: i + j 右边数组元素: m + n - i - j
此时 需要满足
|右边元素个数 - 左边元素个数| <= 1
A[i] > B[j-1]
B[j] > A[i-1]
中位数 = (max(A[i-1], B[j-1]) + min(A[i], B[j])) /2 或者 max(A[i-1], B[j-1]) 或者 min(A[j], B[j]) 根据左右元素个数而定
但是边界条件挺多的 分割线在第一个元素左边 或 最后一个元素的右边,可以使得 len(A) >= len(B)减少一点边界条件的组合
计算过程
两个数组开始时都是从中间开始切 然后调整线的位置,调整的时候就是用二分法调整,优先满足
A[i] > B[j-1]
B[j] > A[i-1]
根据左右元素的个数和A[i] B[j-1] B[j] A[i-1]大小关系调整
保证以上关系再调整,满足
|右边元素个数 - 左边元素个数| <= 1
同样也是根据左右元素的个数和A[i] B[j-1] B[j] A[i-1]大小关系调整
获取中位数
其实比较麻烦的计算中位数时边界条件不少
5.最长回文子串
题目
给定一个字符串 s
,找到 s
中最长的回文子串。你可以假设 s
的最大长度为 1000。
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
解法 时间复杂度 o(n²) 空间复杂度o(1)
假定回文的中线要么在某字符上 要么在两个字符中间 如
中线0 中线1 中线2 中线3 中线4 中线5 中线6 中线7
b a b a d
遍历中线位置 往两边拓展即可 值得注意的是要注意中线是在字符上面的时候 长度从1往上涨
6.Z形变换
将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "LEETCODEISHIRING"
行数为 3 时,排列如下:
L C I R
E T O E S I I G
E D H N
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"
解法一 时间复杂度 o(n) 空间复杂度o(n²)
计算出行数 然后将数据按形状填入一个二维数组 最后在横向输出
解法二 时间复杂度o(n) 空间复杂度o(1)
可以找到每一行输出时的规律:
假定每一行行号从0开始,
每行第一个字符在原字符串的位置一定是0, 1, 2, 3 ... 行数 - 1
第一行和最后一行 每两个字符间隔是(行数 - 1) * 2
中间的行每两个字符间隔是 (行数-1) * 2 - 当前行号2 当前行号 2 交替
遍历行数即可 (当计算的坐标大于字符串长度时跳到下一行)
7.整数反转
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
输入: 123
输出: 321
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231, 231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。
解法 时间复杂度o(n) 空间复杂度o(1)
如果是负数 取反先算
使用一个变量 一边取出个位数一边计算
var ret int
for x > 0 {
ret = ret * 10 + x % 10
x /= 10
// 溢出检查
}
感觉在循环里面做溢出检查好一些 不过比较坑的是溢出返回的是0 所以负数和正数计算上下限不一样
8.字符串转整数
略
9.回文数字 时间复杂度o(n) 空间复杂度o(1)
题目
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
输入: 121
输出: true
输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
解法
先直接判断输入 为0返回true 小于0或者是%10 == 0 返回false节约一些case的时间
算的时候类似 整数反转, 算出整数反转的数是否等于原来的数即可
10.正则表达式匹配
题目
给你一个字符串 s
和一个字符规律 p
,请你来实现一个支持 '.'
和 '*'
的正则表达式匹配。
'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
输入:
s = "aa"
p = "a*"
输出: true
解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
解法 时间复杂度o(n²) 空间复杂度o(1)
正常遍历s 和 p即可 注意当s或者p遍历之后结果的判断
如果碰到 .* 或者 字符* 并且符合匹配到当前字符的时候需要走两个分支, 一个将这个字符算在匹配到 (a*) 这个表达式里,一个是不算, 类似于
func match(s, p string, sPtr, pPtr int)
if p[pPtr+1] == '*' {
if p[pPtr] == '.' || s[sPtr] == p[pPtr] {
return match(s, p, sPtr + 1, pPtr) || match(s, p, sPtr, pPtr)
}
}