leetcode:448. 找到所有数组中消失的数字
问题描述:给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。找到所有在 [1, n] 范围之间没有出现在数组中的数字。不使用额外空间且时间复杂度为O(n)
解法:把元素的值作为下标,将下标对应的值取负,然后返回正数的下标
时间复杂度: O(n)
class Solution(object):
def findDisappearedNumbers(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
# 把元素的值作为下标,将下标对应的值取负,然后返回正数的下标
# 元素可能已经为负数 abs(i)
# 元素重复出现改变对应值 -abs(nums[abs(i) - 1])
for i in nums:
nums[abs(i) - 1] = -abs(nums[abs(i) - 1])
return [k + 1 for k, v in enumerate(nums) if v > 0]
leetcode:581. 最短无序连续子数组
问题描述:给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。
你找到的子数组应是最短的,请输出它的长度。
解法:将数组排序然后对比原数组不同的元素,最后一个不同元素的下标 - 第一个不同元素的下标 + 1
class Solution(object):
def findUnsortedSubarray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
sort_nums = sorted(nums)
res = [i for i, (v1, v2) in enumerate(zip(nums, sort_nums)) if v1 != v2]
if res == []:
return 0
else:
return max(res) - min(res) + 1
剑指offer:二维数组中的查找
问题描述:一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
解法:从第一行的最后一列数字,即右上角数字开始。若是数字大于目标,则往左移动(列);若是数字小于目标,则往下移动(行);直到找到目标,或者范围为空。
时间复杂度:0 (k)
# -*- coding:utf-8 -*-
class Solution:
# array 二维列表
def Find(self, target, array):
# i 行 j 列
i = 0
j = len(array[0]) - 1
while i < len(array) and j >= 0:
if array[i][j] == target:
return True
elif array[i][j] > target:
j -= 1
else:
i += 1
return False
leetcode:34. 在排序数组中查找元素的第一个和最后一个位置
问题描述:给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
解法:二分查找到数字之后,左右移动指针查找开始和结束位置
时间复杂度: O(log n)
class Solution:
# 二分查找
def find_med(self, nums, target):
l = 0
r = len(nums) - 1
while l <= r:
mid = (l + r) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
l = mid + 1
elif nums[mid] > target:
r = mid - 1
# 左右相同值查找
def searchRange(self, nums: List[int], target: int) -> List[int]:
res = []
if nums == [] or target is None:
return [-1, -1]
t = self.find_med(nums, target)
if t is None:
return [-1, -1]
else:
pre = t
while pre > 0 and nums[pre-1] == target:
pre -= 1
last = t
while last < len(nums) - 1 and nums[last+1] == target:
last += 1
# 输出开始位置和结束位置
return [pre, last]
剑指offer:斐波那契数列
问题描述:斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)
解法:递归解法会重复计算节点,用循环的方式从底至上计算避免了重复计算
时间复杂度:O(n)
class Solution:
def Fibonacci(self, n):
# write code here
res = [0, 1]
if n < 2:
return res[n]
n_sub_1 = 1
n_sub_2 = 0
f_n = 0
for i in range(n-1):
f_n = n_sub_1 + n_sub_2
n_sub_2 = n_sub_1
n_sub_1 = f_n
return f_n
剑指offer:跳台阶
问题描述:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)
解法:斐波那契数列的变形
时间复杂度:O(n)
class Solution:
def jumpFloor(self, number):
if number < 1:
return 0
elif number == 1:
return 1
elif number == 2:
return 2
else:
fib1 = 1
fib2 = 2
for i in range(number-2):
fibn = fib1 + fib2
fib1 = fib2
fib2 = fibn
return fibn
剑指offer:变态跳台阶
问题描述:一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
解法:
n个台阶:一次到位 + f(n-1) + f(n-2) + f(n-3) +...+ f(2) + f(1)
n-1个台阶:一次到位 + f(n-2) + f(n-3) +...+ f(2) + f(1)
2个台阶:一次到位 + f(1)
1个台阶:f(1) = 1
根据数学归纳法,当n=1时,显然成立;假设当n=k时(把式中n换成k,写出来)成立
证明
时间复杂度:O(1)
class Solution:
def jumpFloorII(self, number):
return 2**(number-1)
剑指offer:矩形覆盖
问题描述:我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
解法:竖着放,f(n-1);横着放,f(n-2)
时间复杂度:O(n)
class Solution:
def rectCover(self, number):
if number < 1:
return 0
elif number == 1:
return 1
elif number == 2:
return 2
else:
fib1 = 1
fib2 = 2
for i in range(number-2):
fibn = fib1 + fib2
fib1 = fib2
fib2 = fibn
return fibn
剑指offer:二进制中1的个数
问题描述:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
解法:
如果输入是负数,右移时最高位会设为1,从此陷入死循环。
常规解法——标记为1,不断左移标记1,与输入做与运算判断1的个数,但是这个解法需要考虑二进制的所有位数。
最佳解法——输入减1,与原输入做与运算,会把输入最右边的一个1变成0
时间复杂度:O(k)
# -*- coding:utf-8 -*-
class Solution:
def NumberOf1(self, n):
count = 0
if n < 0:
# 获得负数的补码
# python中负数(十进制)输出的是原码二进制加上负号,负数(十六进制)输出的是对应的补码,因此需要手动将其和十六进制数0xfffffffd进行按位与操作,得到十六进制数
n = n & 0xffffffff
while n:
n = (n - 1) & n
count += 1
return count
剑指offer:调整数组顺序使奇数位于偶数前面
问题描述:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
解法:
时间复杂度:O(n)
1.如果不要求奇数和偶数的相对位置不变,只要能奇数在前,偶数在后。解法如下:
两个指针,一个在前一个在后,分别指向奇数偶数,然后交换位置
def reOrderArray(array):
if array is None or len(array) <= 0:
return None
p_head = 0
p_tail = len(array) - 1
while p_head < p_tail:
# 找到偶数
while p_head < p_tail and array[p_head] % 2 != 0:
p_head += 1
# 找到奇数
while p_head < p_tail and array[p_tail] % 2 == 0:
p_tail -= 1
if p_head < p_tail:
array[p_head], array[p_tail] = array[p_tail], array[p_head]
p_head += 1
p_tail -= 1
return array
2.如果要求奇数和偶数的相对位置不变,解法如下:
方法一:构造奇数和偶数数列,分别存放元素,最后返回合并的数组
class Solution:
def reOrderArray(self, array):
# 奇数
odd_num = []
# 偶数
even_num = []
for i in array:
if i % 2 == 0:
even_num.append(i)
else:
odd_num.append(i)
return odd_num + even_num
方法二:从前往后遍历,指针指向可以存放的奇数位置,如果遇到奇数就往前冒泡直到奇数标志
class Solution:
def reOrderArray(self, array):
# 指向可以存放奇数的位置
odd_flag = 0
for i in range(len(array)):
# 找到奇数
if array[i] % 2 != 0:
for j in range(i, odd_flag, -1):
array[j], array[j-1] = array[j-1], array[j]
odd_flag += 1
return array
剑指offer:顺时针打印矩阵
问题描述:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
解法:一次打印矩阵的一个圈,假设矩阵为5x5,那么初始点分别是(0,0) (1,1) (2,2),那么循环打印的条件为rows > start * 2 且columns > start * 2,需要考虑只有一行、只有一列、只有一行一列的情况。
时间复杂度:O(n)
class Solution:
# matrix类型为二维列表,需要返回列表
def printMatrix(self, matrix):
if matrix is None:
return None
res = []
start = 0
# 行 row
rows = len(matrix)
# 列 column
columns = len(matrix[0])
while rows > start * 2 and columns > start * 2:
# 打印圈
endX = rows - 1 - start
endY = columns - 1 - start
# 从左往右,行不变;打印一圈至少走一步
for i in range(start, columns-start):
res.append(matrix[start][i])
# 从上往下,列不变;如果只有一行,不需要第二步,需要终止行>起始行
if start < endX:
for i in range(start+1, rows-start):
res.append(matrix[i][endY])
# 从右往左,行不变;第三步需要圈内至少两行两列,需要终止行>起始行,终止列>起始列
if start < endX and start < endY:
for i in range(endY-1, start-1, -1):
res.append(matrix[endX][i])
# 从下往上,列不变;第四步需要圈内至少三行两列,需要终止行>起始行+1,终止列>起始列
if start < endX-1 and start < endY:
for i in range(endX-1, start, -1):
res.append(matrix[i][start])
# 更新初始位置
start += 1
return res
leetcode:78. 子集
问题描述:给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
解法:动态规划,每新增一个元素则在原有基础上+[i]
f([1]) = [[], [1]]
f([1,2]) = [[], [1], [2], [1,2]] = f([1]) + [[]+[2], [1]+[2]]
时间复杂度:O(n)
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
# 初始化为二维数组
dp = [[]]
for i in range(len(nums)):
# print(dp)
# print([nums[i]])
# print([_ + [nums[i]] for _ in dp])
# print('###')
dp = dp + [_ + [nums[i]] for _ in dp]
return dp
面试问题:循环单词
如果一个单词通过循环右移获得的单词,我们称这些单词都为一种循环单词。例如:picture和turepic就是属于同一种循环单词。现在给出n个单词,需要统计这n个单词中有多少种循环单词。
解法:难点在于判断两个单词是不是循环单词、如何把几种单词记做一种。创建循环单词数组,如果单词不在循环数组里,则加入它的所有右移循环单词;如果单词在循环数组里,标记为False。将标记为True的元素放入结果数组,返回结果数组的长度。
def circle_word(lines):
if len(lines) == 0:
print(0)
list_word = []
res = []
for line in lines:
flag = True
if line in list_word:
flag = False
else:
for j in range(len(line) - 1, -1, -1):
# 单词右移,最后一位移到第一位
list_word.append(line[j:] + line[:j])
if flag:
res.append(line)
print(len(res))
if __name__ == '__main__':
a = ['word', 'ordw','picture','turepic','turepic','icturep','ps','sp']
circle_word(a)
面试问题:操作序列
小易有一个长度为n的整数序列,a_1,...,a_n。然后考虑在一个空序列b上进行n次以下操作:
1、将a_i放入b序列的末尾
2、逆置b序列
小易需要你计算输出操作n次之后的b序列。
解法:找规律。
首先根据题意写出数组直接转置的程序:
line = [1,2,3,4,5,6,7,8,9,10]
res = []
for i in line:
res.append(str(i))
res = res[::-1]
print(res)
结果为:['10', '8', '6', '4', '2', '1', '3', '5', '7', '9']
发现从从最后一个数字开始往前,跳一格;然后从前往后,也是跳一格,但是奇序列和偶序列有点区别,在从前向后输出时,奇序列从第二个数开始,偶序列从第一个开始
import sys
n = str(sys.stdin.readline().strip())
line = sys.stdin.readline().strip()
line = list(map(int, line.split()))
res = []
# 从最后一个数开始,从后向前输出,每次都跳过一个
for i in range(len(line)-1, -1, -2):
res.append(str(line[i]))
# 偶序列从第一个开始
if len(line) % 2 == 0:
for i in range(0, len(line)-1, 2):
res.append(str(line[i]))
# 奇序列从第二个数开始
else:
for i in range(1, len(line)-1, 2):
res.append(str(line[i]))
print(' '.join(res))
leetcode:665. 非递减数列
问题描述:
解法:
case1:如果没有一处n[i]>n[i+1],满足条件
case2:如果只有一处n[i]>n[i+1],那么只要通过修改n[i]或者n[i+1]来达到n[i-1]<=n[i]<=n[i+1]<=n[i+2]满足条件
只有一处n[i]>n[i+1],当n[i-1]<=n[i+1] || n[i]<=n[i+2]成立时即满足条件,当n[i-1]>n[i+1] && n[i]>[i+2]成立时即不满足条件
case3:如果n[i]>n[i+1]有两处及其以上,必然不满足条件
时间复杂度:O(N)
class Solution:
def checkPossibility(self, nums: List[int]) -> bool:
flag = 0
for i in range(len(nums)-1):
if nums[i] > nums[i+1]:
# 当修改n[i]时 需要保证n[i-1]<=n[i+1]即满足条件
# 当修改n[i+1]时 需要保证n[i]<=n[i+2]即满足条件
if 1 <= i < len(nums)-2 and nums[i+1] < nums[i-1] and nums[i+2] < nums[i]:
return False
flag += 1
# 如果只有0、1处n[i]>n[i+1]
if flag <= 1:
return True
else:
return False
剑指offer:数组中出现次数超过一半的数字
问题描述:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
解法一:哈希表,判断最大键值是否大于长度的一半
# -*- coding:utf-8 -*-
class Solution:
def MoreThanHalfNum_Solution(self, numbers):
dict1 = {}
for i in range(len(numbers)):
dict1[numbers[i]] = dict1.get(numbers[i], 0) + 1
# 最大值对应的键 max(dict1, key=dict1.get)
if dict1[max(dict1, key=dict1.get)] > len(numbers) / 2.0:
return max(dict1, key=dict1.get)
else:
return 0
解法二:保存两个值:数字,出现次数。
当下一个数字与保存的数字不同,次数减去一;
当下一个数字与保存的数字相同,次数加上一;
如果次数为零,保存下个数字,并且次数设置为一。
# -*- coding:utf-8 -*-
class Solution:
def MoreThanHalfNum_Solution(self, numbers):
res = numbers[0]
count = 1
for i in range(1, len(numbers)):
# 当下一个数字与保存的数字相同,次数加上一
if numbers[i] == res:
count += 1
# 当下一个数字与保存的数字不同,次数减去一
else:
count -= 1
# 如果次数为零,保存下个数字,并且次数设置为一
if count == 0:
count = 1
res = numbers[i]
count = 0
for i in range(len(numbers)):
if numbers[i] == res:
count += 1
# 检查一下找到的元素是否个数多于长度的一半
if count * 2 > len(numbers):
return res
else:
return 0
leetcode:892. 三维形体的表面积
问题描述:在 N * N 的网格上,我们放置一些 1 * 1 * 1 的立方体。每个值 v = grid[i][j] 表示 v 个正方体叠放在对应单元格 (i, j) 上。
请你返回最终形体的表面积。
解法:顶部和底部不会被遮挡,且当v = grid[i][j] > 0,有且只有2个面。四周东南西北,取决于grid[i][j]的个数,再减去上下左右遮挡它的个数(注意最小为0,不能加上负数)
时间复杂度:O(N*N)
class Solution:
def surfaceArea(self, grid: List[List[int]]) -> int:
# 顶部和底部不会被遮挡,且当v = grid[i][j] > 0,有且只有2个面
# 四周东南西北,取决于grid[i][j]的个数,再减去上下左右遮挡它的个数
res = 0
# 行
n = len(grid)
# 列
m = len(grid[0])
for i in range(n):
for j in range(m):
if grid[i][j] > 0:
res += 2
for r, c in ((i-1, j), (i, j-1), (i+1, j), (i, j+1)):
if 0 <= r < n and 0 <= c < m:
# 四周其中一个方向,需要扣除周围遮挡的个数
# grid[i][j] - grid[r][c] 可能为负值,不能加上负数
res += max(grid[i][j] - grid[r][c], 0)
else:
res += grid[i][j]
return res
剑指offer:连续子数组的最大和
问题描述:给一个数组,返回它的最大连续子序列的和
解法:初始化最大和为负无穷,因为如果元素为全为负数,则最大和也是负数,不能初始化为0;
如果和<=0,(当前元素+和)<=当前元素,则和更新为当前元素;
如果和>0,则返回当前元素+和。
时间复杂度:
# -*- coding:utf-8 -*-
class Solution:
def FindGreatestSumOfSubArray(self, array):
if not array:
return 0
cur_sum = 0
# 如果元素为全为负数,则最大和也是负数,因为不能初始化为0
res = float("-inf")
# 和必须大于0,否则当前元素加上和比当前元素还要小
for i in range(len(array)):
if cur_sum <= 0:
cur_sum = array[i]
else:
cur_sum += array[i]
res = max(cur_sum, res)
return res
剑指offer:从1到n整数中1出现的次数
问题描述:从1 到 n 中1出现的次数
解法:例如输入213,分别考虑个位、十位、百位为1的情况:
个位为1,个位为最高位有1种 [1] ;改变当前位的前面几位数有21种 [11,21,31,41,...,201,211] ;
十位为1,十位为最高位有4种 [10,11,12,13] ;改变当前位的前面几位数有20种 [110,111,...,119,210,211,212,213,014,....,019] ;
( [10-213] 分解为两部分:[10-13] [14-213] 来处理)
百位为1,百位为最高位有100种 [100-199] ;改变当前位的前面几位数有0种;
时间复杂度:
# -*- coding:utf-8 -*-
def NumberOf1Between1AndN_Solution(n):
res = 0
cur = n
count = 1
while cur:
# 求余数,例如123->3
final_n = cur % 10
# 整除,例如123->12
cur = cur / 10
# res: 当前位的前面几位,保持原始数的位数不变
res += cur * count
# print('当前位的前面几位: ', cur * count)
# res: 当前位作为最高位
# 如果当前位上是1,则n % count会得到后面几位数的值+1,例如123->24,百位为1,因为只能取到100-123,24个值
if final_n == 1:
res += n % count + 1
print('当前位作为最高位: ', n % count + 1)
# 如果当前位上不为1,例如213->100,百位为2,因为100-199都可以取到,100个值
elif final_n > 1:
res += count
print('当前位作为最高位: ', count)
# 从个位开始依次往上
count *= 10
# print('res = ', res)
return res
print(NumberOf1Between1AndN_Solution(213))
剑指offer:把数组排成最小的数
问题描述:输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
解法: python2 自定义排序函数。直接拼接数字,可能导致数值溢出,这是一个隐形的大数问题,需要把数字转换成字符串。然后需要定义一种比较两个数的规则,即把数字m和n拼接为mn,nm,只需要按照字符串大小的比较。
# -*- coding:utf-8 -*-
class Solution:
def PrintMinNumber(self, numbers):
if not numbers:
return ''
# 直接拼接数字,可能导致数值溢出,这是一个隐形的大数问题,需要把数字转换成字符串
str_list = map(str, numbers)
# 把数字m和n拼接为mn,nm,只需要按照字符串大小的比较
str_list.sort(lambda x, y: cmp(x+y, y+x))
return ''.join(str_list)
leetcode:179. 最大数
问题描述:给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数
解法:python3 自定义排序函数。定义一种比较两个数的规则,即把数字m和n拼接为mn,nm,只需要按照字符串大小的比较。
class Solution:
def largestNumber(self, nums: List[int]) -> str:
from functools import cmp_to_key
if not nums:
return ''
nums = map(str, nums)
key = cmp_to_key(lambda x, y: int(y + x) - int(x + y))
# lstrip() 方法: 截掉字符串左边的空格或指定字符 0012->12
res = ''.join(sorted(nums, key=key)).lstrip('0')
# 000->''
return res or '0'
剑指offer:丑数
问题描述:把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
解法:每一个丑数都是前面的丑数乘以2、3、5得到的,假设最大丑数为m,那么将丑数数组的每一个元素乘以2,取第一个大于m的值为m2,依次得到m3,m5。下一个丑数为m2 m3 m5 三个数中最小的一个。
时间复杂度:O(k)
# -*- coding:utf-8 -*-
class Solution:
def GetUglyNumber_Solution(self, index):
if index <= 0:
return 0
m2 = 1
m3 = 1
m5 = 1
# 最小的丑数是1
num = [1]
m = 1
if index == 1:
return 1
while len(num) < index:
# m2 m3 m5 三个数中最小的一个
m = min(m2 * 2, m3 * 3, m5 * 5)
while m2 * 2 <= m:
# 防止重复数组的出现
if m2 * 2 not in num:
num.append(m2 * 2)
m2 = num[num.index(m2) + 1]
while m3 * 3 <= m:
if m3 * 3 not in num:
num.append(m3 * 3)
m3 = num[num.index(m3) + 1]
while m5 * 5 <= m:
if m5 * 5 not in num:
num.append(m5 * 5)
m5 = num[num.index(m5) + 1]
return m
剑指offer:数字在排序数组中出现的次数
问题描述:统计一个数字在排序数组中出现的次数
解法:二分查找找到数组的第一个k和最后一个k的下标。还有一种简单做法是用python的count方法。
时间复杂度:O(logn)
# -*- coding:utf-8 -*-
# 思路一
class Solution:
def GetNumberOfK(self, data, k):
return data.count(k)
# 思路二
class Solution:
def GetNumberOfK(self, data, k):
def get_first(data, k):
start, end = 0, len(data)-1
while start <= end:
mid = (end + start) / 2
if data[mid] == k:
# 找到这个元素后判断是否为第一个 k
if mid == 0 or (mid > 0 and data[mid-1] != k):
return mid
else:
end = mid - 1
elif data[mid] < k:
start = mid + 1
else:
end = mid - 1
# 找不到这个元素
return -1
def get_last(data, k):
start, end = 0, len(data) - 1
while start <= end:
mid = (end + start) / 2
if data[mid] == k:
# 找到这个元素后判断是否为最后一个 k
if mid == len(data)-1 or (mid < len(data)-1 and data[mid+1] != k):
return mid
else:
start = mid + 1
elif data[mid] < k:
start = mid + 1
else:
end = mid - 1
# 找不到这个元素
return -1
# 如果数组不为空
if len(data) > 0:
first = get_first(data, k)
last = get_last(data, k)
# 如果能找到元素
if first > -1 and last > -1:
return last - first + 1
# 其余情况返回 0
return 0
剑指offer:数组中只出现一次的数字
问题描述:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
解法:0异或任何数不变,任何数与自己异或为0。a⊕b⊕a=b。异或满足加法结合律和交换律。
首先数组中所有元素依次异或,因为相同的元素异或得到0,所以最终的答案就等于2个出现一次的元素a^b的值。找到a^b的二进制表示中从右边数第一个为1的位,假如是第k位。而a,b两个数在第k位上是不同的,一个为0,一个为1 。然后将第k位是1的分成一组,第k位是0的分成一组,如果2个元素相同,那么他们第k位相同会进入同一组。在分组里面异或每一个元素,得到结果。
时间复杂度:O(n)
# -*- coding:utf-8 -*-
# 思路一:哈希表
class Solution:
# 返回[a,b] 其中ab是出现一次的两个数字
def FindNumsAppearOnce(self, array):
dict1 = {}
for i in array:
dict1[i] = dict1.get(i, 0) + 1
ans = []
for k, v in dict1.items():
if v == 1:
ans.append(k)
return ans
# 思路二:count方法
class Solution:
# 返回[a,b] 其中ab是出现一次的两个数字
def FindNumsAppearOnce(self, array):
return [i for i in array if array.count(i)==1]
# 思路三:异或
class Solution:
# 返回[a,b] 其中ab是出现一次的两个数字
def FindNumsAppearOnce(self, array):
# 找到二进制数的从右边数第一个为1的位置
def FindFirstOne(num):
pos = 0
while num & 1 == 0:
num = num >> 1
pos += 1
return pos
# 判断从右边数的index是不是1
def IsBit1(num, indexBit):
num = num >> indexBit
return num & 1
if not array or len(array) < 2:
return
res = 0
# 数组中所有元素依次异或
for i in array:
res ^= i
# 从0开始的位置
pos_index = FindFirstOne(res)
num1 = 0
num2 = 0
# 分为两种情况异或
for i in range(len(array)):
if IsBit1(array[i], pos_index):
num1 ^= array[i]
else:
num2 ^= array[i]
return num1, num2
剑指offer:和为S的连续正数序列
问题描述:输出所有和为S的连续正数序列(至少两个数)。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序
解法:用small和big代表序列的最小值和最大值,从1和2开始初始化。如果序列和大于s,则去掉较小值,增大small;如果小于s,则增大big。遍历在small到 (s + 1) / 2 为止。
# -*- coding:utf-8 -*-
class Solution:
def FindContinuousSequence(self, tsum):
ans = []
small = 1
big = 2
cur_sum = small + big
mid = (tsum + 1) / 2
# 如果tsum为11,则small的最大值不能超过6,因为6+7>11
# 遍历在small到 (s + 1) / 2 为止
while small < mid:
if cur_sum == tsum and small < big:
ans.append([i for i in range(small, big+1)])
# 如果序列和大于s,则去掉较小值,增大small
while cur_sum > tsum and small < big:
cur_sum -= small
small += 1
if cur_sum == tsum and small < big:
ans.append([i for i in range(small, big + 1)])
# 如果小于s,则增大big
big += 1
cur_sum += big
return ans
剑指offer:和为S的两个数字
问题描述:输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
解法:从两边到中间,两个指针遍历。两边的乘积是最小的。
时间复杂度:O(n)
# -*- coding:utf-8 -*-
class Solution:
def FindNumbersWithSum(self, array, tsum):
if not array or not tsum:
return []
l = 0
r = len(array) - 1
res = float('inf')
ans = []
while l < r:
cur_sum = array[l] + array[r]
# 找到第一个就是乘积最小的,直接退出
if cur_sum == tsum:
ans = (array[l], array[r])
break
elif cur_sum > tsum:
r -= 1
else:
l += 1
return ans
剑指offer:扑克牌的顺子
问题描述:从扑克牌随机抽取5张,判断是不是顺子。大小王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。如果牌能组成顺子就输出true,否则就输出false。
解法:把大小王定义为0,先把数组排序,然后统计数组中0的个数,如果相邻数字之间的空缺总数小于等于0的个数,则是连续的
时间复杂度:O(n)
# -*- coding:utf-8 -*-
class Solution:
def IsContinuous(self, numbers):
if not numbers:
return False
numbers.sort()
# 统计数组中0的个数
zero_num = len([i for i in numbers if i == 0])
# 统计数组中相邻数字之间的空缺总数
gap_num = 0
# 第一个非0位下标
small = zero_num
# 第二个非0位下标
big = small + 1
while big < len(numbers):
# 如果两数相等,不可能是顺子
if numbers[small] == numbers[big]:
return False
gap_num += numbers[big] - numbers[small] - 1
small = big
big += 1
# 如果相邻数字之间的空缺总数小于等于0的个数,则是连续的
return gap_num <= zero_num
剑指offer:圆圈中最后剩下的数字
问题描述:从0到n-1围成一个圈,从0开始删除第m个数字,求最后剩下的数字
解法:分析数字规律,
时间复杂度:
剑指offer:
问题描述:
解法:
时间复杂度: