目前是二刷阶段,记录一下刷题的思路,记录下重点,方便以后复习,目前还没写多少,持续补充
整数
整数这里主要是位运算,常见知识点:
- 对于一个数
num
,num >> i & 1
表示取第i
位上的二进制值 - 设置
ans
二进制的第i位一般或运算:ans = 1 << i
- 异或两次后会自己抵消掉,相当于没干
004 只出现一次的数字
一个整数数组 nums
,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。
1 <= nums.length <= 3 * 104
-2^31 <= nums[i] <= 2^31 - 1
方法一:map遍历
1 | func singleNumber(nums []int) int { |
- 时间复杂度:O(n),其中 n 是数组的长度
- 空间复杂度:O(n)
方法二:依次确定二进制位
最先想到的肯定是和3做一些变换,如果看每一个二进制的话,加起来然后与3取余,最后汇总就行了
go的话不像python,是不是有符号自己能定义,这点注意一下就行了
1 | func singleNumber(nums []int) int { |
005 单词长度的最大乘积
给定一个字符串数组 words
,请计算当两个字符串 words[i]
和 words[j]
不包含相同字符时,它们长度的乘积的最大值。假设字符串中只包含英语的小写字母。如果没有不包含相同字符的一对字符串,返回 0。
很怪的题目,不过可以发现只包含小写字母,但是map又不太行,那就考虑位运算,把他们对应的整数做个&
1 | func maxProduct(words []string) (ans int) { |
006 排序数组中两个数字之和
给定一个已按照 升序排列 的整数数组 numbers
,请你从数组中找出两个数满足相加之和等于目标数 target
整数数组的形式返回答案,数组里放对应下标,增大的顺序
假设数组中存在且只存在一对符合条件的数字,同时一个数字不能使用两次
1 | 输入:numbers = [1,2,4,6,10], target = 8 |
方法一:map
只有一对符合要求,遇事不决,map解决,但空间O(n),并且数组已经排序而没用好这个性质
方法二:双指针
头(low
)尾( `heigh
)一块出发,二者的和记为sum
,如果比target
小,low++
,大的话heigh--
,相等直接输出。for循环条件是low<heigh
比较难考虑的是会不会错过去,可以这样想,反正low
和heigh
肯定会有第一个先到答案的位置,这个时候另一个必须根据sum
和target
的比较做配合,最后肯定能到相应的地方。
1 | func twoSum(numbers []int, target int) []int { |
方法三:二分查找
时间复杂度:O(nlogn),其中 n 是数组的长度。需要遍历数组一次确定第一个数,时间复杂度是 O(n),寻找第二个数使用二分查找,时间复杂度是 O(logn)
空间复杂度:O(1)
复杂度这块不如双指针,主要还是练习一下二分,目前不细说二分
1 | func twoSum(numbers []int, target int) []int { |
数组
这块就要寄出我曾经总结的东西了:算法总结——数组,当初打算给大一写的,但似乎没啥人看,不过个人感觉很全面,主要参考的代码随想录
数组主要知识点:
- 二分查找(莫得办法,多练)
- 前缀和(一维二维):计算给定区间的和
- 差分(一维二维):某一特定范围内的所有值都加上或减去一个常数
- 双指针
- 滑动窗口
- 螺旋矩阵(就是跟着逻辑来)
建议先看我的算法总结(・∀・)ノ
007 数组中和为 0 的三个数
【典型的双指针】
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意: 答案中不可以包含重复的三元组。
1 | 示例: |
思路:先排序,固定一个,另外俩双指针。
注意点:
- 但是固定的那个需要考虑去重,去重为了防止把之后的撅了,可以去和前一个比较
- 因为双指针遍历时碰到的答案不止一个,所以碰到正解后装入ans,left++(right–也行,反正就是离开舒适区),但是因为要去重,所以
for l < r && nums[l] == n2
内进行l++
(n2指之前的nums[l])
1 | func threeSum(nums []int) (ans [][]int) { |
008 和大于等于 target 的最短子数组
给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其和 ≥ target
的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回 0
。
二分查找
算法总结——数组,当初总结的,二分查找模块挺细的
left<right
时,一般left=mid+1
,right=mid
,而不是left=mid
,right=mid-1
,要不容易出现死循环
068 查找插入位置
给定一个排序的整数数组 nums
和一个整数目标值target
,请在数组中找到 target
,并返回其下标。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n)
的算法。
1 | func searchInsert(nums []int, target int) int { |
069 山峰数组的顶部
符合下列属性的数组 arr
称为 山峰数组(山脉数组) :
arr.length >= 3
- 存在i(
0 < i < arr.length - 1
) arr[0] < arr[1] < ... arr[i-1] < arr[i]
arr[i] > arr[i+1] > ... > arr[arr.length - 1]
给定由整数组成的山峰数组 arr
,返回任何满足 arr[0] < arr[1] < ... arr[i - 1] < arr[i] > arr[i + 1] > ... > arr[arr.length - 1]
的下标 i
,即山峰顶部。
3 <= arr.length <= 104
0 <= arr[i] <= 106
- 题目数据保证
arr
是一个山脉数组
虽然形式变了,但是我们可以变换比较方式,还是用二分查找,left<=right时最后return是什么可能不太好理解,最好自己找些数对比对比就出来了,当然最好还是left<right,把区间当成左闭右开,这样直接无脑返回右就行了(但其实左右是相等的)
1 | func peakIndexInMountainArray(arr []int) int { |
sort.search
使用模板index := sort.Search(n int,f func(i int) bool) int
使用二分查找的方法,会从[0, n)中取出一个值index,index为[0, n)中最小的使函数f(index)为True的值,并且f(index+1)也为True
如果无法找到该index值,则该方法为返回n
例如:
1 | func main() { |
1 | // 使用sort.search的做法 |
070 排序数组中只出现一次的数字
给定一个只包含整数的有序数组 nums
,每个元素都会出现两次,唯有一个数只会出现一次,请找出这个唯一的数字。
你设计的解决方案必须满足 O(log n)
时间复杂度和 O(1)
空间复杂度。
方法一:全数组的二分查找
相对好想一些,这个是保证每次比较时根据索引与对应的位置比较
- 如果 mid 是偶数,则比较 nums[mid] 和 nums[mid+1] 是否相等;
- 如果 mid 是奇数,则比较 nums[mid−1] 和 nums[mid] 是否相等。
利用按位异或的性质,可以得到 mid 和相邻的数之间的如下关系,其中 ⊕ 是按位异或运算符:
- 当 mid 是偶数时,mid+1=mid⊕1;
- 当 mid 是奇数时,mid−1=mid⊕1。
1 | func singleNonDuplicate(nums []int) int { |
方法二:偶数下标的二分查找
保证索引都是偶数,每次比较直接与+1位置比较
通过mid -= mid & 1
保证mid每次变为偶数
1 | func singleNonDuplicate(nums []int) int { |
071 按权重生成随机数
给定一个正整数数组 w
,其中 w[i]
代表下标 i
的权重(下标从 0
开始),请写一个函数 pickIndex
,它可以随机地获取下标 i
,选取下标 i
的概率与 w[i]
成正比。
例如,对于 w = [1, 3]
,挑选下标 0
的概率为 1 / (1 + 3) = 0.25
(即,25%),而选取下标 1
的概率为 3 / (1 + 3) = 0.75
(即,75%)。
也就是说,选取下标 i
的概率为 w[i] / sum(w)
方法:前缀和 + 二分查找
我人傻了,这思路,咋想出来的
例如[3,1,2,4],求前缀和[3,4,6,10],然后随机数落在1-10十个数之间,记这个数为x,再看看大于等于x的第一个位置是哪里,例如x=2,落在了1-2-3之间,所以3是选择的数。因为前缀和有序,且要查找第一个比x大的数,所以用二分查找。
1 | type Solution struct { |
072 求平方根
给定一个非负整数 x
,计算并返回 x
的平方根,即实现 int sqrt(int x)
函数
正数的平方根有两个,只输出其中的正数平方根
如果平方根不是整数,输出只保留整数的部分,小数部分将被舍去
1 | // 注意:要返回的是平方小于或等于x的第一个数 |
073 狒狒吃香蕉
狒狒喜欢吃香蕉。这里有 n
堆香蕉,第 i
堆中有 piles[i]
根香蕉。警卫已经离开了,将在 h
小时后回来。
狒狒可以决定她吃香蕉的速度 k
(单位:根/小时)。每个小时,她将会选择一堆香蕉,从中吃掉 k
根。如果这堆香蕉少于 k
根,她将吃掉这堆的所有香蕉,然后这一小时内不会再吃更多的香蕉,下一个小时才会开始吃另一堆的香蕉。
狒狒喜欢慢慢吃,但仍然想在警卫回来前吃掉所有的香蕉。
返回她可以在 h
小时内吃掉所有香蕉的最小速度 k
(k
为整数)。