剑指 Offer II(专项突击版) 解题记录 golang版 第1-5天 001-016

记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步




第 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

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值