数组中出现次数超过一半的数字
- 哈希表统计法: 遍历数组 nums ,用 HashMap 统计各数字的数量,即可找出 众数 。此方法时间和空间复杂度均为 O(N)
- 数组排序法: 将数组 nums 排序,数组中点的元素 一定为众数。
- 摩尔投票法: 核心理念为票数正负抵消。此方法时间和空间复杂度分别为 O(N)和 O(1),(遍历一边,只有两个变量m和i)为本题的最佳解法。
摩尔投票法
初始化元素m并给计数器i赋初值i = 0
对于输入队列中每一个元素x:
若i = 0, 那么 m = x and i = 1
否则若m = x, 那么 i = i + 1
否则 i = i − 1
返回 m
即便输入序列没有多数元素,这一算法也会返回一个序列元素。然而如果能够进行第二轮遍历,检验返回元素的出现次数,就能判断返回元素是否为多数元素。因此算法需要两次遍历,亚线性空间算法无法通过一次遍历就得出输入中是否存在多数元素。
推论一: 若记 众数 的票数为 +1,非众数 的票数为 −1,则一定有所有数字的 票数和 >0 。
推论二: (设众数为x,共n个数) 若数组的前 a 个数字的 票数和 =0,则 数组剩余 (n−a) 个数字的 票数和一定仍 >0 ,即后 (n−a) 个数字的 众数仍为 x
class Solution:
def majorityElement(self, nums: List[int]) -> int:
# 基准数m, 计数器i
m,i = nums[0], 0
for num in nums:
if i == 0:
m,i = num, 0
i += 1 if num == m else -1
# if num == m:
# i += 1
# else:
# i -= 1
return m
构建乘积数组
给定一个数组
A[0,1,…,n-1]
,请构建一个数组B[0,1,…,n-1]
,其中B[i]
的值是数组A
中除了下标i
以外的元素的积, 即B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]
。不能使用除法。
类似于动态规划的解法
- 其中两个三角都可以看成一个dp问题,
- 对于下半部分(绿三角)也就是b[n] = b[n-1]*a[n-1]; --> 同dp的方法
- 对于上半部分(红三角) b[n] = b[n+1] * a[n+1]; --> 建立一个变量,从下至上的累乘上去即可(注意从倒数第二行开始,遍历到第一行)
class Solution:
def constructArr(self, a: List[int]) -> List[int]:
# 初始化b,简历对应长度,每个元素都为1的list
b = [1]*len(a)
right_part = 1 # 从下向上的累乘逻辑
for i in range(1,len(a)):
b[i] = b[i-1]*a[i-1] # 下三角dp(绿)
# 右上三角是通过从下到上的累乘逻辑,每次多乘上一个 a[i+1]
# 注意最后一行是没有右上三角形的,也就是遍历需要从倒数第二行到第一行
for i in range(len(a)-2,-1,-1):
right_part *= a[i+1]
b[i] = b[i] * right_part
return b
和为S的连续序列
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
数学法
当前序列的平均值元素个数 = s*
- 确定左右边界 i,j
- s = (i+j)*(j-i+1)/2 <-- 高斯求和
-
s = (j^2 - i^2 + j + i)/2
- 通过遍历i,求出所有j,如果j为整数 j == int(j) 即返回当前list
class Solution:
def findContinuousSequence(self, target: int) -> List[List[int]]:
i,j = 1,2
result = []
while j>i:
# 公式、根据i求j
j = (-1 + (1 + 4 * (2 * target + i * i - i)) ** 0.5) / 2
if i
滑动窗口(双指针)
左边界 i 和右边界 j
判断i,j之间元素和和target的关系,如果大于target,j左移; 如果小于target,i右移
- 初始化 i,j = 1,2
- 循环,while j>i:
- 如果当前sum>target:需要把当前的最小数剃掉 s-=i,左边界+1,框入更少的元素
- 如果当前sum
class Solution:
def findContinuousSequence(self, target: int) -> List[List[int]]:
i,j = 1,2
s = 3
result = []
while j>i:
if s == target:
result.append(list(range(i,j+1)))
if s >= target:
s -= i
i +=1
elif s < target:
j += 1
s += j
return result
圆圈中最后剩下的数字
0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。
例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。
- 假设第一次删除的是第m%n个元素,那么剩下n-1的长度,通过F(n-1)可知如果从头做一遍,剩下第几个
- 由于题意,在第一次删除第m%n时,指针停留在那里并且继续向后遍历了;而f(n-1)是从头做的结果,所以在得知F(n-1)之后还要再加上这个第一次指针移动的距离,避免越界再用一次%
f(n, m) = (m % n + f(n-1,m) ) % n = (m + f(n-1,m) ) % n - 因此递归的计算 f(n,m), f(n-1,m), f(n-2,m),....
- stop case: f(1, m): 当序列长度为 1 时,一定会留下唯一的那个元素,它的编号为 0
class Solution:
def lastRemaining(self, n: int, m: int) -> int:
x = 0
for i in range(2, n + 1):
x = (x + m) % i
return x