参考:
题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。
数组中有一个数字出现的次数超过数组长度的一半,也就是说它出现的次数比其他所有数字出现次数的和还要多。因此我们可以考虑在遍历数组的时候保存两个值:
class Solution:
# 基于Partition函数的O(n)算法
def MoreThanHalfNum_Solution(self, numbers):
length = len(numbers)
if length == 1:
return numbers[0]
if self.CheckInvalidArray(numbers, length):
return 0
times = 0
for i in numbers:
if not times:
res = i
times += 1
if i == res:
times += 1
else:
times -= 1
return res if self.CheckMoreThanHalf(numbers, length, res) else 0
# 检查输入的数组是否合法
def CheckInvalidArray(self, numbers, length):
InputInvalid = False
if numbers == None or length <= 0:
InputInvalid = True
return InputInvalid
# 检查查找到中位数的元素出现次数是否超过所有元素数量的一半
def CheckMoreThanHalf(self, numbers, length, number):
times = 0
for i in range(length):
if numbers[i] == number:
times += 1
if times*2 <= length:
return False
return True
class Solution:
# 基于Partition函数的O(n)算法
def MoreThanHalfNum_Solution(self, numbers):
length = len(numbers)
if length == 1:
return numbers[0]
if self.CheckInvalidArray(numbers, length):
return 0
middle = length >> 1
start = 0
end = length - 1
index = self.Partition(numbers, length, start, end)
while index != middle:
if index > middle:
end = index - 1
index = self.Partition(numbers, length, start, end)
else:
start = index + 1
index = self.Partition(numbers, length, start, end)
result = numbers[middle]
if not self.CheckMoreThanHalf(numbers, length, result):
result = 0
return result
# 划分算法
def Partition(self, numbers, length, start, end):
if numbers == None or length <= 0 or start < 0 or end >= length:
return None
if end == start:
return end
pivotvlue = numbers[start]
leftmark = start + 1
rightmark = end
done = False
while not done:
while numbers[leftmark] <= pivotvlue and leftmark <= rightmark:
leftmark += 1
while numbers[rightmark] >= pivotvlue and rightmark >= leftmark:
rightmark -= 1
if leftmark > rightmark:
done = True
else:
numbers[leftmark], numbers[rightmark] = numbers[rightmark], numbers[leftmark]
numbers[rightmark], numbers[start] = numbers[start], numbers[rightmark]
return rightmark
# 检查输入的数组是否合法
def CheckInvalidArray(self, numbers, length):
InputInvalid = False
if numbers == None or length <= 0:
InputInvalid = True
return InputInvalid
# 检查查找到中位数的元素出现次数是否超过所有元素数量的一半
def CheckMoreThanHalf(self, numbers, length, number):
times = 0
for i in range(length):
if numbers[i] == number:
times += 1
if times*2 <= length:
return False
return True
题目:输入n个整数,找出其中最小的k个数。例如输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
第一种方法是基于划分的方法,如果是查找第k个数字,第一次划分之后,划分的位置如果大于k,那么就在前面的子数组中进行继续划分,反之则在后面的子数组继续划分,时间复杂度O(n);
def GetLeastNumbers_Solution(self, tinput, k):
if tinput == None or len(tinput) < k or len(tinput) <= 0 or k <=0:
return []
n = len(tinput)
start = 0
end = n - 1
index = self.Partition(tinput, n, start, end)
while index != k-1:
if index > k-1:
end = index - 1
index = self.Partition(tinput, n, start, end)
else:
start = index + 1
index = self.Partition(tinput, n, start, end)
output = tinput[:k]
output.sort()
return output
第二种方法是可以适用于海量数据的方法,该方法基于二叉树或者堆来实现,
题目:输入一个整型数组,数组里有正数也有负数。数组中一个或连续的多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。
class Solution:
def FindGreatestSumOfSubArray(self, array):
if array == None or len(array) <= 0:
return 0
maxSum = array[0]
s = array[0]
length = len(array)
for i in range(1,length):
s += array[i]
# 如果当前和小于等于当前数字,则抛弃数组中之前的数字
if s <= 0:
s = array[i]
if s > maxSum:
maxSum = s
return maxSum
如果用函数f(i)表示以第i个数字结尾的子数组的最大和,那么我们需要求出max[f(i)],其中0≤i
这个公式的意义:
# -*- coding:utf-8 -*-
class Solution:
def FindGreatestSumOfSubArray(self, array):
if array == None or len(array) <= 0:
return 0
length = len(array)
aList = [0]*length
aList[0] = array[0]
for i in range(1, length):
# 如果当前和小于等于当前数字,则抛弃数组中之前的数字
if aList[i-1]<=0:
aList[i] = array[i]
else:
aList[i] = aList[i-1] + array[i]
return max(aList)
题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。例如输入12,从1到12这些整数中包含1 的数字有1,10,11和12,1一共出现了5次。
思路2:将1-n全部转换为字符串,只需要统计每个字符串中’1’出现的次数并相加即可
class Solution:
def NumberOf1Between1AndN_Solution(self, n):
count = 0
for i in range(1,n+1):
for i in str(i):
if i == '1':
count += 1
return count
设N = abcde ,其中abcde分别为十进制中各位上的数字。如果要计算百位上1出现的次数,它要受到3方面的影响:百位上的数字,百位以下(低位)的数字,百位以上(高位)的数字。
① 如果百位上数字为0,百位上可能出现1的次数由更高位决定。比如:12013,则可以知道百位出现1的情况可能是:100-199,1100-1199,2100-2199,,…,11100-11199,一共1200个。可以看出是由更高位数字(12)决定,并且等于更高位数字(12)乘以 当前位数(100)。
② 如果百位上数字为1,百位上可能出现1的次数不仅受更高位影响还受低位影响。比如:12113,则可以知道百位受高位影响出现的情况是:100-199,1100-1199,2100-2199,,…,11100-11199,一共1200个。和上面情况一样,并且等于更高位数字(12)乘以 当前位数(100)。但同时它还受低位影响,百位出现1的情况是:12100~12113,一共114个,等于低位数字(113)+1。
③ 如果百位上数字大于1(2-9),则百位上出现1的情况仅由更高位决定,比如12213,则百位出现1的情况是:100-199,1100-1199,2100-2199,,…,11100-11199,12100~12199,一共有1300个,并且等于更高位数字+1(12+1)乘以当前位数(100)。
def NumberOf1Between1AndN_Solution(self, N):
#make sure that N is an integer
N = int(N)
#convert N to chars
a = str(N)
#the lenth is string a
n = len(a)
i = 0
count = 0
while (i < n):
if(i == 0):
if(int(a[i]) == 1 ):
count += int(a[1:])+1
elif(int(a[i]) > 1):
count += 10 ** (n-1)
elif(i == n - 1):
if(int(a[i]) == 0):
count += int(a[:n-1])
else:
count += int(a[:n-1]) + 1
else:
if(int(a[i]) == 0):
count += int(a[:i]) * (10 ** (n - i - 1))
elif(int(a[j]) == 1):
count += int(a[:i]) * (10 ** (n - i - 1)) + int(a[i+1:]) + 1
else:
count += (int(a[:i]) + 1) * (10 ** (n - i -1))
i += 1
return count
每次去掉最高位做递归,递归的次数和位数相同。一个数字n有O(logn)位,因此这种思路的时间复杂度是O(logn)
题目:输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这3个数字能排成的最小数字321323。
class Solution:
def quickSort(self, nums, start, end):
# 由于本算法是直接对数组进行改变,所以针对于非法输入的数组可以直接忽略
if end > start:
pivot = nums[-1]
i = j = start
while j < end:
s1 = nums[j] + pivot
s2 = pivot + nums[j]
if s1 < s2:
nums[i], nums[j] = nums[j], nums[i]
i += 1
j += 1
# 交换pivot和i索引的元素, 此时的j指向pivot
nums[i], nums[end] = nums[end], nums[i]
# 递归调用,对左右两部分排序
self.quickSort(nums, start, i-1)
self.quickSort(nums, i+1, end)
def PrintMinNumber(self, numbers):
if not numbers:
return ""
str_list = []
for n in numbers:
str_list.append(str(n))
length = len(numbers)
st = 0
en = length - 1
self.quickSort(str_list, st, en)
# 使用 python 自带的方法,将字符串列表连接起来
return ''.join(str_list)