目录
1013. 将数组分成和相等的三个部分
https://leetcode-cn.com/problems/partition-array-into-three-parts-with-equal-sum/
给定一个整数数组 A,只有我们可以将其划分为三个和相等的非空部分时才返回 true,否则返回 false。形式上,如果我们可以找出索引 i+1 < j 且满足 (A[0] + A[1] + ... + A[i] == A[i+1] + A[i+2] + ... + A[j-1] == A[j] + A[j-1] + ... + A[A.length - 1]) 就可以将数组三等分。
示例 1:输出:[0,2,1,-6,6,-7,9,1,2,0,1],输出:true。解释:0 + 2 + 1 = -6 + 6 - 7 + 9 + 1 = 2 + 0 + 1
示例 2:输入:[0,2,1,-6,6,7,9,-1,2,0,1]。输出:false
示例 3:输入:[3,3,6,5,-2,2,5,1,-9,4],输出:true,解释:3 + 3 = 6 = 5 - 2 + 2 + 5 + 1 - 9 + 4
提示:3 <= A.length <= 50000,-10000 <= A[i] <= 10000
思路
一:从前往后找,找出三个和均是总和1/3的区间。
class Solution(object):
def canThreePartsEqualSum(self, A):
"""
:type A: List[int]
:rtype: bool
"""
total = sum(A)
if total % 3 != 0:
return False
j, i, flag = 0, 0, False
for i in range(3):
target, flag = 0, False
while j < len(A):
target += A[j]
j += 1
if target == total // 3:
flag = True
break
if i == 2 and j == len(A) and flag:
return True
if i == 2 and flag:
target = total // 3
while j < len(A):
target += A[j]
j += 1
if target != total // 3:
return False
return True
return False
二:由两侧往中间寻找,若左侧右侧都有和为总和的1/3的话,且中间区域还有元素,则符合要求。
class Solution(object):
def canThreePartsEqualSum(self, A):
if len(A) <= 2:
return False
total = sum(A)
if total % 3 != 0:
return False
target = total // 3
l, r, l_ans, r_ans = 1, len(A) - 2, A[0], A[-1]
while l < r:
if l_ans != target:
l_ans += A[l]
l += 1
if r_ans != target:
r_ans += A[r]
r -= 1
# r + 1 > l确保中间还有元素
if l_ans == target and r_ans == target and r + 1 > l:
return True
return False
三:网上看的题解,虽然也提交通过了,但是存在特例的问题,例如[0,0,0,0]应该是True,但是程序返回False。
class Solution(object):
def canThreePartsEqualSum(self, A):
total = sum(A)
if total % 3 != 0:
return False
target, cnt = 0, 0
for j in range(len(A)):
target += A[j]
if target * 3 == total:
cnt += 1
target = 0
return cnt == 3
697. 数组的度
https://leetcode-cn.com/problems/degree-of-an-array/
给定一个非空且只包含非负数的整数数组 nums, 数组的度的定义是指数组里任一元素出现频数的最大值。你的任务是找到与 nums 拥有相同大小的度的最短连续子数组,返回其长度。
示例 1:输入: [1, 2, 2, 3, 1],输出: 2,解释: 输入数组的度是2,因为元素1和2的出现频数最大,均为2.连续子数组里面拥有相同度的有如下所示:[1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [2, 2],最短连续子数组[2, 2]的长度为2,所以返回2.
示例 2:输入: [1,2,2,3,1,4,2],输出: 6
注意:nums.length 在1到50,000区间范围内。nums[i] 是一个在0到49,999范围内的整数。
思路
一:滑动窗口,用字典rec记录每个元素出现的次数,并求出数组的度max_freq。再通过滑动窗口,每次保证右指针均向前移动,当右指针的元素在窗口中出现了max_freq次,则该窗口符合要求,我们寻找以右指针结尾的最短的符合要求的窗口,即向前移动左指针。
from collections import defaultdict
class Solution(object):
def findShortestSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
rec, max_freq = defaultdict(int), 0
for num in nums:
rec[num] += 1
max_freq = max(max_freq, rec[num])
if max_freq == 1:
return 1
l, rec_cur = 0, defaultdict(int)
res = len(nums)
for r in range(len(nums)):
rec_cur[nums[r]] += 1
if rec_cur[nums[r]] == max_freq:
while nums[l] != nums[r]:
rec_cur[nums[l]] -= 1
l += 1
res = min(res, r - l + 1)
return res
二:借鉴leetcode的题解https://leetcode-cn.com/problems/degree-of-an-array/solution/shu-zu-de-du-by-leetcode/,若num的元素出现频次是max_freq,即以第一个num起始至最后一个num结束,就是与
nums
拥有相同大小的度的连续子数组。用字典rec来记录一些信息,其中键为元素,值为一个列表,列表的第一个元素,是num第一次出现的位置,第二个元素是至今num最后出现的位置,第三个元素是num至今出现的次数。得到rec之后,我们只要列表第三个元素是max_freq的,则该列表的第二个元素减去第一个元素是其一个可行解,所有可行解中取最小。
class Solution(object):
def findShortestSubArray(self, nums):
rec, max_freq = {}, 0
for i, num in enumerate(nums):
if num not in rec:
rec[num] = [i, i, 0]
rec[num][1], rec[num][2] = i, rec[num][2] + 1
max_freq = max(max_freq, rec[num][2])
if max_freq == 1:
return 1
res = len(nums)
for k, pair in rec.items():
if pair[2] == max_freq:
res = min(res, pair[1] - pair[0] + 1)
return res
896. 单调数列
https://leetcode-cn.com/problems/monotonic-array/
如果数组是单调递增或单调递减的,那么它是单调的。如果对于所有 i <= j,A[i] <= A[j],那么数组 A 是单调递增的。 如果对于所有 i <= j,A[i]> = A[j],那么数组 A 是单调递减的。当给定的数组 A 是单调数组时返回 true,否则返回 false。
示例 1:输入:[1,2,2,3],输出:true
示例 2:输入:[6,5,4,4],输出:true
示例 3:输入:[1,3,2],输出:false
思路
一:两次遍历,分别看是否非递减、是否非递增。
class Solution(object):
def isMonotonic(self, A):
"""
:type A: List[int]
:rtype: bool
"""
if len(A) <= 1:
return True
flag_P, flag_N = True, True
for i in range(1, len(A)):
if A[i - 1] > A[i]:
flag_P = False
break
for i in range(1, len(A)):
if A[i-1] < A[i]:
flag_N = False
break
return flag_P or flag_N
二:借鉴https://leetcode-cn.com/problems/monotonic-array/solution/dan-diao-shu-lie-by-leetcode/官方题解,若数组单调则所有相邻两值的差都必须同号,cmp函数:要在一次遍历中执行该检查,我们将会处理由 {−1,0,1}组成的比较流,分别对应 <
,==
,或 >。
class Solution(object):
def isMonotonic(self, A):
if len(A) <= 1:
return True
store = 0
for i in range(1, len(A)):
c = self._cmp(A[i-1], A[i])
if c:
if c != store and store != 0:
return False
store = c
return True
def _cmp(self, a, b):
if a < b:
return -1
elif a == b:
return 0
else:
return 1
1232. 缀点成线
https://leetcode-cn.com/problems/check-if-it-is-a-straight-line/
在一个 XY 坐标系中有一些点,我们用数组 coordinates 来分别记录它们的坐标,其中 coordinates[i] = [x, y] 表示横坐标为 x、纵坐标为 y 的点。请你来判断,这些点是否在该坐标系中属于同一条直线上,是则返回 true,否则请返回 false。
示例 1:
输入:coordinates = [[1,2],[2,3],[3,4],[4,5],[5,6],[6,7]]
输出:true
思路
一:直接由数学公式来的,所有的点与第一个点的斜率相同,则是一条直线,因为涉及到除法,此处将0单独讨论了。
class Solution(object):
def checkStraightLine(self, coordinates):
"""
:type coordinates: List[List[int]]
:rtype: bool
"""
if len(coordinates) <= 2:
return True
n = len(coordinates)
dx, dy = [0] * (n - 1), [0] * (n - 1)
for i in range(1, n):
dx[i-1] = coordinates[i][0] - coordinates[0][0]
dy[i-1] = coordinates[i][1] - coordinates[0][1]
v, h = True, True
for i in range(0, len(dx)):
if dx[i] != 0:
v = False
break
for i in range(0, len(dy)):
if dy[i] != 0:
h = False
break
if v or h:
return True
for i in range(len(dx)):
if dx[i] == 0 or dy[i] == 0:
return False
theta = 1.0 * dy[0] / dx[0]
for i in range(len(dx)):
if 1.0 * dy[i] / dx[i] != theta:
return False
return True
二:为了避免单独处理0,可以将除法操作改为乘法操作。
class Solution(object):
def checkStraightLine(self, coordinates):
if len(coordinates) <= 2:
return True
n = len(coordinates)
dx, dy = [0] * (n - 1), [0] * (n - 1)
for i in range(1, n):
dx[i-1] = coordinates[i][0] - coordinates[0][0]
dy[i-1] = coordinates[i][1] - coordinates[0][1]
for i in range(1, len(dx)):
if dx[i] * dy[0] != dx[0] * dy[i]:
return False
return True
面试题53 - II. 0~n-1中缺失的数字
https://leetcode-cn.com/problems/que-shi-de-shu-zi-lcof/
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
示例 1:输入: [0,1,3],输出: 2
示例 2:输入: [0,1,2,3,4,5,6,7,9],输出: 8
限制:1 <= 数组长度 <= 10000
思路
一:因为有序,可用二分法,第一个不等于下标的元素的下标即为缺失值,此处r=len(nums),而不是len(nums)-1,是因为若只有一个元素,后者不会进入循环,则返回的是nums[0],这种是错误的。只要进去循环了,l必定是第一个不等于下标的元素的下标。
class Solution(object):
def missingNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if len(nums) == 1:
if nums[0] == 0:
return 1
return 0
l, r = 0, len(nums)
while l < r:
mid = l + (r - l) // 2
if nums[mid] <= mid:
l = mid + 1
else:
r = mid
return l
1018. 可被 5 整除的二进制前缀
https://leetcode-cn.com/problems/binary-prefix-divisible-by-5/
给定由若干 0 和 1 组成的数组 A。我们定义 N_i:从 A[0] 到 A[i] 的第 i 个子数组被解释为一个二进制数(从最高有效位到最低有效位)。返回布尔值列表 answer,只有当 N_i 可以被 5 整除时,答案 answer[i] 为 true,否则为 false。
示例 1:输入:[0,1,1],输出:[true,false,false],解释:输入数字为 0, 01, 011;也就是十进制中的 0, 1, 3 。只有第一个数可以被 5 整除,因此 answer[0] 为真。
示例 2:输入:[1,1,1],输出:[false,false,false]
示例 3:输入:[0,1,1,1,1,1],输出:[true,false,false,false,true,false]
示例 4:输入:[1,1,1,0,1],输出:[false,false,false,false,false]
提示:1 <= A.length <= 30000,A[i] 为 0 或 1
思路
一:直接移位,但是后面的数据会越来越大,python中貌似没有溢出,但是运行时间高达300ms+。
二:参照lee题code上的大神题解,https://leetcode-cn.com/problems/binary-prefix-divisible-by-5/solution/gen-ju-qu-mo-yun-suan-gui-ze-ji-suan-by-clementiv/,根据余数定理(a+b)%q=(a%q+b%q)%q,(a * b) % p = (a % p * b % p) % p
可以每添加一位求一次余数,然后在余数的基础上继续求下一位,依次类推。
class Solution(object):
def prefixesDivBy5(self, A):
"""
:type A: List[int]
:rtype: List[bool]
"""
res, mat = [False] * len(A), 0
for i in range(0, len(A)):
mat = mat * 2 + A[i]
if mat % 5 == 0:
res[i] = True
# 对5取余不改变最终结果,还会提高运行效率。
mat %= 5
return res
54. 螺旋矩阵
https://leetcode-cn.com/problems/spiral-matrix/
给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。
示例 1:
输入:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
输出: [1,2,3,6,9,8,7,4,5]
示例 2:
输入:
[
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12]
]
输出: [1,2,3,4,8,12,11,10,9,5,6,7]
思路
一:转载leetcode官方题解的法一,https://leetcode-cn.com/problems/spiral-matrix/solution/luo-xuan-ju-zhen-by-leetcode/,模拟。
直觉
绘制螺旋轨迹路径,我们发现当路径超出界限或者进入之前访问过的单元格时,会顺时针旋转方向。
算法
假设数组有 m行 n 列,visited[x][y]表示第 x 行第 y 列的单元格之前已经被访问过了。当前所在位置为 (x,y),前进方向是 di。我们希望访问所有 m*n个单元格。当我们遍历整个矩阵,下一步候选移动位置是 (tx ,ty)。如果这个候选位置在矩阵范围内并且没有被访问过,那么它将会变成下一步移动的位置;否则,我们将前进方向顺时针旋转之后再计算下一步的移动位置。
class Solution(object):
def spiralOrder(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: List[int]
"""
if not matrix or not matrix[0]:
return []
m, n = len(matrix), len(matrix[0])
visited = [[False] * n for _ in range(m)]
dx = [0, 1, 0, -1]
dy = [1, 0, -1, 0]
res, x, y, di = [], 0, 0, 0
for i in range(m * n):
res.append(matrix[x][y])
visited[x][y] = True
tx = x + dx[di]
ty = y + dy[di]
if not(0 <= tx < m and 0 <= ty <n) or visited[tx][ty]:
di += 1
di %= 4
x += dx[di]
y += dy[di]
return res
二:转载leetcode官方题解的法二,https://leetcode-cn.com/problems/spiral-matrix/solution/luo-xuan-ju-zhen-by-leetcode/,按层模拟
直觉
答案是最外层所有元素按照顺时针顺序输出,其次是次外层,以此类推。
class Solution(object):
def spiralOrder(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: List[int]
"""
if not matrix or not matrix[0]:
return []
m, n = len(matrix), len(matrix[0])
x1, x2, y1, y2 = 0, m - 1, 0, n - 1
res = []
while x1 <= x2 and y1 <= y2:
for j in range(y1, y2 + 1):
res.append(matrix[x1][j])
for i in range(x1 + 1, x2 + 1):
res.append(matrix[i][y2])
if x1 < x2 and y1 < y2:
for j in range(y2 - 1, y1 - 1, -1):
res.append(matrix[x2][j])
for i in range(x2 - 1, x1, -1):
res.append(matrix[i][y1])
x1 += 1
x2 -= 1
y1 += 1
y2 -= 1
return res
1346. 检查整数及其两倍数是否存在
https://leetcode-cn.com/problems/check-if-n-and-its-double-exist/
给你一个整数数组 arr,请你检查是否存在两个整数 N 和 M,满足 N 是 M 的两倍(即,N = 2 * M)。更正式地,检查是否存在两个下标 i 和 j 满足:i != j,0 <= i, j < arr.length,arr[i] == 2 * arr[j]
示例 1:输入:arr = [10,2,5,3],输出:true,解释:N = 10 是 M = 5 的两倍,即 10 = 2 * 5 。
示例 2:输入:arr = [7,1,14,11],输出:true,解释:N = 14 是 M = 7 的两倍,即 14 = 2 * 7 。
示例 3:输入:arr = [3,1,7,11],输出:false,解释:在该情况下不存在 N 和 M 满足 N = 2 * M 。
提示:2 <= arr.length <= 500,-10^3 <= arr[i] <= 10^3
思路
一:暴力解法,直接两重循环遍历。
二:排序+双指针,此方法参考leetcode官方题解,https://leetcode-cn.com/problems/check-if-n-and-its-double-exist/solution/jian-cha-zheng-shu-ji-qi-liang-bei-shu-shi-fou-cun/,此处将数组分为大于等于零和小于零排序,因为这两种情况指针移动方法不一样,例如x>=0时,r指针只需要一直前进,若在前进过程中找到一个比2x大的数字,则2x必然不存在,在l前进的过程中,l所指向的x会不断增大,2x 也会不断递增,因此指针 r 不需要后退。
class Solution(object):
def checkIfExist(self, arr):
"""
:type arr: List[int]
:rtype: bool
"""
a1 = [item for item in arr if item >= 0]
a1 = sorted(a1)
a2 = [item for item in arr if item < 0]
a2 = sorted(a2, reverse=True)
l, r = 0, 0
for l in range(len(a1)):
while r < len(a1) and a1[r] < 2 * a1[l]:
r += 1
if r < len(a1) and r != l and a1[l] * 2 == a1[r]:
return True
l, r = 0, 0
for l in range(len(a2)):
while r < len(a2) and a2[r] > 2 * a2[l]:
r += 1
if r < len(a2) and r != l and a2[l] * 2 == a2[r]:
return True
return False
面试题04. 二维数组中的查找
https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof/
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
示例:
现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。给定 target = 20,返回 false。
限制:0 <= n <= 1000,0 <= m <= 1000
思路
一:两重循环遍历每一个元素,时间复杂度O(m*n)
二:遍历每一层,对该层的元素用二分查找,时间复杂度O(m*lg(n)),在此基础上稍微优化了下,二分查找的右指针无须每次都从n开始,该右指针必定在上一层右指针的左边,直接用上一层右指针即可,无须重新赋值。
class Solution(object):
def findNumberIn2DArray(self, matrix, target):
"""
:type matrix: List[List[int]]
:type target: int
:rtype: bool
"""
if not matrix or not matrix[0]:
return False
m, n = len(matrix), len(matrix[0])
r = n
for i in range(m):
l = 0
while l < r:
mid = l + (r - l) // 2
if matrix[i][mid] == target:
return True
if matrix[i][mid] < target:
l = mid + 1
else:
r = mid
return False
三:copyleetcode的https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof/solution/jian-er-zhi-zhi-er-fen-cha-zhao-by-liweiwei1419-3/大神题解,时间复杂度O(m+n)。
class Solution(object):
def findNumberIn2DArray(self, matrix, target):
if not matrix or not matrix[0]:
return False
m, n = len(matrix), len(matrix[0])
r, c = 0, n - 1
while r < m and c >=0:
if matrix[r][c] == target:
return True
if matrix[r][c] < target:
r += 1
else:
c -= 1
return False
189. 旋转数组
https://leetcode-cn.com/problems/rotate-array/
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:输入: [1,2,3,4,5,6,7] 和 k = 3,输出: [5,6,7,1,2,3,4]
解释:向右旋转 1 步: [7,1,2,3,4,5,6],向右旋转 2 步: [6,7,1,2,3,4,5],向右旋转 3 步: [5,6,7,1,2,3,4]
示例 2:输入: [-1,-100,3,99] 和 k = 2,输出: [3,99,-1,-100]
解释: 向右旋转 1 步: [99,-1,-100,3],向右旋转 2 步: [3,99,-1,-100]
说明:尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。要求使用空间复杂度为 O(1) 的 原地 算法。
思路
一:暴力:旋转k次,每次讲数组旋转一个元素,时间复杂度O(k*n),超时
class Solution(object):
def rotate(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: None Do not return anything, modify nums in-place instead.
"""
if len(nums) <= 1:
return nums
n = len(nums)
k = k % n
for i in range(k):
last = nums[-1]
for j in range(n - 2, -1, -1):
nums[j + 1] = nums[j]
nums[0] = last
二:使用额外的数组,将每个元素直接放到最终的位置,即i放在(i+k)%n的位置上,时间复杂度O(n)。
class Solution(object):
def rotate(self, nums, k):
if len(nums) <= 1:
return nums
n = len(nums)
helper = nums[:]
for i in range(n):
nums[(i + k) % n] = helper[i]
三:借鉴官方题解方法三,https://leetcode-cn.com/problems/rotate-array/solution/xuan-zhuan-shu-zu-by-leetcode/,使用环状替换。
class Solution(object):
def rotate(self, nums, k):
if len(nums) <= 1:
return nums
n, i, pre, count = len(nums), 0, 0, 0
for pre in range(len(nums)):
if count >= len(nums):
return
idx, i = (pre + k) % n, pre
replace = nums[i]
while idx != pre:
idx = (i + k) % n
old = nums[idx]
nums[idx] = replace
replace = old
i = idx
count += 1
四:三次反转,转自https://leetcode-cn.com/problems/rotate-array/solution/xuan-zhuan-shu-zu-by-leetcode/官方题解,
class Solution(object):
def rotate(self, nums, k):
if len(nums) <= 1:
return nums
n = len(nums)
k = k % n
self._reverse(nums, 0, n - 1)
self._reverse(nums, k, n - 1)
self._reverse(nums, 0, k - 1)
def _reverse(self, nums, l, r):
while l < r:
nums[l], nums[r] = nums[r], nums[l]
l += 1
r -= 1