时间复杂度与排序算法

时间复杂度

时间复杂度是一个算法流程中,最差情况下常数操作数量的指标。

例子:一个有序数组A,另一个无序数组B,请打印B中所有不在A中的数。A数组长度为N,B数组长度为M。
第一种方法:B中每个数在A中遍历一下,输出所有不在A中的数。

A = list(map(int,input().split()))
B = list(map(int,input().split()))

N = len(A)
M = len(B)

for i in range(M):
    j = 0
    while j<N and B[i]!=A[j]:
        j += 1
    if j==N:
        print(B[i])

第一种方法包含两个循环,对B遍历一遍,B中每个数又要在A中遍历一遍,因此时间复杂度为 O ( M × N ) O(M\times N) O(M×N)

第二种方法:B中每个数在A中二分找一下。
因A有序(假设升序),将A进行二分,分为leftright。中间位置的数为mid。将B中每个数先与mid相比较,如果B[i]mid大,那么B[i]只可能在A的right,如果B[i]mid小,那么B[i]只可能在left。每次砍一半的操作,时间复杂度为 O ( log ⁡ 2 N ) O(\log_2N) O(log2N),又对B中每个数遍历一次,故这个算法的时间复杂度为 M × O ( log ⁡ 2 N ) M\times O(\log_2N) M×O(log2N)

二分法的停止条件是左边界超过右边界,while L<=R,也不用担心mid在加加减减过程中越界。

A = list(map(int,input().split()))
B = list(map(int,input().split()))
N = len(A)
M = len(B)

def binSearch(A,b):
    L = 0
    R = len(A)-1
    while L<=R:
        mid = int((L+R)/2)
        # 在A中找到b了,返回True
        if b==A[mid]:
            return True
        # b比A[mid]小,说明b在左边
        if b<A[mid]:
            R = mid - 1
        # b比A[mid]大,说明b在右边
        if b>A[mid]:
            L = mid + 1
    # 没找到b,返回False
    return False

for i in range(M):
    flag = binSearch(A,B[i])
    if not flag:
        print(B[i])

第三种方法:先给B排序,然后用类似外排的方法打印不在A中的数。
关于排序算法之后单独介绍,对B排序后,在A,B均为有序数组。
在这里插入图片描述
a指针指向1,b指针指向3,当a<b时,a只能往后移动,当a=b时,b只能往后移动,当a>b时,打印b,同时b往后移。
(1)排序代价 O ( M × log ⁡ M ) O(M\times\log M) O(M×logM) (2)打印数过程中,a最多指 N N N个数,b最多指 M M M个数,所以时间复杂度 O ( N + M ) O(N+M) O(N+M)
因此整个算法时间复杂度 O ( M × log ⁡ M ) + O ( N + M ) O(M\times\log M)+O(N+M) O(M×logM)+O(N+M)

停止条件是A或B数组已经到界了,如果A到界了,那么B[b,M-1]均需打印出来;如果B到界了,说明B数组里的数已经查找完了。

A = list(map(int,input().split()))
B = list(map(int,input().split()))
N = len(A)
M = len(B)

a = b = 0
while a<N and b<M:
    if A[a]<B[b]:
        a += 1
    elif A[a]==B[b]:
        b += 1
    else:
        print(B[b])
        b += 1

while b<M:
    print(B[b])
    b += 1

排序算法

冒泡排序

算法思想:从小到大排。每次找到最大的数放在末位。
第一次看0,1位置比较大小,0位置比1位置大就交换。
第二次看1,2位置比较大小,1位置比2位置大就交换,依次进行下去,第一次遍历,最大的数会被排到最后。
第二次只需遍历0至end-1个数,因为最大的数已经到最后,然后重复第一步步骤,直到所有数被排好。

def BubbleSort(arr):
    length = len(arr)
    if not arr or length < 2:
        return arr
    for i in range(length,-1,-1):
        for j in range(0,i-1):
            if arr[j] > arr[j+1]:
                tmp = arr[j]
                arr[j] = arr[j+1]
                arr[j+1] = tmp
    return arr

常数操作次数为 N + ( N − 1 ) + ( N − 2 ) + ⋯ + 2 + 1 N+(N-1)+(N-2)+\cdots+2+1 N+(N1)+(N2)++2+1,所以算法时间复杂度为 O ( N 2 ) O(N^2) O(N2)

选择排序

算法思想:从小到大排。
第一步,从0到N-1位置找到最小的数放在[0]位置上。
第二步,从1到N-1位置找到最小的数放在[1]位置上。

如何确定最小的数? 先找一个基准数,比基准数小的就互换,成为新的基准数。

def SelectionSort(arr):
    length = len(arr)
    if not arr or length < 2:
        return arr
    for i in range(length):
        minNumber = arr[i]
        for j in range(i+1,length):
            if minNumber > arr[j]:
                tmp = minNumber
                minNumber = arr[j]
                arr[j] = tmp
        arr[i] = minNumber
    return arr

算法时间复杂度为 O ( N 2 ) O(N^2) O(N2)

插入排序

算法思想:从小到大排。
每进来一个数,与它前面一个数比较,比前面的数小就交换,到了前一个位置后,在与前前位置的数比较大小,小就交换,大就停止。再进来一个数,按照上面的方法与前面的数交换插入。

def InsertSort(arr):
    length = len(arr)
    if not arr or length < 2:
        return arr
    for i in range(1,length):
        j = i - 1
        while j>=0:
            if arr[j] > arr[j+1]:
                tmp = arr[j]
                arr[j] = arr[j+1]
                arr[j+1] = tmp
                j -= 1
            else:
                j = -1
    return arr

插入排序的时间复杂度与数据状况有关。例如数组[1,2,3,4,5]整体有序,那么在计算时代价为 O ( N ) O(N) O(N),但如果是[5,4,3,2,1]要升序排列,那么代价就是 O ( N 2 ) O(N^2) O(N2),按最差情况计算,所以插入排序算法时间复杂度为 O ( N 2 ) O(N^2) O(N2)

归并排序

算法思想:将数组一分为二,分别把左侧和右侧部分排好序,然后再利用外排的方式将整体排好序。
左右排好序后,需要一个辅助数组,左右两边比较大小,将小的数依次填入辅助数组。额外空间复杂度为O(n)。

def MergeSort(arr):
    length = len(arr)
    if not arr or length < 2:
        return arr
    sortProcess(arr,0,length-1)
    return arr

def sortProcess(arr,L,R):
    if L==R:
        return arr[L]
    mid = (L+R)//2
    sortProcess(arr,L,mid)
    sortProcess(arr,mid+1,R)
    merge(arr,L,mid,R)

def merge(arr,L,mid,R):
    help = []
    p1 = L
    p2 = mid+1
    while p1<=mid and p2<=R:
        if arr[p1] < arr[p2]:
            help.append(arr[p1])
            p1 += 1
        else:
            help.append(arr[p2])
            p2 += 1
    while p1<=mid:
        help.append(arr[p1])
        p1 += 1
    while p2<=R:
        help.append(arr[p2])
        p2 += 1
    for i in range(len(help)):
        arr[L+i] = help[i]

递过算法的时间复杂度计算有一个master公式:
T ( N ) = a × T ( N b ) + O ( N d ) T(N)=a\times T(\frac{N}{b})+O(N^d) T(N)=a×T(bN)+O(Nd)
N b \frac{N}{b} bN表示子过程的规模, a a a表示子过程的个数, O ( N d ) O(N^d) O(Nd)表示除去调用子过程之外,剩余操作的时间复杂度。

  • log ⁡ b a > d \log_ba>d logba>d时,算法复杂度为 O ( N log ⁡ b a ) O(N^{\log_ba}) O(Nlogba)
  • log ⁡ b a = d \log_ba=d logba=d时,算法复杂度为 O ( N d × log ⁡ N ) O(N^d\times\log N) O(Nd×logN)
  • log ⁡ b a < d \log_ba<d logba<d时,算法复杂度为 O ( N d ) O(N^d) O(Nd)

归并排序将数据规模一分为二,分为左右两个子过程,即 a = 2 , b = 2 a=2,b=2 a=2,b=2,且外排时间复杂度为 O ( N ) O(N) O(N) d = 1 d=1 d=1,所以为第二种情况。归并算法的时间复杂度为 O ( N × log ⁡ N ) O(N\times\log N) O(N×logN)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值