Python 排序算法(一)、时间复杂度、冒泡、选择插入、对数器、递归、归并、小和、逆序对

本节:

  1. 理解时间复杂度,递归的master公式
  2. 用python实现冒泡排序,选择排序,插入排序,对数器,归并排序
  3. 练习:小和问题,逆序对问题

认识时间复杂度

  • 常数时间的操作:跟数据量没有关系,每次时间都是固定的
  • 在常数操作数量的表达式中,只要高阶项,不要低阶项,也不要高阶项的系数,剩下的部分如果记为f(N),那么时间复杂度为0(f(N))
  • 评价算法好坏,先看时间复杂度的指标,然后分析样本实际运行时间(即常数项)

实例:

一个有序数组A,另外一个无序数组B,请打印B中所有不在A中的数,A数组长度为N,B数组长度为M

算法1:对于数组B中的每个数,都在A中通过遍历方式找。时间复杂度: (M*N)

算法2:对于数组B中每个数,都在A中通过二分的方式找。时间复杂度: (M*logN)

算法3:先把数组B排序,然后用类似外排的方式打印所有在A中出现的数。时间复杂度: o(M*logM) + o(N+M)

这里根据M的数量选择2(量少)或3(量大),通过时间复杂度去判断一个算法的好坏。

# 算法2
de

# 算法3


冒泡排序

  1. 比较相邻的元素,如果第一个比第二个大(升序),交换他们
  2. 从第一对到最后一对执行1操作,这步完成后,最后的元素为最大的数
  3. 对所有元素重复12,已经排序的数不用交换(保证稳定性)。
# 时间复杂为o(n^2)
def bubble_sort(alist):
	# 边界问题,-1和不取零,是由于单个数不用比较
    for j in range(len(alist)-1, 0, -1): # 这里不需要取0
        for i in range(j):  # 这里不需要取到-1
            if alist[i] > alist[i+1]:
                alist[i], alist[i+1] = alist[i+1], alist[i]
li = [54,26,93,17,77,31,44,55,20]
bubble_sort(li)
print(li)

选择排序

遍历数组,取出一个数,与后续所有元素比较,如果后续元素较小,互换位置

# 时间复杂为o(n^2)
def selection_sort(alist): #升序
    for i in range(len(alist)-1):  # 最后一个数不需要比较
        for j in range(i+1, len(alist)):  # 不需要跟自己比较,最后一个数需要比较
            if alist[i] > alist[j]:
                alist[i], alist[j] = alist[j], alist[i]
li = [54,26,93,17,77,31,44,55,20]
selection_sort(li)
print(li)

插入排序(业务还有使用)

原理是构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入

# 数据状况最好时间和最坏时间,一律取最坏状况时间
# 时间复杂度Sn=a1*n + (n*(n-1)d)/2 = n^2
def insert_sort(alist):
    for i in range(1, len(alist)):
        for j in range(i, 0, -1):
            if alist[j] < alist[j-1]:
                alist[j], alist[j-1] = alist[j-1], alist[j]
alist = [54,26,93,17,77,31,44,55,20]
insert_sort(alist)
print(alist)

对数器

没有OJ(Online Judge)系统测试自己代码时,就可以用对数器测试。

  1. 有一个你要测的方法a
  2. 实现一个绝对正常但复杂度不好的方法b
  3. 实现一个随机样本产成器
  4. 实现比对的方法
  5. 把方法a和方法b对比很多次来验证a是否正确
  6. 如何有一个样本出错,打印样本并分析
  7. 当样本数量很多时测试依然正确,可以确定方法a正确
# 实现对数器
def generateRandomArray(size, value):
    """random -> double[0,1), 也确保size和value随机"""
    arr = []
    arr_len = int((size+1)*random.random()) #range不取最大那个数,则size+1
    for i in range(arr_len):
        arr.append(int((value+1)*random.random()) - int(value*random.random()))
    return arr
"""
Author: lllong33
data: 2019/5/26/0026 
"""

import random
import copy
import time

def generate_random_array(max_size, value):
    """
    random -> double[0,1),还需要保证 size 和 value 随机
    size 范围 [0, size]
    value 保证取值范围为[-value+1, value]

    int()会取整,保证 arr_len 取值范围为 [0, size]
    int(value * random.random) --> [0, value-1]
    验证:max([int((value+1) * random.random()) for _ in range(10000)])
    """
    arr = []
    arr_len = int((max_size+1) * random.random()) # 生成[0, size]
    for i in range(arr_len):
        random_value = int((value+1) * random.random()) - int(value*random.random())
        arr.append(random_value)
    return arr


def bubble_sort(alist):
    for j in range(len(alist)-1, 0, -1):
        for i in range(j):
            if alist[i] > alist[i+1]:
                alist[i], alist[i+1] = alist[i+1], alist[i]
    return alist

def right_mathod(arr):
    arr.sort()
    return arr

def main():
    s_time = time.time()
    test_num = 50000
    max_size = 10
    max_value = 1000
    success = True
    for i in range(test_num):
        arr1 = generate_random_array(max_size, max_value)
        arr2 = copy.copy(arr1)
        a = bubble_sort(arr1)
        b = bubble_sort(arr2)
        if a != b:
            success = False
    print(f'spend time: {time.time() - s_time}', 'Good' if success else f'Fuck fucked! my:{a}, right:{b}')
	print(f'my:{a}, right:{b}')
if __name__ == '__main__':
    main()

# bubble_sort(generate_random_array(100, 10000))

递归行为和递归时间复杂度

master公式:

T(N) = a*T(N/b) + O(N^d)

log(b,a) > d —> 复杂度为O(N^log(b,a))

log(b,a) = d —> 复杂度为O(N^d * logN)

log(b,a) < d —> 复杂度为O(N^d)

补充阅读:http://www.gocalf.com/blog/algorithm-complexity-and-master-theorem.html

# T(N) = 2T(N/2) + O(1) 即log(2,2)>0 --> O(N^log(2,2))
def getMax(arr, left, right):
    if left == right:
        return arr[left]
    mid = left+ (right - left)//2
    left = getMax(arr, left, mid)
    right = getMax(arr, mid+1, right)
    return max(left, right)
arr = [4,3,2,1]
print(getMax(arr, 0, len(arr)-1))

归并排序(分治)

先递归分解数组,再合并数组

# 时间复杂度O(nlogn),空间复杂度为O(N)
def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    mid = len(arr)//2 #分解
    left = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    return merge(left, right) #合并

def merge(left, right):
    """将两个有序数组left[]和right[]合并为一个大的有序数组"""
    l,r = 0,0 #left与right的下标指针
    result = []
    while l<len(left) and r<len(right):
        if left[l] < right[r]:
            result.append(left[l])
            l += 1
        else:
            result.append(right[r])
            r += 1
    result += left[l:]
    result += right[r:]
    return result

arr = [54,26,93,17,77,31,44,55,20]
sorted_alist = merge_sort(arr)
print(sorted_alist)

小和、逆序对问题

"""在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和,求某个数组小和 eg:[1,3,4,2,5]"""
def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    mid = len(arr)//2 #分解
    left = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    return merge(left, right) #合并

def merge(left, right):
    """将两个有序数组left[]和right[]合并为一个大的有序数组"""
    global result_sum
    l,r = 0,0 #left与right的下标指针
    result = []
    while l < len(left) and r<len(right):
        if left[l] < right[r]:
            result_sum += left[l] * (len(right) - r)
            result.append(left[l])
            l += 1
        else:
            result.append(right[r])
            r += 1
    result += left[l:]
    result += right[r:]
    return result

result_sum = 0
arr = [1,3,4,2,5]
sorted_alist = merge_sort(arr)
print(result_sum)
"""在一个数组中,左边数如果比右边的数大,则两个数构成一个逆序对,请打印所有逆序对"""
def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    mid = len(arr)//2 #分解
    left = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    return merge(left, right) #合并

def merge(left, right):
    """将两个有序数组left[]和right[]合并为一个大的有序数组"""
    global result_sum
    l,r = 0,0 #left与right的下标指针
    result = []
    while l < len(left) and r<len(right):
        if left[l] > right[r]:
            for i in left[l:]:
                print('逆序对:', i, right[r])
            result.append(right[r])
            r += 1
        else:
            result.append(left[l])
            l += 1
    result += left[l:]
    result += right[r:]
    return result

arr = [1,3,4,2,5]
merge_sort(arr)
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值