zoukankan      html  css  js  c++  java
  • 高级算法

    什么是算法?

    算法(Algorithm):一个计算过程,解决问题的方法。

    输入→算法→输出

    时间复杂度

    时间复杂度:用来评估算法运行效率的一个东西。

    print('Hello World')           #假如说这行代码运行时间是一个单位O(1)
    for i in range(n):             # 这段代码的时间是O(n),因为执行了n次
        print('Hello World')   
    for i in range(n):             # 这段代码是O(n*n),因为在执行了n*n次
        for j in range(n):        
            print('Hello World')
    for i in range(n):             #这代码是O(n*n*n),执行了n的立方次
        for j in range(n):       
            for k in range(n):          
                print('Hello World')

    小结:

    时间复杂度是用来估计算法运行时间的一个式子(单位)。
    一般来说,时间复杂度高的算法比复杂度低的算法慢。
    常见的时间复杂度(按效率排序):
        O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n2logn)<O(n3)
    不常见的时间复杂度(看看就好)
        O(n!) O(2n) O(nn) …

    空间复杂度

    空间复杂度:用来评估算法内存占用大小的一个式子

    空间换时间:分给它一些空间或内存,让它运行速度更快

     递归

     递归的两个特点:

    1.调用自身

    2.有结束条件

    def func(x):    
        if x>0:       
            print(x)       
            func(x-1)
    print(4)
    # 打印结果 4 3 2 1
    #因为先打印再递归
    def func(x):
        if x > 0:
            func(x-1)
            print(x)
    func(4)
    # 打印结果 1 2 3 4
    # 因为先递归,再打印

    打印  抱着抱着抱着抱着抱着我的小鲤鱼的我的我的我的我的我

    def test(n):
        if n == 0:
            print("我的小鲤鱼", end='')
        else:
            print("抱着", end='')
            test(n-1)
            print("的我", end='')
    
    test(5)
    
    # 尾递归

    汉诺塔问题

    t = 0
    
    def hanoi(n, A, B, C):
        global t
        if n > 0:
            hanoi(n-1, A, C, B)
            t += 1
            print("%s -> %s" % (A, C))
            hanoi(n-1, B, A, C)
    
    # hanoi(8,'A','B','C')  # 8表示8层  A B C 参数不能变
    # print(t)

    列表查找

     列表查找:从列表中查找指定元素

     输入:列表、待查找元素

     输出:元素下标或未查找到元素

    顺序查找

    顺序查找:从列表第一个元素开始,顺序进行搜索,直到找到为止。

    二分查找:从有序列表的候选区data[0:n]开始,通过对待查找的值与候选区中间值的比较,可以使候选区减少一半。

    有序列表,列表的元素值随着索引值的增加而增加:

    简单版二分查找

    def bin_search(li, val):         # li是传入的列表 val是要查找的值
        low = 0                      # low是起始的索引值
        high = len(li) - 1           # high是末尾的索引值
        while low <= high:           # 满足起始索引小于末尾索引的条件就执行循环
            mid = (low + high) // 2  # mid是列表的中间数的索引
            if li[mid] == val:       # 正好找到要查找的值的索引
                return mid
            elif li[mid] < val:      # 中间数的值小于被查找的值
                low = mid + 1        # 说明val在中间数的右边
            else:
                high = mid - 1       # 说明val在中间数的左边

    递归版的二分查找 这是尾递归

    def bin_search_rec(data_set, value, low, high):
        if low <= high:
            mid = (low + high) // 2
            if data_set[mid] == value:
                return mid
            elif data_set[mid] > value:
                return bin_search_rec(data_set, value, low, mid - 1)
            else:
                return bin_search_rec(data_set, value, mid + 1, high)
        else:
            return

    列表排序

    列表排序:将无序列表变成有序列表

    输入:无序列表

    输出:有序列表

    顺序:升序与倒序

    排序low B 三人组;

        冒泡排序       其次最多    O(n*n)

        选择排序                         O(n*n)

        插入排序

    排序niu B 三人组:

        快速排序        用得最多

        堆排序           最难的

        归并排序

    冒泡排序

    冒泡排序:两层遍历,相邻的两个值,如果左边的数大于右边的数,则交换位置。

    li = [1, 2, 1, 3, 1, 4, 5, 4, 6, 7, 6, 8, 4, 5, 3, 2]


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


    print(bubble_sort(li))

    选择排序

    选择排序:遍历一趟记录最小的数,放到第一位。再遍历剩下的数,找的最小的数,继续放置。

    关键点:无序区和最小数的位置。

    li = [1, 2, 1, 3, 1, 4, 5, 4, 6, 7, 6, 8, 4, 5, 3, 2]


    def select_sort(li):
    for i in range(len(li) - 1): # 总趟数可以是len(li)次,也可以是len(li)-1次,因为倒数第二趟结束的时候,列表已经排序完成。
         # i表示趟数,也表示无序区的第一个数 min_loc
    = i # for j in range(i+1, len(li)): if li[j] < li[min_loc]: # 每趟都把最小的数放到无序区第一个位置 min_loc = j li[min_loc], li[i] = li[i], li[min_loc] # 有序区会多一个数,无序区会少一个数。接下来下一趟开始 return li print(select_sort(li))

    插入排序:

    列表被分为有序区和无序区两个部分。最初有序区只有一个元素。

    每次从无序区选择一个元素,插入到有序区的位置,直到无序区变空。

    代码关键点:如何找到无序区数,如何插到有序区中。

    li = [1, 2, 1, 3, 1, 4, 5, 4, 6, 7, 6, 8, 4, 5, 3, 2]


    def insert_sort(li):
    for i in range(1,len(li)): # 无序区的范围,第一次无序区只有列表的第一个数,所以无序区范围就是range(1,len(li))
    # i表示摸到的牌的位置
    tem = li[i]
    j = i - 1 # j表示有序区的最后面的一个数,j也表示有序区用来和i进小红比较的数
    # 如果有序区的这个用来比较的值比tem大,则用索引为j-1的值继续比较.
    # 如果tem这个值比有序区的所有数都小,这是索引是-1,表示找到位置,并退出while循环并插入
    while j >= 0 and li[j] > tem:
    li[j+1], li[j] = li[j], li[j+1] # 把tem的值往j的位置移
    j -= 1 # 往前看
    # 如果有序区的数比tem小,表示找到位置,就插入
    li[j+1] = tem

    return li


    print(insert_sort(li))

    插入排序优化:应用二分查找来寻找插入点(并没有什么卵用)

    快速排序

    快速排序:取一个元素p(第一个元素),使元素p归位,列表被p元素分为两部分,左边都比p小,右边都比p大,然后递归完成排序。

    li = [1, 2, 1, 3, 1, 4, 5, 4, 6, 7, 6, 8, 4, 5, 3, 2]


    def partition(li, left, right):
    tem = li[left] # 第一次取第一个元素left = 0
    while left < right: # 列表里至少两个元素才满足这个条件
    while left < right and li[right] >= tem: # 从右边边找小于tem的数
    right -= 1 # 如果不小于,继续找
    li[left], li[right] = li[right], li[left] # 找到比tem小的数,挪到左边
    while left < right and li[left] <= tem: # 从左边找比tem大的数
    left += 1
    li[right], li[left] = li[left], li[right]
    li[left] = tem
    return left # 这里返回left和right是一样的


    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) # 递归
    return 111


    quick_sort(li, 0, len(li)-1)
    print(li)

     快排的最坏情况:列表是倒序的[9,8,7,6,5,4,3,2,1,0]

    堆排序

    树是一种数据结构          比如:目录结构

    树是一种可以递归定义的数据结构

    树是由n个节点组成的集合:

    如果n=0,那这是一棵空树;

    如果n>0,那存在1个节点作为树的根节点,其他节点可以分为m个集合,每个集合本身又是一棵树。

     

    一些概念

    根节点:没有父节点的是根节点

    叶子节点:没有子节点的是叶子节点

    树的深度(高度):几层就是几

    树的度:就是他有几个节点(A有6个节点,度就是6。j的是2。f的是3。整个树的度,就是最大的度。)

    孩子节点/父节点:A是B的父节点,B是A的孩子节点

    子树:任何一个节点和它的孩子节点就是一个子树,没有孩子节点他也是一个树。

     二叉树:度不超过2的树(节点最多有两个叉)

     

     B是A的左孩子,C是A的右孩子

    满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。
    完全二叉树:叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树。

     

    二叉树的存储方式

    链式存储方式

    顺序存储方式(列表)

     

    大根堆:一棵完全二叉树,满足任一节点都比其孩子节点大

    小根堆:一棵完全二叉树,满足任一节点都比其孩子节点小

    堆未完待续。。。。。。

     归并排序

    假设现在的列表分两段有序,如何将其合成为一个有序列表

     

    分别从两个有序的片段取值,按从小到大的顺序取值。先比较两个片段的最小值,谁小就取出来。

    所以顺序是1-2-3-4-5-6,将6取出来后,右边的片段没值了,就将左边剩下的片段一次性取出来7-8-9。

    然后就组合成一个新的有序列表了。

    这种操作称为一次归并

    def merge(li, low, mid, high):  # high是右边片段最后一个数的索引
        li_tem = []
        i = low  # low是左边片段第一个数的索引
        j = mid + 1  # mid是左边片段最大的数的索引
        while i <= mid and j <= high:  # 如果左右两个片段都有值,就继续取值。只要其中一个片段没值,就跳出循环。
            if li[i] < li[j]:  # 如果左边片段的最小值小于右边的最小值
                li_tem.append(li[i])  # 就将最小的那个值取出来放到一个列表中。
                i += 1  # 然后继续比较剩下的最小值
            else:  # 反之,就是右边的最小值小于左边的最小值
                li_tem.append(li[j])
                j += 1  # 然后继续比较剩下的最小值
        # 跳出第一个while循环的条件是:如果左边的片段取完了,其索引i不小于mid了
        # 如果右边的片段取完了,其索引j就不小于high了
        # 下面的两个while循环只可能有一个执行
        while i <= mid:  # 这里是左偏片段还有值
            li_tem.append(li[i])
            i += 1  # 继续添加到列表中
        while j <= high:  # 这里是右边片段还有值
            li_tem.append(li[j])
            j += 1  # 继续添加到列表中
        li[low:high+1] = li_tem  # 将li_tem copy给li
        
    
    li = [2, 5, 7, 8, 9, 1, 3, 4, 6]
    merge(li, 0, 4, len(li)-1)
    print(li)

     如果不是两端有序列表怎么用归并,加上递归就可以了。

    先把列表分解,然后合并。下半段的每一步操作都是一个归并。

    归并排序

    def merge(li, low, mid, high):  # high是右边片段最后一个数的索引
        li_tem = []
        i = low  # low是左边片段第一个数的索引
        j = mid + 1  # mid是左边片段最大的数的索引
        while i <= mid and j <= high:  # 如果左右两个片段都有值,就继续取值。只要其中一个片段没值,就跳出循环。
            if li[i] < li[j]:  # 如果左边片段的最小值小于右边的最小值
                li_tem.append(li[i])  # 就将最小的那个值取出来放到一个列表中。
                i += 1  # 然后继续比较剩下的最小值
            else:  # 反之,就是右边的最小值小于左边的最小值
                li_tem.append(li[j])
                j += 1  # 然后继续比较剩下的最小值
        # 跳出第一个while循环的条件是:如果左边的片段取完了,其索引i不小于mid了
        # 如果右边的片段取完了,其索引j就不小于high了
        # 下面的两个while循环只可能有一个执行
        while i <= mid:  # 这里是左偏片段还有值
            li_tem.append(li[i])
            i += 1  # 继续添加到列表中
        while j <= high:  # 这里是右边片段还有值
            li_tem.append(li[j])
            j += 1  # 继续添加到列表中
        li[low:high+1] = li_tem
    
    
    def merge_sort(li, low, high):
        if low < high:  # 保证列表至少有两个元素
            mid = (low + high) // 2  # 取中间数的索引
            merge_sort(li, low, mid)  # 将左边的通过递归编程有序列表
            merge_sort(li, mid+1, high)  # 将右边的通过递归编程有序列表
            merge(li, low, mid, high)  # 将左右两个片段合并成一个有序列表
    
    
    li = [2, 5, 7, 8, 9, 1, 3, 4, 6, 10]
    merge_sort(li, 0, len(li)-1)
    print(li)

     niu B 三人组总结

    三种排序算法的时间复杂度都是O(nlogn)

    一般情况下,就运行时间而言:

    快速排序<归并排序<堆排序

    三种排序算法的缺点:

    快速排序:极端情况下,排序效率低

    归并排序:需要额外的内存开销

    堆排序:在快的排序算法中相对较慢

    希尔排序  待续。。。。。。

    计数排序 待续。。。。。。

    桶排序 待续。。。。。。

    基数排序 待续。。。。。。

  • 相关阅读:
    spark dataframe 正则表达式匹配
    JVM申请的memory不够导致无法启动SparkContext
    <scope>provided</scope> 关于maven依赖中的scope的作用和用法
    web前端网站
    元素居中
    如何在Vue项目中使用vw实现移动端适配
    微任务、宏任务、同步、异步、Promise、Async、await
    前端自动化工作流环境
    Web前端学习笔记——构建前端自动化工作流环境
    JS判断值是否是数字
  • 原文地址:https://www.cnblogs.com/aaronthon/p/9452303.html
Copyright © 2011-2022 走看看