• day30-算法


    算法、数据结构、金融      算法ppt ,72页!
    day30 算法(algorithm):11个py文件 (查找和排序)
    1.简介--算法必考
    一个计算过程,解决问题的方法
    时间复杂度:用来估算算法运行时间的一个式子(单位),一般来说,时间
    复杂度高的算法比复杂度低的算法慢。

    空间复杂度:用来评估算法内存占用大小的一个式子----一般不讨论了

    时间复杂度小结:
    时间复杂度是用来估计算法运行时间的一个式子(单位)。
    一般来说,时间复杂度高的算法比复杂度低的算法慢。
    常见的时间复杂度(按效率排序)
    O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n2logn)<O(n3)
    不常见的时间复杂度(看看就好)
    O(n!) O(2n) O(nn) …
    
    如何一眼判断时间复杂度?
    循环减半的过程O(logn)
    几次循环就是n的几次方的复杂度
    小结


    递归:调用自身,结束条件
    # 斐波那契数列
    # 1 1 2 3 5 8 13
    # 1 2 3 4 5
    # def func(x):
    #     if x>0:
    #         func(x-1)
    #         print(x)
    #
    # func(5)
    
    # 斐波那契数列
    # 1 1 2 3 5 8 13
    
    # def fib(n):
    #     if n == 0 or n == 1:
    #         return 1
    #     else:
    #         return fib(n-1) + fib(n-2)
    # print(fib(100))
    
    
    # def fib(n):
    #     li = [1,1]
    #     for i in range(2,n+1):
    #         li.append(li[-1]+li[-2])
    #     return li[-1]
    # print(fib(100))
    
    
    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
    
    
    print(fib(100))
    递归

    汉诺塔问题:移盘子
    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(5, 'A', 'B', 'C')
    hanoi
    走楼梯,递归问题,类似于斐波那契,按照最后一步是一阶或者两阶去算
    铺砖问题同理


    列表查找:顺序查找、二分查找(需要是有序列表)
    第21页ppt两个查找代码
    def linear_search(data_set, value):  
        for i in range(range(data_set)):
            if data_set[i] == value:      
                return i  
        return
    
    时间复杂度:O(n)
    
    
    def bin_search(li, val):
        low = 0
        high = len(li) - 1
        while low <= high:
            mid = (low + high) // 2
            if li[mid] > val:
                high = mid - 1
            elif li[mid] < val:
                low = mid + 1
            else:   # =
                return mid
        return None
    
    时间复杂度:O(logn)
    
    应用:
    li = [1,3,4,5,6,8,9,80]
    print(bin_search(li,5))
    查找

    递归版二分查找
    列表是对象,可以按照key去查

    列表排序:冒泡,选择,插入;快速排序,堆排序,归并排序

    算法关键点:有序区、无序区
    1.冒泡:必须要会
    一趟之后,最大的元素在最上面,共需要n-1趟
    顺序: [0,n-i-1]
    箭头到n-i-2,但前包后不包,for循环写n-i-1
    import random
    from timewrap import cal_time
    
    @cal_time
    # def bubble_sort(li):
    #     for i in range(len(li)-1): # i表示第i趟
    #         for j in range(len(li)-i-1): # j表示箭头位置,
    #             if li[j] > li[j+1]:
    #                 li[j], li[j+1] = li[j+1], li[j]
    #         print(li)
    # li = list(range(10))
    # random.shuffle(li) #将函数打乱
    # print(li)  #这10个数开始的顺序
    # bubble_sort(li)
    
    
    
    
    @cal_time
    def bubble_sort_2(li):
        for i in range(len(li)-1): # i表示第i趟
            exchange = False
            for j in range(len(li)-i-1): # j表示箭头位置
                if li[j] > li[j+1]:
                    li[j], li[j+1] = li[j+1], li[j]
                    exchange = True
            if not exchange:
                return
    
    
    li = list(range(1000))
    random.shuffle(li) #将函数打乱
    # print(li)
    bubble_sort_2(li)
    #print(li)
    冒泡排序
    import time
    
    
    def cal_time(func):
        def wrapper(*args, **kwargs):
            t1 = time.time()
            result = func(*args, **kwargs)
            t2 = time.time()
            print("%s running time: %s secs." % (func.__name__, t2-t1))
            return result
        return wrapper

    2.选择排序:每次选出一个最小的,比冒泡少了交换,所以速度快一些
    import random
    from timewrap import cal_time
    
    def find_min(li): #找最小值
        min_val = li[0]
        for i in range(1, len(li)):
            if li[i] < min_val:
                min_val = li[i]
        return min_val
    
    def find_min_pos(li): #找最小值位置
        min_pos = 0
        for i in range(1, len(li)):
            if li[i] < li[min_pos]:
                min_pos = i
        return min_pos
    
    @cal_time
    def select_sort(li): #排序方法
        for i in range(len(li)-1): # i表示第i趟
            # 无序区的范围 [i, len(li))
            min_pos = i
            for j in range(i+1, len(li)):
                if li[j] < li[min_pos]:
                    min_pos = j
            li[i], li[min_pos] = li[min_pos], li[i]
            # print(li) #每趟结束之后打印
    
    
    li = list(range(10000))
    #random.shuffle(li)
    # print(li)
    select_sort(li)
    #print(li)
    选择排序

    3.插入排序:类似于摸牌插入(摸到的牌,手里的牌) 共需要n-1趟
    时间复杂度:n方级别,速度与选择差不多

    选择速度最快只能是n方,插入和冒泡可能是n
    import random
    from timewrap import cal_time
    
    
    # 何时插入? 1. j位置的值小于tmp  2.j=-1   li[j+1]=tmp
    
    @cal_time
    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[j + 1] = li[j]
                j -= 1
            li[j + 1] = tmp
            # print(li)
    
    
    li = list(range(10000))
    # random.shuffle(li)
    # print(li)
    insert_sort(li)
    插入排序
    # 布尔值的短路问题
    def func():
        print('aaa')
    
    
    x = -3
    x > 0 and func()  # func()不执行
    
    #证明快排空间复杂度
    def A(a, b):
        return a + b
    
    
    def B(a, b):
        return A(a + b, b)
    
    
    def C(x):
        return B(x // 2, x + 5)
    
    
    x = C(10)
    print(x)
    测试

    4.快速排序:重点!必须掌握!
    取一个元素归位,递归完成排序
    算法关键点:整理(partition)、递归()
    空位的概念,左右寻找(从后开始)
    降序排序,只需将两个while条件中的与tmp比较的大于小于号写反
    import sys
    import random
    from timewrap import cal_time
    
    sys.setrecursionlimit(1100) #设置递归深度
    
    
    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)
    
    
    @cal_time
    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:
                right -= 1
            li[left] = li[right]
            while left < right and li[left] <= tmp:
                left += 1
            li[right] = li[left]
        li[left] = tmp
        return left
    
    
    # @cal_time
    # def sys_sort(li): #系统自带的排序,底层是c语言,所以速度快
    #     li.sort()
    
    
    li = list(range(100000, -1, -1)) #把数据倒着
    random.shuffle(li)
    quick_sort(li)
    # sys_sort(li)
    快速排序!!


    5.堆排序:难!
    树与二叉树简介:树是一种数据结构,可以递归定义的数据结构
    树的度:最多节点孩子的个数;根节点的度:其对应孩子的个数
    二叉树:度不超过2的树(节点最多有两个叉),分左右孩子
    满二叉树:每一个结点都达到最大值
    完全二叉树:叶结点只能出现在最下层和次下层,且最下面一层的结点都集中在最左边
    二叉树的存储方式:链式存储方式--存。。、顺序存储方式(列表)--存完全二叉树
    i表示下标:左孩子:2i+1 右孩子:2i+2 孩子找父亲:(i-1)//2

    堆:大根堆:一颗完全二叉树,任一节点都比孩子节点大 (排完升序)
    小根堆:一颗完全二叉树,任一节点都比孩子节点小(排完降序)
    堆的向下调整性质 :左右子树都是堆,但自身不是堆,一次调整即可成堆!
    挨个出数:棋子从最后一个非叶子节点开始,退休,棋子,调整
    构造堆:从子树开始从下往上调整
    堆排序过程:1.建立堆2.得到最大元素3.去掉堆顶,将堆最后一个元素放到堆顶
    4.堆顶元素为第二大元素 5.重复3,直到堆变空

    代码书写:high表示最后元素的位置;堆排序函数,重点是两个条件
    问题:topK问题,现在有n个数,设计算法找出前k大的数(k<n)
    1.排序后切片 O(nlogn +k) 2.LowB三人组思想 O(kn)
    3.堆排序:建立小根堆, O(nlogk) 建堆klogk +调整 (n-k)logk = O(nlogk)
    或用内置模块
    import random
    from timewrap import cal_time
    
    # def sift(li, low, high): #调整函数,重点是两个条件 logn
    #     # 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
    #
    # @cal_time
    # def heap_sort(li):  # 堆排函数
    #     n = len(li)
    #     # 1. 建立堆
    #     for low in range(n//2-1, -1, -1): #倒着走
    #         sift(li, low, n-1)   # high=n-1
    #     # print(li)
    #     # 2. 挨个出数 退休-棋子-调整
    #     for high in range(n-1, -1, -1):
    #         li[0], li[high] = li[high], li[0]  #退休
    #         sift(li, 0, high-1) #注意是high-1
    #         # print(li)
    #
    #
    # li = list(range(100000))
    # random.shuffle(li)
    # #print(li)
    # heap_sort(li)
    
    
    import heapq
    
    li = [2, 5, 7, 8, 9, 6, 1, 4, 3]
    heapq.heapify(li)
    print(li)  # 小根堆,建堆的过程
    heapq.heappush(li, 0)  # 往堆里加入
    print(li)
    print(heapq.heappop(li))  # 返回最小的值
    print(heapq.heappop(li))  # 返回最小的值
    
    print(heapq.nlargest(5, li))  # 5个最大的
    print(heapq.nsmallest(5, li))  # 5个最小的
    堆排序

    6.归并排序:条件分两段有序,一次归并
    一个列表一次归并;两个列表一次归并
    递归归并 时间复杂度:O(nlogn) 空间复杂度:O(n)
    import random
    from timewrap import cal_time
    
    
    # def merge_two(li1, li2): #两个有序列表 一次归并
    #     li = []
    #     i = 0
    #     j = 0
    #     while i < len(li1) and j < len(li2):
    #         if li1[i] <= li2[j]:
    #             li.append(li1[i])
    #             i += 1
    #         else:
    #             li.append(li2[j])
    #             j += 1
    #     while i < len(li1):
    #         li.append(li1[i])
    #         i += 1
    #     while j < len(li2):
    #         li.append(li2[j])
    #         j += 1
    #     return li   #不用写回,直接return即可
    
    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:  # 右边剩下,两个while二选一
            li_tmp.append(li[j])
            j += 1
        for i in range(len(li_tmp)):  # 将tmp值写回,tmp值写回列表
            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])
    
    
    # li = [10, 4, 6, 3, 8, 2, 5, 7]
    # _merge_sort(li, 0, len(li) - 1)
    # print(li)
    
    
    @cal_time
    def merge_sort(li):
        _merge_sort(li, 0, len(li) - 1)
    
    
    li = list(range(100000))
    random.shuffle(li)
    merge_sort(li)
    归并排序
    1 三种排序算法的时间复杂度都是O(nlogn)
    2 
    3 一般情况下,就运行时间而言:
    4 快速排序 < 归并排序 < 堆排序
    5 
    6 三种排序算法的缺点:
    7 快速排序:极端情况下排序效率低
    8 归并排序:需要额外的内存开销
    9 堆排序:在快的排序算法中相对较慢
    NB三人组-小结

    小结:挨着换的稳定

     


    快排、归并必须理解!
    排序理解:思路,代码写法,时间复杂度

    下周:不常用的排序方式:希尔排序、计数排序、桶排序、基数排序
  • 相关阅读:
    CF601B Solution
    CF847F Solution
    CF877D Solution
    CF1472F Solution
    CF1472G Solution
    CF1355E Solution
    CF912D Solution
    CF1167F Solution
    shell脚本自动备份MySQL数据库
    centos7 crontab笔记
  • 原文地址:https://www.cnblogs.com/lijie123/p/10240159.html
Copyright © 2020-2023  润新知