记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步
目录
第 1 天 整数
001. 整数除法
同29
都转变为正数相除 记录结果是否为负数
无法用乘法 除法 则使用减法 每次减去被除数 太耗时
考虑 被除数divisor* 2^x
找到最大的x= i-1 divisor*(2^i)>dividend
从i-1到0考虑 是否可以减去divisor*(2^x) 如果可以 则结果加上2^x
特殊考虑溢出
func divide(dividend int, divisor int) int {
if dividend == 0 {
return 0
}
var same = 0
if dividend < 0 {
dividend = -dividend
same ^= 1
}
if divisor < 0 {
divisor = -divisor
same ^= 1
}
var ans = 0
var base = 1
var tmp = divisor
var valuelist []int
var loc = 0
valuelist = append(valuelist, tmp)
for dividend >= tmp {
base <<= 1
tmp += tmp
valuelist = append(valuelist, tmp)
loc += 1
}
base >>= 1
loc -= 1
for base > 0 {
if dividend >= valuelist[loc] {
ans += base
dividend -= valuelist[loc]
}
base >>= 1
loc -= 1
}
if same == 1 {
ans = -ans
}
if ans > 2147483647 {
ans = 2147483647
}
return ans
}
002. 二进制加法
同67
分别从最低位len(a)-1,len(b)-1开始考虑
carry代表进位
va,vb代表当前为数值 如果当前字符串已经没有了 则默认为0
s代表当前为结果
如果 va==vb
如果都为1
如果carry=1 结果为1 carry为1
如果carry=0 结果为0 carry为1
如果都为0
如果carry=1 结果为1 carry为0
如果carry=0 结果为0 carry为0
如果 va!=vb
说明一个为1 一个为0
如果carry=1 结果为0 carry为1
如果carry=0 结果为1 carry为0
将当前结果加入
最后考虑carry是否仍有进位
func addBinary(a string, b string) string {
var lena = len(a) - 1
var lenb = len(b) - 1
var l int
if lena > lenb {
l = lena
} else {
l = lenb
}
var carry = 0
var ans = ""
for i := 0; i <= l; i++ {
var s = "0"
var va = "0"
var vb = "0"
if lena >= 0 {
va = string(a[lena])
}
if lenb >= 0 {
vb = string(b[lenb])
}
lena -= 1
lenb -= 1
if va == vb {
if va == "1" {
if carry == 1 {
s = "1"
}
carry = 1
} else {
if carry == 1 {
carry = 0
s = "1"
}
}
} else {
if carry == 0 {
s = "1"
}
}
ans = s + ans
}
if carry == 1 {
ans = "1" + ans
}
return ans
}
003. 前 n 个数字二进制中 1 的个数
同338
从小到大考虑 对于数值num 去除二进制最高位后的数值必定已经考虑过
例如对于11 = 1011 去除最高位 8=1000 => 011 = 3已经考虑过有2个1加上最高位1个 得到11有3个1
使用base记录最高位=2^i base<num
如果num==base*2 说明此时num只有一个1
否则num的1位数 = 1 + ans[num-base]
func countBits(n int) []int {
var ans = []int{0}
if n == 0 {
return ans
}
ans = append(ans, 1)
if n == 1 {
return ans
}
var base = 1
for i := 2; i <= n; i++ {
if i == base*2 {
ans = append(ans, 1)
base *= 2
} else {
ans = append(ans, 1+ans[i-base])
}
}
return ans
}
第 2 天 整数
004. 只出现一次的数字
同137
使用 one two记录出现一次 两次的数状态
one two
0 0 0
1 x 0
2 0 x
3 0 0
对于数x 状态如上
使用异或可以去除重复出现的数或添加第一次出现的数
one = one^num
two = two^num
当第一次时 one=num 正确但是two也变成了num 我们需要去除 同~num想与
及 two = two^num&(~one) = 0
当第二次时 one=num two=num
当第三次时 one变成了num 不需要同样与~num想与及one = one^num&(~two)
func singleNumber(nums []int) int {
var one, two int
for _, num := range nums {
one = one ^ num&(^two)
two = two ^ num&(^one)
}
return one
}
005. 单词长度的最大乘积
同318
使用一个26位二进制来表示字符串出现的单词种类 出现的单词位置设为1
两个字符串若二进制相与为0 则说明两个字符串不含有公共字母
遍历不含公共字母的字符串 求最大长度乘积
func maxProduct(words []string) int {
var n = len(words)
var taglist []int
for _, word := range words {
var tag int
for _, c := range word {
var v = c - 'a'
tag |= 1 << v
}
taglist = append(taglist, tag)
}
var ans = 0
for i := 0; i < n; i++ {
for j := i + 1; j < n; j++ {
if taglist[i]&taglist[j] == 0 {
if ans < len(words[i])*len(words[j]) {
ans = len(words[i]) * len(words[j])
}
}
}
}
return ans
}
006. 排序数组中两个数字之和
同167
双指针 从头尾分别开始 计算最大最小值之和
若小于目标数 需要增大 则左指针向右移
若大于目标数 需要减小 则右指针向左移
func twoSum(numbers []int, target int) []int {
var l, r = 0, len(numbers) - 1
for l < r {
if target == numbers[l]+numbers[r] {
break
} else if target > numbers[l]+numbers[r] {
l += 1
} else {
r -= 1
}
}
return []int{l + 1, r + 1}
}
第 3 天 数组
007. 数组中和为 0 的三个数
同15
1.对于三个数[a,b,c] 为了防止重复 假定a<=b<=c
因为a+b+c=0 -> a<=0 c>=0
使用map记录所以数出现次数 从小到大排序所有不同的数
从头遍历a
在a后遍历b b>=a
根据a+b = -c 得到目标c
判断c是否能够取到
2.排序
遍历第一个数a 在a后续进行双指针left,right查找
如果left+right>-a right左移
如果left+right<-a left右移
func threeSum(nums []int) [][]int {
var ret [][]int
if len(nums) < 3 {
return ret
}
var m = make(map[int]int)
for _, num := range nums {
m[num] += 1
}
var nlist []int
for k, _ := range m {
nlist = append(nlist, k)
}
sort.Ints(nlist)
for i := 0; i < len(nlist); i++ {
var a = nlist[i]
if a > 0 {
break
}
m[a] -= 1
for j := i; j < len(nlist); j++ {
var b = nlist[j]
if m[b] == 0 {
continue
}
var s = a + b
if s > 0 {
break
}
if -s < b {
continue
}
m[b] -= 1
if m[-s] > 0 {
var ans = []int{a, b, -s}
ret = append(ret, ans)
}
m[b] += 1
}
m[a] += 1
}
return ret
}
func threeSum2(nums []int) [][]int {
var ret [][]int
if len(nums) < 3 {
return ret
}
sort.Ints(nums)
n := len(nums)
for i := 0; i < n-2 && nums[i] <= 0; i++ {
if i > 0 && nums[i] == nums[i-1] {
continue
}
var left, right = i + 1, n - 1
for left < right {
var tmp = nums[left] + nums[right]
if tmp == -nums[i] {
ret = append(ret, []int{nums[i], nums[left], nums[right]})
for left < right && nums[left] == nums[left+1] {
left += 1
}
for left < right && nums[right] == nums[right-1] {
right -= 1
}
left += 1
right -= 1
} else if tmp > -nums[i] {
right -= 1
} else {
left += 1
}
}
}
return ret
}
008. 和大于等于 target 的最短子数组
同209
1.双指针
指针l,r代表当前区间[l,r] 总和为total
每次将nums[r]加入 如果total>=target 尝试缩小left直到无法缩小为止
此时判断当前长度r-l+1是否小于结果 更新结果
func minSubArrayLen(target int, nums []int) int {
var n = len(nums)
var l, r = 0, 0
var ans = 0
var total = 0
for r < n {
total += nums[r]
fmt.Println(ans, total, nums[r])
if total >= target {
for l < r && total-nums[l] >= target {
total -= nums[l]
l += 1
}
if ans == 0 || r-l+1 < ans {
ans = r - l + 1
}
}
r += 1
}
return ans
}
009. 乘积小于 K 的子数组
同713
1.前缀数组 乘积过大 取对数log2
数组l[i] 记录log2(nums[0]nums[1]…*nums[i])
对于nums[i:j]的乘积对数 = l[j]-l[i-1]
因为l数组必定是单调递增的 找到第一个满足的点 后续必定满足 使用二分
防止误差加上1e-9
2. 双指针 l,r
total记录nums[l:r]的总乘积
每次将r右移一位 判断l是否需要右移
此时增加r-l+1个子数组
func numSubarrayProductLessThanK(nums []int, k int) int {
var li = []float64{0}
var mult float64
for _, num := range nums {
mult += math.Log2(float64(num))
li = append(li, mult)
}
var target = math.Log2(float64(k))
var ans = 0
for i := 1; i <= len(nums); i++ {
if li[i] < target {
ans += i
fmt.Println(i, i)
} else {
var l, r = 0, i
for l <= r {
var mid = (l + r) >> 1
if li[i]-li[mid]+1e-9 < target {
r = mid - 1
} else {
l = mid + 1
}
}
if l < i {
ans += i - l
fmt.Println(i, i-l)
}
}
}
return ans
}
func numSubarrayProductLessThanK2(nums []int, k int) int {
if k <= 1 {
return 0
}
var ans = 0
var l, r = 0, 0
var total = 1
for r < len(nums) {
total *= nums[r]
for l <= r && total >= k {
total /= nums[l]
l += 1
}
ans += r - l + 1
r += 1
}
return ans
}
第 4 天 数组
010. 和为 k 的子数组
同560
前缀数组
mem记录前缀和出现过的次数
对于位置i 为了满足和为k 需要减去target=slist[i]-k
在mem中寻找target出现的次数
func subarraySum(nums []int, k int) int {
var slist = []int{0}
for i, num := range nums {
slist = append(slist, num+slist[i])
}
var ans = 0
var mem = make(map[int]int)
mem[0] = 1
for i := 1; i <= len(nums); i++ {
var target = slist[i] - k
if num, ok := mem[target]; ok {
ans += num
}
mem[slist[i]] += 1
}
return ans
}
011. 0 和 1 个数相同的子数组
同525
前缀数组 遇到0 -1 遇到1 +1
如果l[j]-l[i]=0 说明nums[i:j]内0,1数量相同
mem记录前缀和出现过的最早位置
对于位置i 为了满足和为0 需要减去target=l[i]
在mem中寻找target出现最早位置
func findMaxLength(nums []int) int {
var l = []int{0}
for i, num := range nums {
if num == 0 {
l = append(l, l[i]-1)
} else {
l = append(l, l[i]+1)
}
}
var ans = 0
var mem = make(map[int]int)
mem[0] = 0
for i := 1; i <= len(nums); i++ {
var target = l[i]
if num, ok := mem[target]; ok {
if i-num > ans {
ans = i - num
}
}
if _, ok := mem[l[i]]; !ok {
mem[l[i]] = i
}
}
return ans
}
012. 左右两边子数组的和相等
同724
前缀数组 l记录前缀和
总和为sum
从左到右考虑 对于位置i 如果前缀和l[i]*2+nums[i]==sum
说明i位置左右两边和相同 并且i是满足条件的最左侧位置 返回
func pivotIndex(nums []int) int {
var sum int
var l = []int{0}
for _, num := range nums {
sum += num
l = append(l, sum)
}
for i := 0; i < len(nums); i++ {
if l[i]*2+nums[i] == sum {
return i
}
}
return -1
}
013. 二维子矩阵的和
同304
前缀和 mat[i+1][j+1] 记录从左上角到[i,j]这个矩阵的和
子矩阵为 m[row2+1][col2+1] - m[row2+1][col1] - m[row1][col2+1] + m[row1][col1]
type NumMatrix struct {
Matrix [][]int
}
func Constructor(matrix [][]int) NumMatrix {
var mat NumMatrix
var n = len(matrix)
var m = len(matrix[0])
var tmp = make([]int, m+1)
mat.Matrix = append(mat.Matrix, tmp)
for i := 0; i < n; i++ {
var tmp = make([]int, m+1)
var linesum = 0
for j := 0; j < m; j++ {
linesum += matrix[i][j]
tmp[j+1] = mat.Matrix[i][j+1] + linesum
}
mat.Matrix = append(mat.Matrix, tmp)
}
return mat
}
func (this *NumMatrix) SumRegion(row1 int, col1 int, row2 int, col2 int) int {
var m = this.Matrix
var value = m[row2+1][col2+1] - m[row2+1][col1] - m[row1][col2+1] + m[row1][col1]
return value
}
第 5 天 字符串
014. 字符串中的变位词
同567
cnt记录s1中 每个字母需要出现的次数
双指针遍历s2 对于在right位置出现的字母值v cnt中减去一次
如果cnt[v]<0 说明该字母不满足条件 出现次数过多或不在s1中
将left往右移除字母 移除掉的字母在cnt中加回 保证cnt中每一位必定>=0
如果此时[left,right]长度为len(s1) 说明所有s1的字母都被找到 返回true
func checkInclusion(s1 string, s2 string) bool {
var cnt [26]int
for _, c := range s1 {
cnt[c-'a'] += 1
}
var left = 0
for right, c := range s2 {
var v = c - 'a'
cnt[v] -= 1
for cnt[v] < 0 {
cnt[s2[left]-'a'] += 1
left += 1
}
if right-left+1 == len(s1) {
return true
}
}
return false
}
015. 字符串中的所有变位词
同438
思路与上一题一致
cnt记录p中 每个字母需要出现的次数
双指针遍历s 对于在right位置出现的字母值v cnt中减去一次
如果cnt[v]<0 说明该字母不满足条件 出现次数过多或不在p中
将left往右移除字母 移除掉的字母在cnt中加回 保证cnt中每一位必定>=0
如果此时[left,right]长度为len§ 说明所有p的字母都被找到 记录当前起始位置left
func findAnagrams(s string, p string) []int {
var cnt [26]int
var ans []int
for _, c := range p {
cnt[c-'a'] += 1
}
var left = 0
for right, c := range s {
var v = c - 'a'
cnt[v] -= 1
for cnt[v] < 0 {
cnt[s[left]-'a'] += 1
left += 1
}
if right-left+1 == len(p) {
ans = append(ans, left)
}
}
return ans
}
016. 不含重复字符的最长子字符串
同3
双指针
m 记录字母出现的次数
若次数大于1 则从left开始移除 直至right位置字母只出现一次 符合条件
记录符合条件的最大长度 right-left+1
func lengthOfLongestSubstring(s string) int {
var m = make(map[rune]int)
var left int
var ans int
for right, c := range s {
m[c] += 1
for m[c] > 1 {
m[rune(s[left])] -= 1
left += 1
}
if right-left+1 > ans {
ans = right - left + 1
}
}
return ans
}