目录
【面试题3】:数组中重复的数字
题目描述:
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
解题思路:
1.充分利用题干信息。
首先判断当前值与下标是否相等。
相等的话,continue。不等的话,判断number[i] 和 number[number[i]]是否相等。
相等的话,就认为找到了。不等的话,就交换number[i] 和 number[number[i]],直到当前位置的值与下标相等位置。
时间复杂度:O(n)
# -*- coding:utf-8 -*-
class Solution:
# 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
# 函数返回True/False
def duplicate(self, numbers, duplication):
# write code here
for i in range(len(numbers)):
while numbers[i] != i:
if numbers[i] == numbers[numbers[i]]:
duplication[0] = numbers[i]
return True
#swap the numbers[i] and numbers[numbers[i]]
temp = numbers[i]
numbers[i] = numbers[temp]
numbers[temp] = temp
return False
2.哈希表
可以利用collections库中的Counter 函数,建立哈希表。
时间复杂度:O(n) 空间复杂度:O(n)
# -*- coding:utf-8 -*-
import collections
class Solution:
# 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
# 函数返回True/False
def duplicate(self, numbers, duplication):
# write code here
flag = False
hashTable = collections.Counter(numbers)
for item,times in hashTable.items():
if times > 1:
duplication[0] = item
return True
return False
【面试题4】二维数组的查找
题目描述:
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
解题思路:因为二维数组是有序的,因此假设从右上角开始查找,如果该值小于target,就在下一行寻找,如果该值大于target,就在上一列中寻找。直到找到,或者满足退出条件为止
时间复杂度:O(n)
# -*- coding:utf-8 -*-
class Solution:
# array 二维列表
def Find(self, target, array):
# write code here
row = 0
col = len(array[0]) - 1
while row < len(array) and col >= 0:
if array[row][col] > target:
col -= 1
elif array[row][col] < target:
row += 1
else:
return True
else:
return False
【面试题11】:旋转数组的最小数字
题目描述:
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
解题思路:
因为存在两个有序的子数组,因此可以考虑二分查找。
定义left和right,分别表示前一个数组最大的元素和后一个数组最小的元素。
若arr[left] 小于arr[mid],说明mid之前都是单调递增的数组,不会有最小值,因此在[mid,right]之间查找
若arr[left] 大于arr[mid],说明在[left,mid]之间存在两个数组的交界处,肯定存在最小值,因此在[mid,right]中寻找
当right和left相差1的时候,right对应的值就是最小值时,
时间复杂度:O(n)
# -*- coding:utf-8 -*-
class Solution:
def minNumberInRotateArray(self, rotateArray):
# write code here
length = len(rotateArray)
left = mid = 0
right = length - 1
while rotateArray[left]>rotateArray[right]:
if (right - left) == 1:
mid = right
break
mid = left + (right-left)//2
if rotateArray[left] == rotateArray[right] and rotateArray[left] == rotateArray[mid]:
return self.minOrder(rotateArray,left,right);
if rotateArray[mid] >= rotateArray[left]:
left = mid
else:
right = mid
return rotateArray[mid]
def minOrder(rotateArray,left,right):
for i in range(left,right+1):
if rotateArray[i] > rotateArray[i+1]:
return rotateArray[i+1]
【面试题21】:调整数组顺序使奇数位于偶数前面
题目描述:
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
解题思路:
新建一个数组,先遍历一遍把奇数放进去,在遍历一遍把偶数放进去
时间复杂度:O(n) ,空间复杂度为O(n)
class Solution:
def reOrderArray(self, array):
# write code here
return [i for i in array if i%2 != 0] + [i for i in array if i%2 ==0]
Python的列表解析式好用的可怕
【面试题22】:链表中倒数第k个结点
题目描述:输入一个链表,输出该链表中倒数第k个结点。
解题思路:开始时让两个指针指向头指针,然后让一个指针先走k-1步,然后在一起走,当先走的指针到达最后一个结点时,后走的指针指向的结点就是导数第k个结点
时间复杂度:O(n)
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def FindKthToTail(self, head, k):
# write code here
pSlow = pFast = head
i = 0
while pFast != None:
if i >= k:
pSlow = pSlow.next
pFast = pFast.next
i+=1
return pSlow if i >= k else None
【面试题25】:合并两个排序的链表
题目描述:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
解题思路:递归思路。
先判断两个链表link1和link2的第一个结点的值的大小,如果link1的结点值更小,就放在新链表link的头结点。
然后对link1->next和link2递归。反之,就把link2的头结点放在link的头结点。对link1和link2->next递归
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回合并后列表
def Merge(self, pHead1, pHead2):
# write code here
if pHead1 == None: return pHead2
if pHead2 == None: return pHead1
pMergedHead = None
if pHead1.val < pHead2.val:
pMergedHead = pHead1
pMergedHead.next = self.Merge(pHead1.next,pHead2)
else:
pMergedHead = pHead2
pMergedHead.next = self.Merge(pHead1,pHead2.next)
return pMergedHead
【面试题39】数组中出现次数超过一半的数字
题目描述:
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
解题思路:
因为有一个数字出现的字数比其他所有字数出现的次数之和还要多。因此遍历数组时记录数组中一个数字lastNum和次数。
当 遍历下一个数字时,如果与lastNum相等,count加1.如果不相等,count--。当count等于时,lastNum保存下一个数字,并count等于1
因此最后一次使count设为1的数就是要的解。
时间复杂度:O(n)
# -*- coding:utf-8 -*-
import collections
class Solution:
def MoreThanHalfNum_Solution(self, numbers):
# write code here
lastNum = numbers[0]
count = 1
for num in numbers[1:]:
if num == lastNum:
count += 1
else:
count -= 1
if count == 0:
lastNum = num
count = 1
print(num,lastNum,count)
if self.checkMoreThanHalf(numbers,lastNum):
return lastNum
else:
return 0
def checkMoreThanHalf(self,numbers,num):
c = collections.Counter(numbers)
if c[num] > len(numbers)//2:
return True
else:
return False
【面试题42】连续子数组的最大和
题目描述:
连续子数组的最大和。例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8
解题思路:
定义一个累计和cumsum和最大和maxsum。遍历数组,如果累计和大于最大和的话,更新最大和。如果累计和小于等于0的话,加上任何数都会使累积和减小,因此抛弃之前的子数组,从下一个数开始累积。
时间复杂度:O(n)
class Solution:
def FindGreatestSumOfSubArray(self, array):
# write code here
if len(array)<=0: return 0
cumSum = 0
maxSum = -float('inf')
for i in array:
cumSum += i
if cumSum > maxSum:
maxSum = cumSum
if cumSum <= 0:
cumSum = 0
continue
return maxSum
【面试题45】把数组排成最小的数
题目描述:
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
解题思路:
所以在这里自定义一个比较大小的函数,比较两个字符串s1, s2大小的时候,先将它们拼接起来,比较s1+s2,和s2+s1那个大,如果s1+s2大,那说明s2应该放前面,所以按这个规则,s2就应该排在s1前面。
时间复杂度:O(nlogn)
class Solution:
def PrintMinNumber(self, numbers):
Str = list(map(str,numbers)) #convert int to str
Str.sort(cmp=lambda a,b :cmp(a+b,b+a)) #按照自定义规则排序
return ''.join(Str) #拼接数组的值,后返回
【面试题51】数组中的逆序对
题目描述:
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
解题思路:
如果直接遍历数组,然后与后面所有的数字作比较的话,好理解,但是时间复杂度是O(n^2),因此需要寻找更快的方法。
归并思路:先把数组分割成子数组,先统计出子数组内部的逆序对的数目,然后再统计出两个相邻子数组之间的逆序对的数目。在统计逆序对的过程中,还需要对数组进行排序。如果对排序算法很熟悉,我们不难发现这个过程实际上就是归并排序。
时间复杂度:O(nlogn)
class Solution:
def InversePairs(self, data):
# write code here
if not data:
return 0
temp = [i for i in data]
return self.mergeSort(temp, data, 0, len(data)-1) % 1000000007
def mergeSort(self, temp, data, low, high):
if low >= high:
temp[low] = data[low]
return 0
mid = (low + high) / 2
left = self.mergeSort(data, temp, low, mid)
right = self.mergeSort(data, temp, mid+1, high)
count = 0
i = low
j = mid+1
index = low
while i <= mid and j <= high:
if data[i] <= data[j]:
temp[index] = data[i]
i += 1
else:
temp[index] = data[j]
count += mid-i+1
j += 1
index += 1
while i <= mid:
temp[index] = data[i]
i += 1
index += 1
while j <= high:
temp[index] = data[j]
j += 1
index += 1
return count + left + right
【面试题53】数字在排序数组中的次数
题目描述:
统计一个数字在排序数组中出现的次数。
解题思路:
利用二分查找,找到这个数字的第一次出现和最后一次出现的位置
如果没找到,就返回0
时间复杂度:O(logn)
class Solution:
def GetNumberOfK(self, data, k):
# write code here
if len(data) <= 0:
return 0
start = 0
end = len(data)-1
firstIndex = self.findFirstIndex(data,start,end,k)
endIndex = self.findEndIndex(data,start,end,k)
if firstIndex == -1 and endIndex == -1:
return 0
return endIndex - firstIndex + 1
def findFirstIndex(self,data,start,end,k):
if start > end:
return -1
Mid = (start+end)//2
if data[Mid] == k:
if (Mid == start) or (data[Mid-1]!=k):
return Mid
else:
end = Mid - 1
elif data[Mid] > k:
end = Mid - 1
else:
start = Mid + 1
return self.findFirstIndex(data,start,end,k)
def findEndIndex(self,data,start,end,k):
if start > end:
return -1
Mid = (start+end)//2
if data[Mid] == k:
if (Mid == end) or (data[Mid+1]!=k):
return Mid
else:
start = Mid + 1
elif data[Mid] > k:
end = Mid - 1
else:
start = Mid + 1
return self.findEndIndex(data,start,end,k)