二分法
# 二分法
# 时间复杂度O(logN)
'''
思路:
先取得该列表的索引中间值mid,如len(li)==0,则没有这个值
判断如li[mid]>最终值,则后再在递归调用前半部分,
判断如li[mid]<最终值,则后再在递归调用后半部分,如li[mid]=最终值,就找到结果.
'''
def binarysearch(li,value):
if len(li)==0:
print('not got it')
return
mid = len(li) // 2
if li[mid] == value:
print('got it')
return mid
elif li[mid] > value:
binarysearch(li[:mid], value)
elif li[mid] < value:
binarysearch(li[mid+1:], value)
binarysearch(li, value)
快速排序
'''
#快速排序
好写的排序算法里最快的
快的排序算法里最好写的
快排思路:
取一个元素p(第一个元素),使元素p归位;
列表被p分成两部分,左边都比p小,右边都比p大;
递归完成排序。
详细思路:
归位第一个值,归位值的前半段和后半段进行排序
归位:
最左边的值定义tmp
如左边的索引大于右边,且最右边的值大于等于tmp,最右的索引-1.最左边的值赋值给最右边
如左边的索引大于右边,且最右边的值小于等于tmp,最左的索引+1.最右边的值赋值给最左边
如左边的索引非大于右边,那temp就还是最左边的值
返回最左边值的索引
'''
### O(n)
def partition(li, left, right):
tmp = li[left]
while left < right:
while left < right and li[right] >= tmp:
right = right - 1
li[left] = li[right]
while left < right and li[left] <= tmp:
left = left + 1
li[right] = li[left]
li[left] = tmp
return left
## 时间复杂度: O(nlogn)
def _quickSort(li, left, right):
if left < right:
mid = partition(li, left, right) ### O(n)
_quickSort(li, left, mid - 1) ### O(logn)
_quickSort(li, mid + 1, right)
@caltime
def quickSort(li, left, right):
_quickSort(li, left, right)
计数排序
现在有一个列表,列表中的数范围都在0到100之间,列表长度大约为100万。设计算法在O(n)时间复杂度内将列表进行排序。
'''
计数排序:创建一个列表,用来统计每个数出现的次数。
思路:
count定义循环列出1到10的数(按需求),如果li中有值在count中,定义count[index]计数+1
清空li
枚举count,把计算添加进li
'''
def countSort(li):
count = [0 for i in range(11)]
for index in li:
count[index] += 1
li.clear()
for index, val in enumerate(count):
print(index, val)
for i in range(val):
li.append(index)
li = [10,4,6,3,8,4,5,7]
countSort(li)
print(li)
li = [random.randint(1,100) for _ in range(10000)]
BubbleSort(li)
li = [random.randint(1,100) for _ in range(10000)]
selectSort(li)
li = [random.randint(1,100) for _ in range(10000)]
insertSort(li)
li = [random.randint(1,100) for _ in range(100000)]
print(li)
quickSort(li, 0, len(li)-1)
print(li)
现在有n个数(n>10000),设计算法,按大小顺序得到前10大的数。
'''
现在有n个数(n>10000),设计算法,按大小顺序得到前10大的数。
应用场景:榜单TOP 10
解析:
因为只要最大的十个数,所以没有必要将整个数据进行排序,因为剩下的数据是否有序不影响结果。
所以可以新建一个数量为10的数组,并将这个数组进行排序,使其有序。
然后从第11位开始取数据,拿取到的数据和十位的列表中的最小的那个做比较,如果不够大就继续循环取数,如果比最小的数大,就把取出的数据覆盖掉最小的数,并再对十位的数组排序。直至数据取完,十位数组里面储存的就是最大的十个数字。
按照这个思路可以用插入排序或者堆排序实现,下面用的是插入排序。
'''
# 将一个数组按照左大右小顺序排好
def inser_sort(list):
for i in range(1, len(list)):
tem = list[i]
j = i - 1
while j >= 0 and list[j] < tem:
list[j + 1] = list[j]
j = j - 1
list[j + 1] = tem
def topk(li, k):
list = li[0:k] # 创建一个长度为k的数组来储存最大的k个数
inser_sort(list) # 将这个K数组先按照大小顺序用插入偶排序排好
print(list)
print(list[-1])
for i in range(k, len(li)): # 将剩下的数字依次拿到
# 将拿到的数字和数组中最小的数字做对比
if li[i] > list[-1]: # 如果比最小的数字大,就做交换,把最小的数字换成取到的数
list[-1] = li[i]
# 交换之后进行排序
inser_sort(list)
print(list)
给定一个列表和一个整数,找到两个数的下标,使得这两个数的各为给定的整数,保证肯定仅有一个结果
'''
算法例子一:
给定一个列表和一个整数,找到两个数的下标,
使得这两个数的各为给定的整数,保证肯定仅有一个结果
'''
# 穷举法:
def brute_force(li,target):
n=len(li)
for i in range(0,n):
for j in range(i+1,n):
if li[i]+li[j]==target:
return i,j
# 二分查找法:
def bin_search(li, val):
low = 0
high = len(li) - 1
while low <= high:
mid = (low + high) // 2
if li[mid] == val:
return mid
elif li[mid] > val:
high = mid - 1
else:
low = mid + 1
return None
def search_index(li, target):
li.sort()
for i in range(0, len(li)):
j=bin_search(li[i + 1:], target - li[i])
if j:
return i,j
'''
方法三
先给列表排序,然后循环遍历列表,如果列表第一个数与列表最后一个数相加的和大于target,把被加数向左偏移一位,
如果列表第一个数与列表最后一个数相加的和小于target,把加数向右偏移一位
如果列表中两个数相加等于target,则返回列表中的两个数的下标
'''
def search_index(li,target):
li.sort()
j=len(li)-1
for i in range(j):
if li[i] + li[j] < target:
i += 1
elif li[i] + li[j] > target:
j -=1
else:
return i,j
给定一个升序列表和一个整数,返回该整数在列表中的下标范围
'''
算法例子二:给定一个升序列表和一个整数,返回该整数在列表中的下标范围
思路:先使用二分法找到val在列表中的下标,然后把下标分别向左和向中移动,直到下标的值不等于目标整数时返回下标的元组
'''
def bin_search(li,val):
low=0
high=len(li)-1
while low <= high:
mid=(low + high) // 2
if li[mid] == val:
return mid
elif li[mid] > val:
high = mid -1
else:
low=mid + 1
return None
def search_index(li,val):
i=0
j=0
mid=bin_search(li,val)
i=mid-1
j=mid + 1
while li[i] ==val:
i -= 1
while li[j] == val:
j += 1
return (i+1,j-1)
two_sum求两数之和
# two_sum求两数之和
# 给定一个列表和一个整数,设计算法找到两个数的下标,使得两个数之和为给定的整数,保证肯定仅有一个结果.
# 例:列表li=[0, 1, 2, 3, 4, 5, 6]与目标整数6,结果为{0: 6, 1: 5, 2: 4, 3: 3}.
li = [0, 1, 2, 3, 4, 5, 6]
# 解法1:
def two_sum_1(li, target):
for i in range(len(li)):
for j in range(i + 1, len(li)):
if li[i] + li[j] == target:
return i, j
print(two_sum_1(li, 6))
# 解法2
def two_sum_2(li, target):
d = {}
for i in range(len(li)):
b = target - li[i]
if b in d:
return d[b], i
else:
d[li[i]] = i
print(two_sum_2(li, 6))
# 解法3
# 结合二分查找法,可以找到所有的可能组合.
# 缺点:提供的列表必须是有序的,否则这个办法没有作用.
class Solution:
# 二分查找法
def binary_search(self, li, val, start, end):
while start <= end:
mid = (start + end) // 2
if li[mid] < val:
start = mid + 1
elif li[mid] > val:
end = mid - 1
else:
return mid
else:
return None
# 给定一个列表和一个整数,设计算法找到两个数的下标,使得两个数之和为给定的整数.
def two_sum3(self, li, target):
dic = {}
for i in range(len(li)):
a = li[i]
b = target - a
# 写0时,{0: 6, 1: 5, 2: 4, 3: 3, 4: 2, 5: 1, 6: 0}
# 写i时,{0: 6, 1: 5, 2: 4, 3: 3}
# 写i+1时,{0: 6, 1: 5, 2: 4}
res = self.binary_search(li, b, i, len(li) - 1)
if res != None:
dic[i] = res
return dic
# 实例化对象
s = Solution()
# 调用类方法
print(s.two_sum3(li, 6))