• s11d27 算法


    s11d27 算法

    一、理论

    1.1 时间复杂度和空间复杂度的理论:

    1)空间复杂度:

    是程序运行所以需要的额外消耗存储空间,一般的递归算法就要有o(n)的空间复杂度了,

    简单说就是递归集算时通常是反复调用同一个方法,递归n次,就需要n个空间。

    2)时间复杂度

    一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。

    算法的基本操作重复执行的次数是模块n的某一个函数fn),因此,算法的时间复杂度记做:Tn=Ofn))。

    随着模块n的增大,算法执行的时间的增长率和fn)的增长率成正比,所以fn)越小,算法的时间复杂度越低,算法的效率越高。

    1.2 函数调用的时间复杂度分析

    按数量级递增排列,常见的时间复杂度有:

    常数阶O(1),  对数阶O(log2n),  线性阶O(n),  线性对数阶O(nlog2n),  平方阶O(n^2), 立方阶O(n^3),..., k次方阶O(n^k), 指数阶O(2^n) 

    常用的时间复杂度所耗费的时间从小到大依次是:O(1)<O(log2n)<O(n)<O(nlog2n)<O(n^2)<O(n^3)<O(2^n)<O(n!)<O(n^n)

    常用的算法的时间复杂度和空间复杂度

    排序法

    平均时间复杂度

    说明

    稳定度

    空间复杂度

    备注

    冒泡排序

    O(n2)

    将待排序的元素看作是竖着排列的气泡,较小的元素比较轻,从而要往上浮

    稳定

    O(1)

    快速排序

    O(n*log2n)

    先选择中间值,然后把比它小的放在左边,大的放在右边(具体的实现是从两边找,找到一对后交换)。然后对两边分别使用这个过程(递归)。

    不稳定

    O(log2n)~O(n) 因为用到了递归!

    选择排序

    O(n2)

    首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此递归。

    稳定

    O(1)

    插入排序

    O(n2)

    逐一取出元素,在已经排序的元素序列中从后向前扫描,放到适当的位置

    稳定

    O(1)

    起初,已经排序的元素序列为空

    堆排序

    O(n*log2n)

    利用堆(heaps)这种数据结构来构造的一种排序算法。堆是一个近似完全二叉树结构,并同时满足堆属性:即子节点的键值或索引总是小于(或者大于)它的父节点。

    不稳定

    O(1)

    近似完全二叉树

    希尔排序

    O

    选择一个步长(Step) ,然后按间隔为步长的单元进行排序.递归,步长逐渐变小,直至为1.

    不稳定

    O(1)

    箱排序

    O(n)

    设置若干个箱子,把关键字等于 的记录全都装入到第个箱子里 分配 ,然后按序号依次将各非空的箱子首尾连接起来 收集 

    O(1)

    分配排序的一种:通过分配 和 收集 过程来实现排序。

    当一个算法的空间复杂度为一个常量,即不随被处理数据量n的大小而改变时,可表示为O(1)

    访问数组中的元素是常数时间操作,一个算法如果能在每个步骤去掉一半数据元素,如二分检索,通常它就取 O(logn)时间。

    二、查找算法

    2.1 两分法

    aList = [1, 2, 3, 4, 5, 6, 7, 8]
    
    
    def binarySearch(aList, t):
        low = 0
        heigh = len(aList) - 1
        while low <= heigh:     # 这里为了防止,low=height然后不进入程序,return正确的情况,如:t=3,low=heigh=2
            mid = (low + heigh) / 2
            print 'mid:',mid
            if t > aList[mid]:
                low = mid + 1
    
            elif t < aList[mid]:
                heigh = mid - 1
    
            else:
                print "索引,值:",mid,aList[mid]
                return mid  # 返回索引值
            print 'low:',low
            print 'heigh:',heigh
        return -1       # 为了防止元素找不到的情况,就返回-1
    
    print binarySearch(aList,2)

    三、排序算法

    3.1 冒泡排序 

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    __author__ = 'WangQiaomei'
    
    import random
    import time
    
    '''
    aList=[]
    for i in range(20):
        aList.append(random.randrange(100))
    print aList
    '''
    aList=[]
    for i in range(50000):
        aList.append(random.randrange(100000))
    
    
    def bubble_sort(aList):# 例如 aList的个数是10个
        for i in range(len(aList)): # 循环索引 0-9
            for j in range(len(aList)-i-1):    # 第一次循环索引j为:8,这里为啥要-1,因为下面会有j+1
                if aList[j]>aList[j+1]:
                    temp=aList[j+1]
                    aList[j+1]=aList[j]
                    aList[j] = temp
        return aList
    
    
    start_time=time.time()
    print bubble_sort(aList)[0:20]
    end_time=time.time()
    print end_time-start_time



    # 给5万个数排序,282秒

    3.2 选择排序

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    __author__ = 'WangQiaomei'
    
    import random
    import time
    
    '''
    aList=[]
    for i in range(20):
        aList.append(random.randrange(100))
    print aList
    '''
    aList=[]
    for i in range(50000):
        aList.append(random.randrange(100000))
    
    #print aList
    
    def select_sort(aList):
        for i in range(len(aList)):
            for j in range(i,len(aList)):
                if aList[i]>aList[j]:
                    temp = aList[i]
                    aList[i]= aList[j]
                    aList[j]= temp
        return aList
    
    
    start_time=time.time()
    print select_sort(aList)[0:20]
    end_time=time.time()
    print end_time-start_time

    '''
    第一层循环是循环整个列表
    第二层是从i开始循环,到列表结尾。
    如果找到比aList[i]小的元素,就和aList[i]交换。
    和冒泡算法的时间和空间复杂度基本是一样的。区别是:
    1)冒泡算法是两两交换,循环交换的结果把最大的数依次放在最后面。
    2)选择排序是把当前的数和aList[i],循环结果就是把最小的数依次放在前面。
    # 给5万个数排序,225秒
    '''

    3.3 直接插入排序

    插入排序(Insertion Sort)的基本思想是:将列表分为2部分,左边为排序好的部分,右边为未排序的部分,循环整个列表,每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中的适当位置,直到全部记录插入完成为止。

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    __author__ = 'WangQiaomei'

    import random
    import time

    '''
    aList=[]
    for i in range(20):
        aList.append(random.randrange(100))
    print aList
    '''
    '''
    插入排序,是已经有一个已排序列表,索引是:0到i-1,靠向右移动一个位置,腾出一个空位来插入当前值。
    详细:
    由于插入排序是:通过从索引“0到i-1”已经排好序的列表中,由iCur(aList[i])去从跟从i-1,一直旺座不停的比对,一直到<某个元素,
    就插入到某个元素的前面。其实就是顺序查找插入,时间复杂度;O(n)。
    注意:这里不可能改变列表的个数,当然更不能用insert语句了。
    那怎么保持原列表不变,从已经排好序的列表里(0到i-1),插入一个数呢?
    就通过:
    如果iCur的左边的紧靠的元素比它大,要把左边的元素一个一个的往右移一位,
    直到iCur比aList[position-1]小,iCur插入到左边挪一个位置出来
    也就是靠向右移动位置,给icur腾出一个空位,以便插入!
    '''

    aList=[]
    for i in range(50000):
        aList.append(random.randrange(100000))

    def insert_sort(aList):
        for i in range(1,len(aList)):   # 默认列表第一个已经排好序了
            iCur= aList[i]
            position=i
            while position>0 and iCur<aList[position-1]:
                aList[position]=aList[position-1]
                position-=1
            aList[position]=iCur
        return aList


    start_time=time.time()
    print insert_sort(aList)[0:20]
    end_time=time.time()
    print end_time-start_time

    '''
    # 给5万个数排序,149秒
    '''

    四、快速排序

    设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动  

    注:在待排序的文件中,若存在多个关键字相同的记录,经过排序后这些具有相同关键字的记录之间的相对次序保持不变,该排序方法是稳定的;若具有相同关键字的记录之间的相对次序发生改变,则称这种排序方法是不稳定的。

    要注意的是,排序算法的稳定性是针对所有输入实例而言的。即在所有可能的输入实例中,只要有一个实例使得算法不满足稳定性要求,则该排序算法就是不稳定的。 

    4.1 快速排序--清爽代码

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    __author__ = 'WangQiaomei'


    def quick_sort(aList, left, right):
        if left >= right:
            return
        low = left
        high = right
        k = aList[left]
        while low < high:
            while high > 0 and low != high and aList[low] <= aList[high]:
                high -= 1
            temp = aList[low]
            aList[low] = aList[high]
            aList[high] = temp

            while low != high and aList[high] > aList[low]:
                low += 1
            temp = aList[high]
            aList[high] = aList[low]
            aList[low] = temp
        quick_sort(aList, left, low - 1)
        quick_sort(aList, low + 1, right)


    aList = [83, 83, 94, 51, 9, 79, 40, 68, 12, 17]

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

    '''
    打印结果:
    [83, 83, 94, 51, 9, 79, 40, 68, 12, 17]
    [9, 12, 17, 40, 51, 68, 79, 83, 83, 94]
    '''

    4.2 快速排序-错误示范讲解

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    __author__ = 'WangQiaomei'
    import random
    '''
    aList=[6,2,5,11,3,8,9]
    6,2,5,11,3,8,9  current_val=6 索引:low=0,high=6 初始值
    此时算hight的:从后往前,跟6比大小,high的索引递减,这里递减了2次,注意:直到遇见比6小的,才交换位置。
    3,2,5,11,6,8,9  current_val=6 索引:low=0,high=4
    此时算low的:从前往后,跟6比大小,low的索引递增,这里递增了3次,注意:直到遇见比6大的,才交换位置。
    3,2,5,6,11,8,9  current_val=6 索引:low=3,high=4
    此时算hight的:从后往前,因为11比6大,high的索引-1=3,不交换位置。此时:low=high=6,
    这是第一次排序,把比6小的发在6的左边,把比6大的放在6的右边。
    3,2,5,6,11,8,9  current_val=6 索引:low=3,high=3


    开始第二次排序,先排序6的左边:此时6的位置已经固定,不需要排它了!
    注意:此时low和high的索引还是针对完整列表(第一次排序好的列表:
    [3,2,5,6,11,8,9])来说的。因为是给aList排序,不是给[3,2,5]排序!
    3,2,5           current_val=3 索引:low=0,high=2  初始值
    2,3,5           current_val=3 索引:low=0,high=1  从后向前,交换
    2,3,5           current_val=3 索引:low=1,high=1  不交换

    开始第三次排序,排6的右边:此时6的位置已经固定,不需要排它了!
    注意:此时low和high的索引还是针对第二次排序好的列表:
    [2,3,5,6,11,8,9])来说的。
    11,8,9          current_val=11 索引:low=4,high=6 初始值
    9,8,11          current_val=11 索引:low=4,high=5 从后向前,交换
    8,9,11          current_val=11 索引:low=5,high=5 从前向后,交换

    第三次排序好的列表,就是最终结果,因为左边列表 和右边的列表,都分别得到了一个值。
    最终结果:
    [2,3,5,6,8,9,11]
    '''


    def quick_sort(aList, left,right):  # left 和right是为了递归用的。
        # if low==high and low>0:
        if left>=right:
            return
        low=left            # 这两句是为了递归用的
        high= right
        k = aList[left]     # 定的固定值,以它为标杆排序
        while low<high:
            # print 'aList[low]:',aList[low]
            # print 'aList[high]:',aList[high]
            # print 'low:',low
            # print 'high:',high
            print aList

            # while high>0 and low!=high and aList[low]<=aList[high]: # 此时 aList[low]是关键字,递归low的值。这是对的
            '''
            注意:这里high是递减的,可能high会减成负数,会报错,所以high>0
            high是递减的,如果high减的跟low相等了,不应该再执行,结果再执行high-=1,则high<low,结果错误的交换了!

            如下所示:
            [9, 12, 51, 17, 40, 68, 79, 83, 94, 83]
            low1: 5
            high1: 5
            low2: 5
            high2: 4
            [9, 12, 51, 17, 68, 40, 79, 83, 94, 83]

            所以,这里应该写成:
            while high>0 and low!=high and aList[low]<=aList[high]:
            或者直接写成:
            while low<high and aList[low]<=aList[high]:
            '''
            # 有时候有多个相同的元素,所以要写成<=
            while high>0 and aList[low]<=aList[high]: # 此时 aList[low]是关键字,递归low的值。应该加上条件:and low!=high

            # while high>0 and k<=aList[high]:    # 这样写也是对的,关键字就是k。这时候k就是aList[low]
                if low==high:
                    print 'low1:',low
                    print 'high1:',high
                high-=1
            print 'low2:',low
            print 'high2:',high
            temp=aList[low]         # 其实是low和high之间互换位置。
            aList[low]= aList[high]
            aList[high]=temp
            print aList
            #print 'sec_aList[low]:',aList[low]
            #print 'sec_aList[high]:',aList[high]
            # 因为low和high是互相变动的,有的时候low是关键字,有的时候high是关键字,有的时候low=high=关键字
            # 而每次while循环,都只针对一个固定值,小的放左边,大的放右边

            '''
            这里也一样,如果low+=1的话,low和high相等的时候(本来不应该执行了),low再加1,则又做了错误的交换了。
            '''
            # while low!=high and aList[high]>aList[low]:   # 此时 aList[high]是关键字,递归low的值 是对的
            while aList[high]>aList[low]:   # 此时 aList[high]是关键字,递归low的值 ,应该加上条件:andand low!=high
            # while k>aList[low]: # 这样写也是对的,关键字就是k。这时候k就是aList[high]
                if low==high:
                    print 'low3:',low
                    print 'high3:',high
                low+=1
            temp=aList[high]
            aList[high]= aList[low]
            aList[low]=temp

            #print 'result:',aList
        # if low==high and low>0:
        # quick_sort(aList, 0,low-1)
        #print 'left:',left
        #print 'low-1:',low-1
        # quick_sort(aList, 0,low-1)
        quick_sort(aList, left,low-1)
        # quick_sort(aList, low+1,len(aList)-1)
        quick_sort(aList, low+1,right)

        #print aList

    '''
    aList=[]
    for i in range(20):
        aList.append(random.randrange(100))
    print aList

    aList=[6,2,5,11,3,8,9]

    aList=[]
    for i in range(10):
        aList.append(random.randrange(100))
    '''

    aList=[83, 83, 94, 51, 9, 79, 40, 68, 12, 17]
    # aList=[83, 83, 94, 51, 9, 79, 40]
    print aList
    quick_sort(aList, 0,len(aList)-1)
    print aList

    4.3 快速排序--正确

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    __author__ = 'WangQiaomei'
    import random
    '''
    aList=[6,2,5,11,3,8,9]
    6,2,5,11,3,8,9  current_val=6 索引:low=0,high=6 初始值
    此时算hight的:从后往前,跟6比大小,high的索引递减,这里递减了2次,注意:直到遇见比6小的,才交换位置。
    3,2,5,11,6,8,9  current_val=6 索引:low=0,high=4
    此时算low的:从前往后,跟6比大小,low的索引递增,这里递增了3次,注意:直到遇见比6大的,才交换位置。
    3,2,5,6,11,8,9  current_val=6 索引:low=3,high=4
    此时算hight的:从后往前,因为11比6大,high的索引-1=3,不交换位置。此时:low=high=6,
    这是第一次排序,把比6小的发在6的左边,把比6大的放在6的右边。
    3,2,5,6,11,8,9  current_val=6 索引:low=3,high=3


    开始第二次排序,先排序6的左边:此时6的位置已经固定,不需要排它了!
    注意:此时low和high的索引还是针对完整列表(第一次排序好的列表:
    [3,2,5,6,11,8,9])来说的。因为是给aList排序,不是给[3,2,5]排序!
    3,2,5           current_val=3 索引:low=0,high=2  初始值
    2,3,5           current_val=3 索引:low=0,high=1  从后向前,交换
    2,3,5           current_val=3 索引:low=1,high=1  不交换

    开始第三次排序,排6的右边:此时6的位置已经固定,不需要排它了!
    注意:此时low和high的索引还是针对第二次排序好的列表:
    [2,3,5,6,11,8,9])来说的。
    11,8,9          current_val=11 索引:low=4,high=6 初始值
    9,8,11          current_val=11 索引:low=4,high=5 从后向前,交换
    8,9,11          current_val=11 索引:low=5,high=5 从前向后,交换

    第三次排序好的列表,就是最终结果,因为左边列表 和右边的列表,都分别得到了一个值。
    最终结果:
    [2,3,5,6,8,9,11]
    '''


    def quick_sort(aList, left,right):  # left 和right是为了递归用的。
        # if low==high and low>0:
        if left>=right:
            return
        low=left            # 这两句是为了递归用的
        high= right
        k = aList[left]     # 定的固定值,以它为标杆排序
        while low<high:
            # print 'aList[low]:',aList[low]
            # print 'aList[high]:',aList[high]
            # print 'low:',low
            # print 'high:',high
            print aList

            # while high>0 low!=high and aList[low]<=aList[high]: # 此时 aList[low]是关键字,递归low的值。这是对的
            '''
            注意:这里high是递减的,可能high会减成负数,会报错,所以high>0
            high是递减的,如果high减的跟low相等了,不应该再执行,结果再执行high-=1,则high<low,结果错误的交换了!

            如下所示:
            [9, 12, 51, 17, 40, 68, 79, 83, 94, 83]
            low1: 5
            high1: 5
            low2: 5
            high2: 4
            [9, 12, 51, 17, 68, 40, 79, 83, 94, 83]

            所以,这里应该写成:
            while high>0 and low!=high and aList[low]<=aList[high]:
            或者直接写成:
            while low<high and aList[low]<=aList[high]:
            '''
            # 有时候有多个相同的元素,所以要写成<=
            while high>0 and low!=high and aList[low]<=aList[high]:
            # while high>0 and aList[low]<=aList[high]: # 此时 aList[low]是关键字,递归low的值。应该加上条件:and low!=high

            # while high>0 and k<=aList[high]:    # 这样写也是对的,关键字就是k。这时候k就是aList[low]
                '''
                if low==high:
                    print 'low1:',low
                    print 'high1:',high
                '''
                high-=1
            print 'low2:',low
            print 'high2:',high
            temp=aList[low]         # 其实是low和high之间互换位置。
            aList[low]= aList[high]
            aList[high]=temp
            print aList
            #print 'sec_aList[low]:',aList[low]
            #print 'sec_aList[high]:',aList[high]
            # 因为low和high是互相变动的,有的时候low是关键字,有的时候high是关键字,有的时候low=high=关键字
            # 而每次while循环,都只针对一个固定值,小的放左边,大的放右边

            '''
            这里也一样,如果low+=1的话,low和high相等的时候(本来不应该执行了),low再加1,则又做了错误的交换了。
            '''
            while low!=high and aList[high]>aList[low]:   # 此时 aList[high]是关键字,递归low的值 是对的

            #while aList[high]>aList[low]:   # 此时 aList[high]是关键字,递归low的值 ,应该加上条件:andand low!=high
            # while k>aList[low]: # 这样写也是对的,关键字就是k。这时候k就是aList[high]
                '''
                if low==high:
                    print 'low3:',low
                    print 'high3:',high
                '''
                low+=1
            temp=aList[high]
            aList[high]= aList[low]
            aList[low]=temp

            #print 'result:',aList
        # if low==high and low>0:
        # quick_sort(aList, 0,low-1)
        #print 'left:',left
        #print 'low-1:',low-1
        # quick_sort(aList, 0,low-1)
        quick_sort(aList, left,low-1)
        # quick_sort(aList, low+1,len(aList)-1)
        quick_sort(aList, low+1,right)

        #print aList

    '''
    aList=[]
    for i in range(20):
        aList.append(random.randrange(100))
    print aList

    aList=[6,2,5,11,3,8,9]

    aList=[]
    for i in range(10):
        aList.append(random.randrange(100))
    '''

    aList=[83, 83, 94, 51, 9, 79, 40, 68, 12, 17]
    # aList=[83, 83, 94, 51, 9, 79, 40]
    print aList
    quick_sort(aList, 0,len(aList)-1)
    print aList

    01背包问题

    def fastMaxVal(w, v, i, aW, m):
        global numCalls
        numCalls += 1
        # print 'maxVal called with:',i,aW
        try:
            return m[(i, aW)]
    
        except KeyError:
            if i == 0:
                if w[i] <= aW:
                    m[(i, aW)] = v[i]
                    return v[i]
                else:
                    m[(i, aW)] = 0
                    return 0
            without_i = fastMaxVal(w, v, i - 1, aW, m)
            if w[i] > aW:
                m[(i, aW)] = without_i
                return without_i
            else:
                with_i = v[i] + fastMaxVal(w, v, i - 1, aW - w[i], m)
            res = max(with_i, without_i)
            m[(i, aW)] = res
            return res
    
    
    def maxVal0(w, v, i, aW):
        m = {}
        return fastMaxVal(w, v, i, aW, m)
    weights = [5, 3, 2]
    vals = [9, 7, 8]
    numCalls = 0
    res = maxVal0(weights, vals, len(vals) - 1, 5)
    print 'max Val=', res, 'number of calls = ', numCalls

    装饰器:

    def auth(func):
        def inner(*args,**kwargs):
            # key = kwargs['token']
            # del kwargs['token']
            key = kwargs.pop('token')
            is_login = login(key)
            if not is_login:
                return '非法用户'
            temp = func(*args,**kwargs)
            print 'after'
            return temp
        return inner

    三层装饰器:

  • 相关阅读:
    NET5 WebApi 解决跨域问题
    CentOS7安装MYSQL
    VMWare安装CentOS7
    MSSQL还原数据库,更改用户登陆权限
    Vue自定义页面路由
    解决VSCODE"因为在此系统上禁止运行脚本"报错
    简析 HTTP 2.0 多路复用
    Git放弃本地修改,强制拉取最新版
    eclipse安装OpenExplorer插件--快速打开文件目录
    git统计某段时间内代码的修改量/总代码量
  • 原文地址:https://www.cnblogs.com/wangqiaomei/p/5500890.html
Copyright © 2020-2023  润新知