import os
import torch
import numpy as np
import pandas as pd
import math
import random
from collections import OrderedDict, deque
from copy import deepcopy
案例1: 二分查找
def half_find(li, aim, start=0):
l = 0
r = len(li)
m = int(r/2)
if aim < li[m]:
# 此时是左边递增,最小值位于右边
return half_find(li[m+1:r], aim, start=m)
elif aim > li[m]:
# 最小值位于mid左面,包含mid
return half_find(li[l:m+1], aim, start=start)
else:
return m, li[m]
if __name__ == '__main__':
a = [12, 51, 81, 94, 111, 165, 198]
print( half_find(a, 94) )
案例2: 零和数组
【描述】 元素和最接近0的子数组
【思路】 记1—n元素和为s(n), 那么有两种情形, S(n)和S(m)-S(n)。其中第二种可以进行排序,取相邻两项做差。排序复杂度nlog(n).
def get_mini_sublist(li):
part1_index = {}
part2_index = {}
# 第一部分,前n项求和
temp = 0
for i in range(1, len(li)):
part1_index[(0, i)] = sum(li[:i])
part1_index = OrderedDict(sorted(part1_index.items(), key=lambda x:x[1] ))
# 第二部分,排序,做差
t1, t2 = deepcopy(part1_index), deepcopy(part1_index)
t1.popitem(last=True),
t2.popitem(last=False)
for i,j in zip(t1.items(), t2.items()):
part2_index[(i[0][1], j[0][1])] = j[1]-i[1]
# 融合,然后取值最小值
part2_index.update(part1_index)
key_min = min(part2_index, key=lambda x:abs(part2_index[x]))
return key_min, part2_index[key_min]
if __name__ == '__main__':
a = [-1, 32, -21, 22, 12, 11, -2, -12]
print(get_mini_sublist(a))
3. 旋转数组最小值
【描述】旋转数组,如[ -2, 3, 5, 6, 9, -10, -5], 特点是把拍好序的数组左边一部分移动到了右边。
【思路】二分查找
def find_min(li, posotion=0):
l = 0
r = len(li)
m = int(r/2)
if li[l] < li[m]:
# 此时是左边递增,最小值位于右边
return find_min(li[m+1:r], posotion=m)
elif li[l] > li[m]:
# 最小值位于mid左面,包含mid
return find_min(li[l:m], posotion=posotion)
else:
return li[m], m
if __name__ == '__main__':
li = [ -2, 3, 5, 6, 9, -10, -5]
print(find_min(li))
4. 调整数组顺序使奇数位于偶数前面
【描述】输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
【思路】创建双向队列,遍历数组,奇数前插入,偶数后插入。
# 双向队列实现
def reOrderArray1( array):
# write code here
odd = deque()
l = len(array)
for i in range(l):
if array[l-i-1] % 2 != 0:
odd.appendleft(array[-i-1])
if array[i] % 2 == 0:
odd.append(array[i])
return list(odd)
# 不开辟新空间
def reOrderArray2(li):
temp = 0 # 找出pop的偶数位数, 相应索引左移
for i in range(len(li)):
index = i - temp
if li[index] % 2 == 0:
li.append(li.pop(index))
temp += 1
return li
if __name__ == '__main__':
li = [ -2, 3, 5, 6, 9, -10, -5]
print( reOrderArray1(li) )
print( reOrderArray2(li) )
5. 快速找出数组中出现次数超过一半的数字
【思路】遍历数组,同时存储两个数,{数字值, 数组出现次数}, 出现相同数则次数+1, 不用-1, 若次数为0,则更换数字值。
考虑到次数超过一般, 那么相同次数应该为正, 保存的数字值应该是最多出现次数
def more_than_half_numb(li):
num, time, temp = 0, 0, 0
for i in li:
if not time:
temp = num = i
else:
if i == temp:
time += 1
else:
time -= 1
time +=1
if time <= 0:
num = i
return num
if __name__ == '__main__':
li = [1,2,3,2,2,2,5,4,2]
print(more_than_half_numb(li))
6. 最大和连续子数组
【思路】: 动态规划, 每次都比较 当前值 与 累加当前值
def find_max_sub_array(li):
store = {}
temp = li[0]
start, end, sum = 0, 0, temp
for i in range(1, len(li)):
current = temp + li[i]
if current >= temp:
end += 1
sum += current
else:
store[sum] = (start, end)
start, end = i, i
sum = li[i]
result = max(store)
return result, store[result]
if __name__ == '__main__':
li = [1, 2, 4, 5, -100, 1, -1]
print(find_max_sub_array(li))
7. 切绳子(可切可不切)
【思路】: 动态规划, 逐步计算
【贪心算法】: n>=5 时有 3(n-3)>=2(n-2),优先多3, 最后处理剩余的4, 5
# 动态规划
def cut(length):
product = {}
product[1] = 1
product[2] = 2 # 必需
product[3] = 3
for i in range(4, length+1):
result = max([product[i-j] * product[j] for j in range(1, i)]) #如果必须切, 此处(2, n)
product[i] = result
return product
if __name__ == '__main__':
print(cut(20))
8. 把数组排成最小的数
【思路】: 应用字符串比较最为方便
def join2max(li):
result = sorted(map(lambda x: str(x), a), reverse=True)
return int(''.join(result))
if __name__ == '__main__':
a = np.random.randint(1000,size=10)
print(join2max(a))
9. 数组中的逆序对
【题目】在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。
【思路】归并排序
class MergeSort:
def __init__(self):
self.num = 0
def _merge(self, a, b):
result = []
while a and b:
if a[0]<=b[0]:
result.append( a.pop(0) )
else:
result.append( b.pop(0) )
self.num += len(a)
result.extend(a if a else b)
return result
def __call__(self, li):
length = len(li)
if length == 1:
return li
else:
mid = int(len(li) / 2)
left = self.__call__(li[:mid])
right = self.__call__(li[mid:])
return self._merge(left, right)
if __name__ == '__main__':
a = list(np.random.randint(1, 100, 10))
sort = MergeSort()
print(sort([1, 3, 5, 2, 7]))
print('逆序对数量为: {}'.format(sort.num))