注:本文是在学习了acwing的算法基础课后撰写,主要用于记录python版本算法的模板。其中部分参考了acwing众多大佬的题解。

1.快速排序

思想:
用列表中的一个数(pivot)来分割列表,左侧的数都小于pivot,右侧的数都大于pivot。

步骤:
1.确定pivot。左、右、中点、随机都可以,选择不同代码上可能有细微区别。这里选择中点。
2.调整pivot左侧和右侧的数,使左侧的数都小于pivot,右侧的数都大于pivot。利用双指针。
3.递归处理左、右两段。

模板:

def quick_sort(nums, start, end):
    if start >= end:
        return
    left = start
    right = end
    
    # 1.确定pivot。这里选择中点。
    mid = (left + right) // 2
    pivot = nums[mid]
    
    # 2.调整pivot左侧和右侧的数,使左侧的数都小于pivot,右侧的数都大于pivot。利用双指针。
    while left <= right:
        while left <= right and nums[left] < pivot:
            left += 1
        while left <= right and nums[right] > pivot:
            right -= 1
        if left <= right:
            nums[left], nums[right] = nums[right], nums[left]
            left += 1       # 这边一定要left+=1,right-=1.否则如果nums[left] == nums[right] == pivot的话就跳不出循环了
            right -= 1
            
    # 3.递归处理左、右两段。
    quick_sort(nums, start, right)
    quick_sort(nums, left, end)



2.快速选择

思想:
因为只需选出列表中第k大的数,因此不需要对left和right都进行递归,只需要递归k所在那侧即可。
时间复杂度:O(n)
n+n/2+n/4+…<=2n

步骤:
与快排一致,区别是第3步只需递归处理一侧。
1.确定pivot。左、右、中点、随机都可以,选择不同代码上可能有细微区别。这里选择中点。
2.调整pivot左侧和右侧的数,使左侧的数都小于pivot,右侧的数都大于pivot。利用双指针。
3.递归处理左侧或右侧。

模板:

def quick_select(nums, start, end, k):
    if start >= end:
        return nums[k]
    left = start
    right = end
    mid = (left + right) // 2
    pivot = nums[mid]
    
    while left <= right:
        while left <= right and nums[left] < pivot:
            left += 1
        while left <= right and pivot < nums[right]:
            right -= 1
        if left <= right:
            nums[left], nums[right] = nums[right], nums[left]
            left += 1
            right -= 1
    
    # 只需要递归一侧
    if k <= right:
        quick_select(nums, start, right, k)
    else:
        quick_select(nums, left, end, k)
    return nums[k]
        

if __name__ == "__main__":
    n, k = map(int, input().split())
    nums = list(map(int, input().split()))
    k = k - 1
    res = quick_select(nums, 0, n-1, k)
    print(res)



3.归并排序

思想:
采用分治的思想,通过列表中的中点将列表划分为左右两部分,将左右两个列表分别排序后合并成一个列表。

步骤:
1.确定分界点(中点)。mid = (l+r) // 2
2.递归排序left和right>
3.归并(合二为一),此时left和right都已有序,利用双指针逐个比较即可。

模板:

def merge_sort(nums):
    if len(nums) <= 1:
        return
    
    # 1.确定分界点(中点)
    mid = len(nums) // 2
    
    # 2.递归排序left和right
    L = nums[:mid]
    R = nums[mid:]
    merge_sort(L)
    merge_sort(R)
    
    # 3.归并(合二为一)
    i, j, k = 0, 0, 0
    while i < len(L) and j < len(R):
        if L[i] <= R[j]:
            nums[k] = L[i]
            i += 1
        else:
            nums[k] = R[j]
            j += 1
        k += 1
        
    while i < len(L):
        nums[k] = L[i]
        k += 1
        i += 1
    while j < len(R):
        nums[k] = R[j]
        k += 1
        j += 1



时间复杂度图

python随机2选一 python 二选一_python随机2选一

4.二分

思想:
如果可以找到这样一个性质,将整个区间一分为二,一半满足、一半不满足,就可以寻找这个性质的边界点
二分和单调性不等价,单调可以二分,二分不一定要求单调。

步骤:
1.确定pivot。左、右、中点、随机都可以,选择不同代码上可能有细微区别。这里选择中点。
2.调整pivot左侧和右侧的数,使左侧的数都小于pivot,右侧的数都大于pivot。利用双指针。
3.递归处理左、右两段。

整数二分模板:

整数二分涉及边界问题,故有两种模板。

python随机2选一 python 二选一_数据结构_02


如上图,找left和找right是两种不同情况,找left和right时会使用不同的模板。

def binary_search_right(nums, x):
    l = 0
    r = len(nums) - 1
    while l < r:
        mid = (l + r) // 2
        if check(mid):     # check()函数用于检查是否满足右侧性质
            r = mid
        else:
            l = mid + 1
    else:
        return l
    
    
def binary_search_left(nums, x):
    l = 0
    r = len(nums) - 1
    while l < r:
        mid = (l + r + 1) // 2
        if check(mid):     # check()函数用于检查是否满足左侧性质
            l = mid
        else:
            r = mid - 1
    else:
        return l

实数二分模板:
实数二分由于不存在向上取整还是向下取整的问题,故无需讨论边界。

def binary_search(l, r):
    while r - l > 1e-8:      # 不一定是1e-8,具体根据精度要求来,经验上来说是精度要求乘1e-2
        mid = (l + r) / 2
        if pow(mid, 3) >= n:
            r = mid
        else:
            l = mid
    return l