面试题:
归并算法
两个有序序列进行归并
l1 = [1, 2, 5, 6, 8,10]
l2 = [3, 4, 7,9,12]
l3 = []
i = 0
j = 0
while i < len(l1) and j < len(l2):
if l1[i] < l2[j]:
l3.append(l1[i])
i += 1
else:
l3.append(l2[j])
j = j + 1
while i < len(l1):
l3.append(l1[i])
i += 1
while i < len(l2):
l3.append(l2[i])
i += 1
print(l3)
求阶乘后结果的末尾0的个数
n= 5
rus = 1
while n>0:
rus = rus*n
n-=1
s = str(rus)
count =0
s= '120000000'
for i in range(len(s)-1,-1,-1):
if s[i]=='0':
count +=1
else:
break
print(count)
字典排序:
alist =[{'name':'a','age':20},{'name':'b','age':30},{'name':'c','age':25}]
alist = sorted(alist,key=lambda dic:dic['age'])
print(alist)
斐波那契数列
a, b, c = 1, 2, 3
n = 400000
i = 0
while a < n:
print(a)
i += 1
a, b, c = b, c, b + c
print(i)
def fib(n):
li = [1,1]
for i in range(2,n+1):
li.append(li[-1]+li[-2])
return li[-1]
def fib(n):
if n ==0 or n == 1:
return 1
a = 1
b = 1
c = 1
for i in range(2,n+1):
a = b
b = c
c = a + b
return c
# 青蛙跳台阶问题
def func(n):
if n == 0:
return 0
if n == 1:
return 1
if n == 2:
return 2
return func(n - 1) + func(n - 2)
print(func(4))
递归
汉诺塔问题
def hanoi(n,A,B,C):
if n>0:
hanoi(n-1,A,C,B)
print('%s -> %s' % (A,C))
hanoi(n-1,B,A,C)
hanoi(2,'A','B','C')
二分查找
def bin_search(li, val):
'''使用low和high标注索引,mid为中间索引,这里的索引代表指针,每次以中间值比较大小,比较完后改变索引'''
low = 0
high = len(li) - 1
while low <= high:
mid = (high - low) // 2
if li[mid] > val:
high = mid - 1
elif li[mid] < val:
low = mid + 1
else:
return mid
return -1
使用and代替if语句
def func():
print('aaa')
x = -3
x > 0 and func()
# 等价于
if x > 0:
func()
# 所以可以使用and代替if语句()
# a and b <==> a if a == 0 else b
# a or b <==> b if a == 0 else a
[外排序,对大内存有效](https://www.cnblogs.com/codeMedita/p/7425291.html)
简单的说就是先分段,将每段排好后,然后使用归并排序,对于两段进行归并排序时,可以一点点读数据归并,然后数据一点点写入磁盘
如 1 2 3 5 6 12 18 20 24 和 4 5 7 20 25 30 21 25 26 进行归并时,可以先归并前5个结果是 1 2 3 4 5 5 6 7 12 18 然后将归并后结果写入磁盘
十种排序算法
# 推荐:https://blog.csdn.net/yangnianjinxin/article/details/77918882
选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,而冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法
low逼三人组
冒泡(稳定 o(n2))
# 走n趟,没趟交换比较选出最大的
def bubble_sort(li):
for i in range(len(li) - 1):
exchange = False # 没有交换表示已经排好序了,就不用再继续执行了
for j in range(len(li) - 1 - i):
if li[j] > li[j + 1]:
li[j], li[j + 1] = li[j + 1], li[j]
exchange = True
if not exchange:
return
选择排序(不稳定)
# 一趟遍历选择记录中最小的数,放到第一个位置
# 再一趟遍历记录剩余列表中最小的数,继续放置
def select_sort(li):
for i in range(len(li) - 1):
min_pos = i
for j in range(i + 1, len(li)):
if li[j] < li[min_pos]:
min_pos = j
li[min_pos], li[j] = li[j], li[min_pos]
插入排序
# 列表分为有序区和无序区两个部分,最初有序区只有一个元素
# 每次从无序区选择一个元素,插入到有序区的位置,直到无序区变空
# 类似于玩纸牌时的插牌
# 可以优化空间使用二分查找来寻找插入点(并没有什么卵用)
def insert_sort(li):
for i in range(1, len(li)): # 无序区
j = i-1 # 有序区最后一个数
tmp = li[i]
while j >= 0 and li[j] > tmp: # 有序区从大到小逐一比较,直到有序区数小于li[i]
li[j+1] = li[j]
j -= 1
li[j+1] = tmp
牛逼三人组(o(nlogn))
快排
# 取一个元素p(第一元素),使元素p归位
# 列表被p分为两部分,左边的都比p小,右边的都比p大
# 递归完成排序
def _quick_sort(li, left, right):
if left < right: # 至少两个元素
mid = partition(li, left, right)
_quick_sort(li, left, mid-1)
_quick_sort(li, mid+1, right)
def quick_sort(li):
_quick_sort(li, 0, len(li)-1)
def partition(li, left, right):
i = random.randint(left, right) # 随机选一个数,防止最坏情况
li[left], li[i] = li[i], li[left]
tmp = li[left]
while left < right:
while left < right and li[right] >= tmp: #从右开始选,直到找到一个小于tmp的数,把这个数填在left位置
right -= 1
li[left] = li[right]
while left < right and li[left] <= tmp: #从左开始选,直到找到一个大于tmp的数,把这个数填在right位置
left += 1
li[right] = li[left]
li[left] = tmp
return left
# 一行代码实现快排
# 这样空间复杂度会高
def quicksortshort(arr):
return [] if arr==[] else quicksortshort([y for y in arr[1:] if y<arr[0]]) + [arr[0]]+ quicksortshort([y for y in arr[1:] if y>=arr[0]])
堆排
# 1,建立堆
# 2,得到堆顶元素,为最大元素
# 3,去掉堆顶,将堆最后一个元素放到堆顶,此时可通过一个调整重新使堆有序
# 4,堆顶元素为第二大元素
# 5,重复步骤3,直到堆变空
def sift(li, low, high): # 调整,从子孩子中选出最大的和父节点比较,如果大于父节点就和父节点的值交换,然后父节点的值继续和子节点的子节点进行比较
# low 表示根位置 high 表示最后元素的位置
tmp = li[low]
i = low # i指向空位
j = 2 * i + 1 # j指向孩子
# 把tmp写回来有两种条件 1. tmp > li[j] 2. j位置没有值 (也就是i已经是叶子了)
while j <= high: # 对应退出条件2
if j + 1 <= high and li[j+1] > li[j]: # 如果右孩子存在且右孩子更大
j += 1
if li[j] > tmp:
li[i] = li[j]
i = j
j = 2 * i + 1
else: # 对应退出条件1
break
li[i] = tmp
li = [3,5,6,2,0,4,1,9,8,7]
def heap_sort(li):
n = len(li)
# 1. 建立堆
for low in range(n//2-1, -1, -1):
# li = [3,5,6,2,0,4,1,9,8,7]
'''
3(索引0)
5(1) 6(2)
2(3) 0(4) 4(5) 1(6)
9(7) 8(8) 7(9)
'''
# 如 n=10时,low的值依次是 4,3,2,1,0 从最后的非叶子节点开始调整
sift(li, low, n-1)
print(li) # 此时的li已经是一个大根堆了
# [9, 8, 6, 5, 7, 4, 3, 0, 1, 2]
'''
9(索引0)
8(1) 6(2)
5(3) 7(4) 4(5) 3(6)
0(7) 1(8) 2(9)
'''
# 2. 挨个出数 退休-棋子-调整
for high in range(n-1, -1, -1):
# high 的值 9,8,7,6,,5,4,3,2,1,0
li[0], li[high] = li[high], li[0] # 这样交换后最大的就变到了列表的最后位置上,
sift(li, 0, high-1) # 然后调整的时候就不把已经退休的(最大的)考虑在内了
python内置的堆模块
import heapq
li = [2,5,7,8,9,6,1,4,3]
heapq.heapify(li) #调整变为小根堆
print(li) # [1, 3, 2, 4, 9, 6, 7, 5, 8]
heapq.heappush(li, 0) # 插入新值,自动调整为小根堆
print(li) # [0, 1, 2, 4, 3, 6, 7, 5根堆, 8, 9]
print(heapq.heappop(li)) # 堆顶退休(最小值),继续调整为小
print(heapq.heappop(li))
print(heapq.nlargest(5, li)) # 输出最大的5个数
# [9, 8, 7, 6, 5]
print(heapq.nsmallest(5, li)) # 输出最小的5个数
# [2, 3, 4, 5, 6]
列表中取出前k大的数
# 1,取列表前k个元素建立一个小根堆,堆顶就是这个堆中最小的数,也是这个堆中第k大的数
# 2,依次向后遍历原列表,对于列表中的元素,如果小于堆顶,则忽略该元素,如果大于堆顶则将堆顶更换为改元素,并且对堆进行一次调整
# 3,遍历列表所有元素后,倒序弹出堆顶
归并
def merge(li, low, mid, high):
li_tmp = []
i = low
j = mid + 1
while i <= mid and j <= high:
if li[i] <= li[j]:
li_tmp.append(li[i])
i += 1
else:
li_tmp.append(li[j])
j += 1
while i <= mid:
li_tmp.append(li[i])
i += 1
while j <= high:
li_tmp.append(li[j])
j += 1
for i in range(len(li_tmp)): # 把归并排好的两个列表放回到li中
li[i+low] = li_tmp[i]
def _merge_sort(li, low, high):
if low < high: # 2个元素及以上
mid = (low + high) // 2
_merge_sort(li, low, mid)
_merge_sort(li, mid+1, high)
#print(li[low:mid+1], li[mid+1:high+1])
merge(li, low, mid, high)
#print(li[low:high + 1])
def merge_sort(li):
_merge_sort(li, 0, len(li)-1)
没什么人用的排序
基数排序
希尔排序
桶排序
二叉树
列表中索引:父节点 -> 左孩子节点 i -> 2i+1
父节点 -> 右孩子节点 i -> 2i+2
孩子节点 -> 父节点 i -> (i-1)//2
堆顶 i = 0
所有父节点 i_list =[i for i in range(len(li)//2)]
大根堆:一个完全二叉树,满足任一节点都比其孩子节点大
小根堆:一个完全二叉树,满足任一节点都比其孩子节点小
当根节点的左右子树都是堆时(这里的示例是大根堆),可以通过一次向下调整来将其变换成一个堆