剑指offer || 数组
剑指offer中有关数组的题目汇总
面试题3:数组中重复的数字
题目一:找出数组中重复的数字
在一个长度为n的数组里的所有数字都在0~n-1的范围内。数组中的数字是重复的
但不知道有几个数字重复了,也不知道每个数字重复几次。请找出数组中任意一个重复的数字。
输入:(2,3,1,0,2,5,3)
输出:2或3
思路
对数组进行排序
比较 与 ,令 ,
如果 , 继续扫描
,则比较
,则找到重复值
, 令
继续扫描
代码
#################面试题3#######################
#题目一:找出数组中重复的数字
def getDupliction(num):
i = 0
while i < len(num):
m = num[i]
if m >= len(num):
print('there are at least one number bigger than length of array!')
return
if i != m:
if m != num[m]:
num[i], num[m] = num[m], num[i]
else:
print(num[i])
i += 1
else:
i += 1
测试
#长度为n的数组里包含一个或多个重复的数字
num = [2,3,7,2,5,3,7]
getDupliction(num)
#数组中不包含重复的数字
num = [2,4,6,1,5,3,0]
getDupliction(num)
#返回None
#无效输入
getDupliction(None)
getDupliction([1,2])
分析
时间复杂度
空间复杂度
改变数组
题目二:不修改数组找出重复数字
在一个长度为 的数组里的所有数字都在 的范围内。所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。
输入:(2,3,5,4,3,2,6,7)
输出:2或3
思路
假如没有重复数字,那么在 的范围里只有 个数字,如果数组里超过n个数字,则一定包含了重复数字。(key point: 某个范围里特定的数字个数)
- 把 的数字从中间的数字 分为两部分
- 分别计算两区间的数字个数,在包含重复数字的区间继续进行二分查找
代码
#################面试题3#######################
#题目二:不修改数组找出重复数字
def countRange(num,start,end):
count = 0
for i in num:
if start <= i <= end:
count += 1
return count
def getDupliction(num):
if not num:
return None
if len(num) == 0:
return -1
L = len(num)
start = 1
end = L-1
while end >= start:
middle = start + (end - start) // 2
count = countRange(num,start,middle)
if start == middle:
if count > 1:
return start
else:
break
if count > middle - start +1 :
end = middle
else:
start = middle + 1
return -1
测试
#长度为n的数组里包含一个或多个重复的数字
num = [2,3,5,4,3,2,6,7]
getDupliction(num)
#数组中不包含重复的数字
num = [2,4,6,1,5,3,0]
getDupliction(num)
#返回-1
#无效输入
getDupliction(None) #返回None
getDupliction([1,3]) #返回 -1
分析
时间复杂度
空间复杂度
不改变数组,以时间换空间
面试题4:二维数组中的查找
题目:二维数组中的查找
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
输入:数组:[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]] 数字: 5
[[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]]
输出:False
思路
从数组的一个角上选取数字来和要查找的数字进行比较。
( 这里选取右上角)
- 如例子,选择9 与 5比较,5<9,则第4列均删除
- 问题变成在前3列数组中查找5,以此类推
代码
#################面试题#######################
#二维数组中的查找
class Solution:
# array 二维列表
def Find(self, target, array):
found = 0
rows = len(array)
if len(array) == 0:
return found
row,col = 0,len(array[0])-1
while col >= 0 and row < rows:
if target == array[row][col]:
found = 1
break
elif target < array[row][col]:
col -= 1
else:
row += 1
return found
测试
#二维数组中包含查找的数字
array = [[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]
s = Solution()
s.Find(1,array)
s.Find(15,array)
s.Find(10,array)
#二维数组中没有查找的数字
array = [[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]
s = Solution()
s.Find(14,array)
#返回None
#无效输入
s = Solution()
s.Find(14,Nonez)
面试题11:旋转数组的最小数字
题目:旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增序列的数组的旋转,输出旋转数组的最小元素。
输入: 旋转数组:[3,4,5,1,2] 原数字: [1,2,3,4,5]
输出:1
思路
旋转数组实际上可以分为两个排序的子数组。前面元素都大于或的等于后面子数组的元素。
最小元素刚好是这两个子数组的分界线。
(用二分法的思路来查找最小元素)
- 两个指针分别指向数组的第一个元素和最后一个元素
- 找数组中间元素,该元素位于前面的递增子数组,那么它大于或等于第一个指针指向的元素。
- 否则,位于后面数组
特例 [1,0,1,1,1,1]
当两个指针指向的数字以及它们中间的数字三者相同时候,无法判断中间数字属于前子数组还是后子数组。此时,采用顺序查找的方法。
代码
#################面试题11#######################
#旋转数组的最小数字
class Solution:
def minNumberInRotateArray(self, rotateArray):
# write code here
if rotateArray is None or len(rotateArray) == 0 :
return
L = len(rotateArray)
if L == 0:
return 0
elif L ==1:
return rotateArray[0]
p1 = 0
p2 = L-1
Mid = p1
while rotateArray[p1] >= rotateArray[p2]:
Mid = (p2 + p1) // 2
if rotateArray[p1] == rotateArray[Mid] == rotateArray[p2]:
minnum = rotateArray[p1]
for i in range(p1+1,p2):
if minnum >= rotateArray[i]:
minnum = rotateArray[i]
return minnum
if p1 - p2 == 1:
return rotateArray[p2]
if rotateArray[p1] < rotateArray[Mid]:
p1 = Mid + 1
if rotateArray[p2] > rotateArray[Mid ]:
p2 = Mid
return rotateArray[Mid]
测试
#功能测试
#输入的数组是升序排序数组的一个旋转,数组中有重复数字或者没有重复数字
s = Solution()
s.minNumberInRotateArray([5,6,7,8,1,2,3,4])
s.minNumberInRotateArray([1,0,1,1,1,1])
#边界值测试
#输入数组是一个升序排序的数组,只包含一个数字的数组
s = Solution()
s.minNumberInRotateArray([1,2,3,4,5,6,7,8])
s.minNumberInRotateArray([8])
#无效输入
s = Solution()
s.minNumberInRotateArray(None)
#牛客上解法
class Solution:
def minNumberInRotateArray(self, rotateArray):
length = len(rotateArray)
if length == 0:
return 0
if length == 1:
return rotateArray[0]
p1, p2 = 0, length - 1
while p1<= p2:
mid = (p1+ p2) >> 1
if rotateArray[p1] < rotateArray[p2]:
return rotateArray[p1]
if rotateArray[mid] > rotateArray[p2]:
p1 = mid+ 1
elif rotateArray[mid] < rotateArray[p2]:
p2 = mid
else:
p2 -= 1
if p1 >= p2:
break
return rotateArray[p1]
面试题21:调整数组顺序使奇数位于偶数前面
题目:调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。
拓展:调整数组顺序使奇数位于偶数前面,且需要保证相对位置不变
输入: 数组:[3,4,5,1,2]
输出:[2,4,5,1,3]
思路
基本解法:
发现偶数在奇数面前,交换它们的顺序
运用两个指针,分别指向数组的第一个和数组的最后一个
拓展解法:
交换之前先调整位置
代码
#################面试题21#######################
#调整数组顺序使奇数位于偶数前面
#基本解法
def ReorderOddEven(lst):
i = 0
j = len(lst)-1
while i <= j:
if lst[i] & 1 == 0 and lst[j] & 1 == 1:
lst[i] , lst[j] = lst[j],lst[i]
i += 1
j -= 1
elif lst[i] & 1 == 0 and lst[j] & 1 == 0:
j -= 1
elif lst[i] & 1 == 1 and lst[j] & 1 == 0:
i += 1
j -= 1
else:
i += 1
return lst
#拓展解法
def ReorderOddEven(lst):
if not lst:
return
right = 1
left = 0
while right < len(lst):
if lst[right] & 1 == 1 and lst[right-1] & 1 == 0:
odd = lst[right]
for i in range(right,left,-1):
lst[i] = lst[i - 1]
lst[left] = odd
right += 1
left += 1
elif lst[right] & 1 == 1 and lst[right-1] & 1 == 1:
right += 1
elif lst[right] & 1 == 0 and lst[right - 1] & 1 == 1:
left = right
right += 1
elif lst[right] & 1 == 0 and lst[right - 1] & 1 == 0:
right += 1
return lst
测试
#功能测试
#输入的数组中奇数、偶数交替出现
lst = [1,2,11,4,1,6,1,1,3,3,3,3]
ReorderOddEven(lst)
#特殊输入
ReorderOddEven(None)
ReorderOddEven([1])
面试题29:顺时针打印矩阵
题目:顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字
输入: 数组:
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
思路
按照圆圈来打印,注意边界条件
代码
#################面试题21#######################
#顺时针打印矩阵
class Solution:
def PrintMatrixInCircle(startcol,startrow,cols, rows,numbers):
if cols == 0 or rows == 0:
return
if cols == 1:
for i in range(startrow, startrow + rows):
print(numbers[i][startcol])
return
elif rows == 1:
for j in range(startcol, startcol + cols):
print(numbers[startrow][j])
return
else:
# 从左往右打印
for i in range(startcol, startcol + cols):
print(numbers[startrow][i])
# 从上到下打印
for i in range(startrow + 1, startrow + rows):
print(numbers[i][startrow + rows - 1])
# 从右往左打印
for i in range(startcol + cols - 2, startcol - 1, -1):
print(numbers[startrow + rows - 1][i])
# 从下往上打印
for i in range(startrow + rows - 2, startrow, -1):
print(numbers[i][startcol])
def PrintMatrixClockwisely(self,numbers,cols,rows):
minOfColsAndRows = min(cols,rows)
if minOfColsAndRows == 1:
Solution.PrintMatrixInCircle(0, 0, cols, rows, numbers)
return
if minOfColsAndRows & 1 == 1:
rounds = minOfColsAndRows//2
else:
rounds = minOfColsAndRows // 2 - 1
for i in range(rounds):
Solution.PrintMatrixInCircle(i,i,cols,rows,numbers)
cols -= 2
rows -= 2
class Solution:
# matrix类型为二维列表,需要返回列表
def printMatrix(self, matrix):
# write code here
def PrintMatrixInCircle(startcol , startrow, cols, rows, numbers):
numLst = []
if cols == 0 or rows == 0:
return False
if cols == 1:
for i in range(startrow, startrow + rows):
numLst.append(numbers[i][startcol])
return numLst
elif rows == 1:
for j in range(startcol, startcol + cols):
numLst.append(numbers[startrow][j])
return numLst
else:
# 从左往右打印
for i in range(startcol, startcol + cols):
numLst.append(numbers[startrow][i])
# 从上到下打印
for i in range(startrow + 1, startrow + rows):
numLst.append(numbers[i][startcol+cols-1])
# 从右往左打印
for i in range(startcol + cols - 2, startcol - 1, -1):
numLst.append(numbers[startrow + rows - 1][i])
# 从下往上打印
for i in range(startrow + rows - 2, startrow, -1):
numLst.append(numbers[i][startcol])
return numLst
cols = len(matrix[0])
rows = len(matrix)
numbers = matrix
minOfColsAndRows = min(cols, rows)
numLst = []
if minOfColsAndRows == 1:
numLst.extend(PrintMatrixInCircle(0, 0, cols, rows, numbers))
return numLst
if minOfColsAndRows & 1 == 1:
rounds = minOfColsAndRows // 2 + 1
else:
rounds = minOfColsAndRows // 2
for i in range(rounds):
numLst.extend(PrintMatrixInCircle(i, i, cols, rows, numbers))
cols -= 2
rows -= 2
return numLst
测试
#功能测试
#数组中有多行多列
numbers= [[1,2],[3,4],[5,6],[7,8],[9,10]]
s = Solution()
s.printMatrix(numbers)
#数组中有一行
numbers= [[1,2]]
s = Solution()
s.printMatrix(numbers)
#数组中有一列
numbers= [[1],[2]]
s = Solution()
s.printMatrix(numbers)
#数组中有一行一列
numbers= [[1]]
s = Solution()
s.printMatrix(numbers)
面试题39:数组中出现次数超过一半的数字
题目:数组中出现次数超过一半的数字
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
输入: 数组:[1,2,3,2,2,2,6,4,2]
输出:2
思路
解法一:基于Partition函数的时间复杂度为O(n)的算法
超过一半的数字是排序后数组的中位数,采用快速排序的思想
解法二:根据数组特点找出时间复杂度为O(n)的算法
思路:数组中有一个数字出现次数超过数组长度的一半,也就是说,它出现的次数比其他所有数字出现次数和还要多
遍历数组时保存两个值,数组中的数字 以及 次数
(1) 如果下一个数与当前数字相同,times + 1
(2) 如果不同,则times - 1 ,如果times == 0,则保存下一个数,times = 1
代码
#################面试题39#######################
#数组中出现次数超过一半的数字
class Solution:
def CheckMoreThanHalf(self,numbers,length,num): #检查该数在数组中个数是否超过一半
count = 0
for i in range(length):
if num == numbers[i]:
count += 1
if count > length >> 1: # length >> 1 等价于 length // 1
return True
else:
return False
def partition(self,numbers,start,end):
K = numbers[start]
small = start # small 小于标杆数字部分数组(也就是左边数组)的最后一个元素
i = start + 1
while i < end + 1 :
if numbers[i] >= K:
i += 1
else:
small += 1
numbers[i],numbers[small] = numbers[small],numbers[i]
i += 1
numbers[small],numbers[start] = numbers[start],numbers[small]
return small
def MoreThanHalfNum_Solution(self, numbers):
if numbers == None:
return None
length = len(numbers)
if length == 1:
return numbers[0]
middle = length >> 1
start = 0
end = length - 1
Index = self.partition(numbers,start,end)
if Index > middle:
end = Index - 1
Index = self.partition(numbers,start,end)
else:
start = Index + 1
Index = self.partition(numbers,start,end)
result = numbers[middle]
if not self.CheckMoreThanHalf(numbers,length,result):
result = 0
return result
测试
################测试1#################
#输入的数组中存在一个出现次数超过数组长度一半的数字
numbers = [1,2,3,3,3,3,3,4,3]
s = Solution()
s.MoreThanHalfNum_Solution(numbers) #return 3
################测试2#################
#输入的数组中不存在一个出现次数超过数组长度一半的数字
numbers = [1,2,3,3,4,5,6,8,7]
s = Solution()
s.MoreThanHalfNum_Solution(numbers) #return 0
################测试3#################
#输入的数组中只有一个数字
numbers = [1] #数组中只有一个数,则返回本身
s = Solution()
s.MoreThanHalfNum_Solution(numbers) #return 1
################测试4#################
#输入None
s = Solution()
s.MoreThanHalfNum_Solution(None) #return None
##########partition函数测试##############
def partition(numbers, start, end):
K = numbers[start]
small = start # small 小于标杆数字部分数组(也就是左边数组)的最后一个元素
i = start + 1
while i < end + 1:
if numbers[i] >= K:
i += 1
else:
small += 1
numbers[i], numbers[small] = numbers[small], numbers[i]
i += 1
numbers[small], numbers[start] = numbers[start], numbers[small]
return small
numbers = [3,2,3,2,2,2,5,4,2]
start = 0
end = len(numbers)-1
partition(numbers, 0, len(numbers)-1)
##########partition函数测试##############
解法二:根据数组特点找出时间复杂度为O(n)的算法
思路:数组中有一个数字出现次数超过数组长度的一半,也就是说,它出现的次数比其他所有数字出现次数和还要多
遍历数组时保存两个值,数组中的数字 以及 次数
(1) 如果下一个数与当前数字相同,times + 1
(2) 如果不同,则times - 1 ,如果times == 0,则保存下一个数,times = 1
代码
#################面试题39#######################
#数组中出现次数超过一半的数字
class Solution:
def CheckMoreThanHalf(self,numbers,length,num): #检查该数在数组中个数是否超过一半
count = 0
for i in range(length):
if num == numbers[i]:
count += 1
if count > length >> 1: # length >> 1 等价于 length // 1
return True
else:
return False
def MoreThanHalfNum_Solution(self, numbers):
if numbers == None:
return None
length = len(numbers)
if length == 1:
return numbers[0]
times = 1
result = numbers[0]
for i in range(1,len(numbers)):
if times == 0:
result = numbers[i]
times = 1
elif result == numbers[i]:
times += 1
else:
times -= 1
if not self.CheckMoreThanHalf(numbers, length, result):
result = 0
return result
测试
################测试1#################
#输入的数组中存在一个出现次数超过数组长度一半的数字
numbers = [1,2,3,3,3,3,3,4,3]
s = Solution()
s.MoreThanHalfNum_Solution(numbers) #return 3
################测试2#################
#输入的数组中不存在一个出现次数超过数组长度一半的数字
numbers = [1,2,3,3,4,5,6,8,7]
s = Solution()
s.MoreThanHalfNum_Solution(numbers) #return 0
################测试3#################
#输入的数组中只有一个数字
numbers = [1] #数组中只有一个数,则返回本身
s = Solution()
s.MoreThanHalfNum_Solution(numbers) #return 1
################测试4#################
#输入None
s = Solution()
s.MoreThanHalfNum_Solution(None) #return None
面试题42:连续子数组的最大和
题目:数组中出现次数超过一半的数字
输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。
输入: 数组:[1,-2,3,10,-4,7,2,-5]
输出:最大的子数组为$[3,10,-4,7,2] ,因此输出该数组的和为18
思路
动态规划思想:
i f(i) = pData[i] if i=0 f(i-1) <=0 f(i) = f(i-1) + pData[i] if i!=0 f(i-1) > 0 max(f(i))$
以第
个数字结尾的子数组中所有数字的和小于0时
,把这个负数与第i个数累加
,得到结果比
还小。该情况下,
代码
#################面试题42#######################
#连续子数组的最大和
def FindGreatestSumOfSubArray(Array):
if not Array:
return
n = len(Array)
maxSumArrayOfsubArray = [0] *(n+1)
maxSum = float('-inf')
for i in range(1,n+1):
if maxSumArrayOfsubArray[i-1] <= 0:
maxSumArrayOfsubArray[i] = Array[i-1]
else:
maxSumArrayOfsubArray[i] = maxSumArrayOfsubArray[i-1] + Array[i-1]
if maxSum < maxSumArrayOfsubArray[i]:
maxSum = maxSumArrayOfsubArray[i]
return maxSum
测试
################测试1#################
#功能测试
Array = [1,-2,3,10,-4,7,2,-5]
result = FindGreatestSumOfSubArray(Array)
print(result)
Array = [-2,-3,-10,-4,-7,-2,-5]
result = FindGreatestSumOfSubArray(Array)
print(result)
Array = [2,3,10,4,7,2,5]
result = FindGreatestSumOfSubArray(Array)
print(result)
#特殊输入测试
result = FindGreatestSumOfSubArray(None)
print(result)
面试题43:1~n整数中1出现的次数
题目:1~n整数中1出现的次数
输入一个整数n,求1~n这n个整数的十进制表示中1出现的次数,
输入: 12
输出:1~12这些整数中包含1的数字有1、10、11、12,一共出现5次
思路
数字规律着手:
每次去掉最高位后进行递归
例子21345
分为两段 1-1345 1346-21345
在1346-21345中,1出现分为两种情况
(1)1出现在最高位 10000 - 19999 一共10000次
(2)1出现在除高位之外的其他四位数中的情况
1346 - 21345 中后四位数中1出现的次数是8000次
把1346-21345 再分为2段 1346-11345 11346-21345
每一段剩下的4位数中,选择其中一位是1.其余三位可以再0~9这10个数字中任意选择
总共出现次数为2 * 4 * 1000 = 8000
代码【未完成】
#################面试题43#######################
#1~n整数中1出现的次数
测试
################测试1#################
#功能测试
面试题44:数字序列中某一位的数字
题目:数字序列中某一位的数字
数字以0123456789101112131415…..的格式序列化到一个字符序列中,在这个序列中,第5位是5,(从0计),第13位是1,第19位是4,请写一个函数,求任意第n位对应的数字
输入: n= 5 字符串0123456789101112131415.....
输出:5
思路
数字规律着手:
个位数:10个 0-9
两位数:180个 (90*2) 10 - 99
三位数:270个 (900 * 3) 100 - 999
代码
#################面试题44#######################
#数字序列中某一位的数字
def digitAtIndex(index):
if index < 0:
return -1
digits = 1
sumOfcount = 0
while True:
sumOfcount += countOfIntegers(digits) * digits
if index < sumOfcount:
indexOfpreDigit = (index - sumOfcount + countOfIntegers(digits) * digits)
number = beginNumber(digits) + indexOfpreDigit // digits
indexOfnumber = index % digits
return str(number)[indexOfnumber]
digits += 1
return -1
# 输入位数,返回该位数共有多少个数字
def countOfIntegers(digits):
if digits < 1:
return False
if digits == 1:
return 10
return (pow(10, digits) - pow(10, digits - 1))
def beginNumber(digits):
if digits < 1:
return False
if digits == 1:
return 0
return pow(10, digits - 1)
测试
################测试1#################
# 功能测试
digitAtIndex(10)
digitAtIndex(190)
digitAtIndex(1000)
# 边界值测试
digitAtIndex(0)
digitAtIndex(1)
面试题45:把数组排成最小的数
题目:数字序列中某一位的数字
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个
输入: [3,32,321]
输出:则打印出这3个数字能排成的最小数字321323
思路
代码【未完成】
#################面试题44#######################
#数字序列中某一位的数字
测试
################测试1#################
# 功能测试
面试题49:丑数
题目:数字序列中某一位的数字
数定义:只包含因子2、3、5的数称为丑数。求从小到大的顺序的第1500个丑数。
例子:6、8是丑数 14不是 1为第一个丑数
思路
根据丑数的定义,丑数应该是另一个丑数乘以2、3、5的结果(1除外)
关键:确保丑数是排序的
采用数组来存储已有的丑数,其中最大的丑数为M
已有的每个丑数 * 2 ,超过M的第一个结果为M2
已有的每个丑数 * 3 ,超过M的第一个结果为M3
已有的每个丑数 * 5 ,超过M的第一个结果为M5
下一个丑数为 min(M2、M3、M5)
存在T2使得它前面的丑数乘以2都不超过M,同理有T3 T5
代码
#################面试题49#######################
#丑数
class Solution:
def GetUglyNumber_Solution(self, index):
# write code here
if index <= 0:
return 0
pUglyNumbers = [None] * index
pUglyNumbers[0] = 1
nextUglyIndex = 1
T2Index = 0
T3Index = 0
T5Index = 0
while nextUglyIndex < index:
# M = pUglyNumbers[nextUglyIndex-1]
pUglyNumbers[nextUglyIndex] = min(pUglyNumbers[T2Index] * 2, pUglyNumbers[T3Index] * 3,
pUglyNumbers[T5Index] * 5)
while pUglyNumbers[T2Index] * 2 <= pUglyNumbers[nextUglyIndex]:
T2Index += 1
while pUglyNumbers[T3Index] * 3 <= pUglyNumbers[nextUglyIndex]:
T3Index += 1
while pUglyNumbers[T5Index] * 5 <= pUglyNumbers[nextUglyIndex]:
T5Index += 1
nextUglyIndex += 1
ugly = pUglyNumbers[nextUglyIndex - 1]
return pUglyNumbers
测试
################测试1#################
# 功能测试
# 输入2、3、4、5、6
s = Solution()
s.GetUglyNumber_Solution(2)
s.GetUglyNumber_Solution(5)
# 特殊输入测试
s = Solution()
s.GetUglyNumber_Solution(1)
s.GetUglyNumber_Solution(0)
# 性能测试
s = Solution()
s.GetUglyNumber_Solution(1500)
面试题50:第一个只出现一次的字符
题目一:字符串中第一个只出现一次的字符
字符串中第一个只出现一次的字符
输入: 'abaccdeff'
输出:'b'
思路
计算每个字符出现的次数,用字典存储
利用数组来创建简单哈希表 根据ascii码值 也可以直接采用字典
扫描两遍,第一遍计算次数 第二编读
扩展1:从第一个字符串中删除在第二个字符串中出现的所有字符
思路:从头到尾扫描第一个字符串,判断其是否是第二个字符串的
扩展2:删除字符串中所有重复出现的字符
思路:从头到尾扫描字符串,若出现,则设置为True
python中可以考虑用集合存储
扩展3:查询两个单词是否是变位词(变位词:字母相同,位置不同)
思路:统计第一个字符串字符出现次数,从头到尾扫描第二个字符串。相应减一
题目二:字符流中第一个只出现一次的字符
思路
利用数组来存储,第一遍扫描数组,记录
重新扫描数组,找到整个数组中最小的大于等于0的值
代码【未完成】
#################面试题50#######################
#第一个只出现一次的字符
class Solution:
def FirstNotRepeatingChar(self, string):
# 题目一
if not string:
return
stringDic = {}
for s in string:
if s not in stringDic:
stringDic[s] = 1
else:
stringDic[s] += 1
for s in string:
if stringDic[s] == 1:
return s
def FirstNotRepeatingstring(self, string):
# 题目二
if not string:
return 0
stringDic = {}
Firstcharlist = []
# i = 0
# s = string[i]
for i, s in enumerate(string):
if i == 0:
Firstcharlist.append(string[0])
stringDic[s] = 1
continue
if s not in stringDic:
stringDic[s] = 1
if not Firstcharlist[i - 1]:
Firstcharlist.append(s)
else:
Firstcharlist.append(Firstcharlist[i - 1])
else:
stringDic[s] = -1 # 重复出现
append = False
for j in range(i):
if stringDic[string[j]] > 0:
Firstcharlist.append(string[j])
append = True
break
if not append:
Firstcharlist.append(None)
return Firstcharlist
def DeleteString2FromString1(self, string1, string2):
if not string2:
return string1
if not string1:
return None
string = ''
for s in string1:
if s not in string2:
string += s
return string
def DeleteRepeatingChar(self, string):
if not string:
return
newstring = ''
stringSet = set()
for s in string:
if s not in stringSet:
stringSet.add(s)
newstring += s
return newstring
def If2stringsAreAnagram(self, string1, string2):
if not string1 or not string2:
return False
stringDic = {}
for s in string1:
if s not in stringDic:
stringDic[s] = 1
else:
stringDic[s] += 1
for s in string2:
if s not in stringDic:
return False
else:
stringDic[s] -= 1
if stringDic[s] == 0:
result = True
else:
result = False
return result
测试
################测试#################
# 仅仅测试读入字符流
# 功能测试
# 读入一个字符
s = Solution()
s.FirstNotRepeatingstring('a')
# 读入多个字符
s.FirstNotRepeatingstring('ahsdssas')
# 读入多个字符,字符都是唯一的
s.FirstNotRepeatingstring('ahs')
# 读入所有的字符都是重复的
s.FirstNotRepeatingstring('aaa')
# 特殊输入测试 None
s.FirstNotRepeatingstring(None)
面试题51:数组中的逆序对
题目:数组中的逆序对
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对,输入一个数组,求这个数组中的逆序对的总数
输入: [7 5 6 4]
输出:一共存在5个逆序对,分别是(7,6) (7,5) (7,4) (6,4),(5,4)
思路
先把数组分隔成子数组,统计出子数组内部的逆序对的数目
然后再统计出两个相邻子数组之间的逆序对的数目
涉及到对数组进行排序
代码【未完成】
#################面试题44#######################
#数组中的逆序对
测试
################测试1#################
# 功能测试
面试题53:在排序数组中查找数字
题目一:数字在排序数组中出现的次数
统计一个数字在排序数组中出现的次数
输入:排序数组[1、2、3、3、3、3、4、5] 和数字3
输出:4
思路
二分查找,确定重复出现的数字的第一个 和最后一个 的位置
用二分法查找确定数组中第一个
数组中间数字与k作比较,
, 在数组前半段
, 在数组后半段
,判断 是不是第一个
前一个数字不是 ,则是第一个,前一个是 ,则在前半段
类似可求数组中最后一个
代码
#################面试题53#######################
#在排序数组中查找数字
class Solution:
def GetFirstK(self,data,k,start,end):
if start > end:
return -1
middleIndex = (start + end) // 2
middleData = data[middleIndex]
if middleData == k:
if middleIndex > 0 and data[middleIndex-1] != k or middleIndex == 0:
return middleIndex
else:
end = middleIndex - 1
elif middleData > k:
end = middleIndex -1
else:
start = middleIndex + 1
return self.GetFirstK(data,k,start,end)
def GetLastK(self,data,k,start,end):
if start > end:
return -1
middleIndex = (start + end) // 2
middleData = data[middleIndex]
if middleData == k:
if middleIndex < len(data) - 1 and data[middleIndex+1] != k or middleIndex == len(data) - 1:
return middleIndex
else:
start = middleIndex + 1
elif middleData > k:
end = middleIndex -1
else:
start = middleIndex + 1
return self.GetLastK(data,k,start,end)
def GetNumberOfK(self, data, k):
if not data:
return 0
length = len(data)
if data and length > 0:
first = self.GetFirstK(data,k,0,length-1)
last = self.GetLastK(data,k,0,length-1)
if first > -1 and last > -1:
return last - first + 1
else:
return 0
测试
################测试1#################
#功能测试
#数组中包含要查找的数字
data = [1,2,3,3,3,3,4,5]
k = 3
s=Solution()
s.GetNumberOfK(data,k)
#数组中没有要查找的数字
data = [1,2,3,3,3,3,4,5]
k = 6
s=Solution()
s.GetNumberOfK(data,k)
#边界值测试
#查找数组中的最大值、最小值
data = [1,2,3,3,3,3,4,5]
k = 5
s=Solution()
s.GetNumberOfK(data,k)
#特殊输入
data = None
k = 5
s=Solution()
s.GetNumberOfK(data,k)
题目二:0~n-1中缺失的数字
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字钟有且只有一个数字不在该数组中,请找出该数字
思路
排序数组,下标与数组中数字相似,转换为在排序数组中找出第一个值和下标不相等的元素
利用二分法查找
中间元素和下标相等,下一轮查找右边
中间元素的值和下标不相等,并且它前面一个元素和它下标相等,该值则为第一个不相等元素,下标则为数组不存在的数字
如果中间元素的值和下标不相等,前面一个元素和它的下标不相等,下一轮查找左半边
代码
#################面试题53#######################
#0~n-1中缺失的数字
class Solution:
def GetMissingNumber(self,numbers):
if not numbers:
return -1
length = len(numbers)
left = 0
right = length - 1
while left <= right:
middle = (right + left) // 2
if numbers[middle] != middle:
if middle == 0 or numbers[middle-1] == middle -1:
return middle
right = middle - 1
else:
left = middle + 1
if left == length:
return length
return -1
测试
################测试1#################
#功能测试
numbers = [0,1,2,3,4,6,7,8,9]
s=Solution()
s.GetMissingNumber(numbers)
numbers = [1,2,3,4,5,6,7,8,9]
s=Solution()
s.GetMissingNumber(numbers)
numbers = [0,1,2,3,4,5,6,7,8]
s=Solution()
s.GetMissingNumber(numbers)
#边界值测试
numbers = [0]
s=Solution()
s.GetMissingNumber(numbers)
#特殊输入测试
s=Solution()
s.GetMissingNumber(None)
题目三:数组中数值和下标相等的元素
假设一个单调递增的数组里的每个元素都是整数并且是唯一的。找出数组中任意一个数值等于其下标的元素
输入:数组[-3,-1,1,3,5]
输出:数字3和它下标相等
思路
二分法,假设中间数字为m,下标为i
, 查找左半边
,查找右半边
,即得到结果
代码
#################面试题53#######################
#数组中数值和下标相等的元素
class Solution:
def GetNumberSameAsIndex(self,numbers):
if not numbers:
return -1
length = len(numbers)
left = 0
right = length - 1
while left <= right:
middle = (left + right) >> 1
if numbers[middle] == middle:
return middle
if numbers[middle] > middle:
right = middle - 1
else:
left = middle + 1
return -1
def GetNumberSameAsIndex2(self,numbers):
if not numbers:
return -1
length = len(numbers)
left = 0
right = length - 1
while left <= right:
middle = (left + right) >> 1
if numbers[middle] == middle :
NumberSameAsIndex = [middle]
while middle != 0 and numbers[middle-1] == middle-1:
NumberSameAsIndex.append(middle-1)
middle -= 1
while middle != length-1 and numbers[middle + 1] == middle + 1:
NumberSameAsIndex.append(middle+1)
middle += 1
if len(NumberSameAsIndex) == 1:
return middle
else:
NumberSameAsIndex.sort()
return NumberSameAsIndex
if numbers[middle] > middle:
right = middle - 1
else:
left = middle + 1
return -1
测试
################测试1#################
#功能测试
numbers = [-3,-1,1,3,5]
s=Solution()
s.GetNumberSameAsIndex(numbers)
numbers = [1,2,3,4,5,6,7,8,9]
s=Solution()
s.GetNumberSameAsIndex(numbers)
numbers = [0,1,2,3,4,5,6,7,8]
s=Solution()
s.GetNumberSameAsIndex2(numbers)
#边界值测试
numbers = [0]
s=Solution()
s.GetNumberSameAsIndex(numbers)
#特殊输入测试
s=Solution()
s.GetNumberSameAsIndex(None)
面试题56:数组中数字出现的次数
题目一:数组中只出现一次的两个数字
一个整形数组里除两个数字之外,其他数字都出现了两次。找出这两个数字,时间复杂度是O(n),空间复杂度是O(1)
思路
异或性质:任何一个数字异或自己都等于0
- 先考虑数组中指出现一次的数字
- 把原本数组的数字分为两组,使得不同的数字分别出现在两个组中
- 采用该数字二进制表示中右边第n位是否为1来划分
代码【需修改】
#################面试题56#######################
#数组中只出现一次的两个数字
class Solution:
def FindNumsAppearOnce(self, data):
if not data:
return
LEN = len(data)
if LEN < 2:
return
resultExclusiveOR = 0
for i in range(LEN):
resultExclusiveOR ^= data[i]
indexOf1 = self.FindFirstBitIs1(resultExclusiveOR)
num1 = num2 = 0
for j in range(LEN):
if self.IsBit1(data[j],indexOf1):
num1 ^= data[j]
else:
num2 ^= data[j]
return num1,num2
def FindFirstBitIs1(self, num):
indexBit = 0
while (num & 1) == 0 and indexBit < 8 * 32:
num = num >> 1
indexBit += 1
return indexBit
def IsBit1(self, num,indexBit):
num = num >> indexBit
return num & 1
测试【未完成】
################测试1#################
# 功能测试