基础算法python

快速排序

思路:
  1. 首先设定一个分界值(基准值),通过该分界值将数组分成左右两部分。
  2. 将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于分界值,而右边部分中各元素都大于或等于分界值。
  3. 左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
  4. 重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

注意⚠️:要注意边界问题!!

快排不一定准确

image-20240325220848549

# 暴力方法 看似暴力但是时间复杂度是一样的
# 第一种
n =int(input())
nums =list(map(int, input().split()))

def quick_sort(nums):

    if(len(nums) <=1): return nums;

    left = []
    mid = []
    right = []
    pivot =nums[len(nums)//2]
    for x in nums:
        if x < pivot:
            left.append(x)
        elif x > pivot:
            right.append(x)
        else:
            mid.append(x)
    return quick_sort(left) +mid + quick_sort(right)


if __name__ =="__main__":
    nums =quick_sort(nums)
    print(" ".join(list(map(str, nums))))
    
# 第二种
n =int(input())
nums =list(map(int, input().split()))

def quick_sort(nums):

    if(len(nums) <=1): return nums;

    privot =nums[len(nums)//2]
    left =[x for x in nums if x < privot]
    mid =[x for x in nums if x == privot]
    right =[x for x in nums if x> privot]
    #print (" ".join(list(map(str, left))) +","+ str(mid[0]) +","+" ".join(list(map(str, right))) )
    return quick_sort(left) +mid + quick_sort(right)


if __name__ =="__main__":
    nums =quick_sort(nums)
    print(" ".join(list(map(str, nums))))

#双指针方法
def quick_sort(l,r,data):
    if l >= r:
        return
    i = l - 1 # 左端指针
    j = r + 1 # 右端指针
    pivot = data[(i+j) // 2] #中间值,随意取;但是一般选择中间值
    while i < j:
        while 1:
            i += 1 # 左边指针向右移
            if data[i] >= pivot: #找到不小于privot的然后break
                break
        while 1: # 右边指针向左移
            j -= 1
            if data[j] <= pivot: # 找到不大于privot的然后break
                break
        if i < j:
            data[i],data[j] = data[j],data[i] # 如果此时i小于j,则交换
    quick_sort(l,j,data) #对左边子区间进行排序
    quick_sort(j+1,r,data) #对右半子区间进行排序


def main():
    l = 0
    r = n-1
    quick_sort(l,r,data)

if __name__ == "__main__":
    n = int(input()) #输入数组长度
    data = [int(x) for x in input().split()] #输入数组
    main()
    print(' '.join(list(map(str, data))))


找到第k个数:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

答案:

n, k = map(int,input().split())
nums = list(map(int,input().split()))


def quick_sort(nums):
    if (len(nums) <= 1): return nums;

    privot = nums[len(nums) // 2]
    left = [x for x in nums if x < privot]
    mid = [x for x in nums if x == privot]
    right = [x for x in nums if x > privot]
    return quick_sort(left) + mid + quick_sort(right)



if __name__ == "__main__":
    nums = quick_sort(nums)
    print(nums[k -1])

归并排序

区别于快排:快排的分界值可以是任意的

但是归并排序是下标的中间值

核心:

一个是将数组一分为二,一个无序的数组成为两个数组.

另外一个操作就是,合二为一,将两个有序数组合并成为一个有序数组.

方法:
  1. 先一分为二
  2. 递归排序
  3. 归并,将左右两个有序序列合并为一个序列
def merge_sort(data, l, r): # 数组的两个边界是l\r
    if l >= r:
        return
    mid = (l + r) // 2 # 划分数据
    merge_sort(data, l, mid)
    merge_sort(data, mid + 1, r)
    tmp = [] # 创建一个临时列表
    i = l
    j = mid + 1
    while (i <= mid) and (j <= r): # 两指针i/j向中间移动,比较数值,添加到tmp中
        if data[i] <= data[j]:
            tmp.append(data[i])
            i += 1
        else:
            tmp.append(data[j])
            j += 1
    # 两数组合并
    tmp += data[i:mid + 1] # 将左侧分区的所有元素添加到tmp列表中
    tmp += data[j:r + 1] # 将右侧分区的所有元素添加到tmp列表中
    data[l:r + 1] = tmp # tmp替换原来的data


if __name__ == "__main__":
    n = int(input())
    data = list(map(int,input().split()))
    merge_sort(data, l, r)
    print(merge_sort(0,n-1))
逆序队数量:

image-20240326221203414

三类:
  1. 逆序对都在左半边
  2. 逆序对都在右半边
  3. 逆序对都在两边都有

image-20240326221727215

def merge_sort(l,r):
    if l >= r:
        return 0
    mid = (l + r) // 2
    i = l
    j = mid + 1
    tmp = []
    num = merge_sort(l,mid) + merge_sort(mid+1,r)
    while i <= mid and j <= r:
        if data[i] <= data[j]:
            tmp.append(data[i])
            i += 1
        else:
            tmp.append(data[j])
            j += 1
            num += mid - i + 1 # 如果不满足,那么第一个数组中剩余的所有数,都会和它构成逆序对
    tmp.extend(data[i:mid+1])
    tmp.extend(data[j:r+1])
    data[l:r+1] = tmp
    return num

if __name__ == "__main__":
    n = int(input())
    data = list(map(int,input().split()))
    print(merge_sort(0,n-1))

二分

  • 整数二分:

    有单调性一定可以二分,可以二分的题不一定二分
    找到某个性质,可以将区间一分为二,二分可以寻找边界

    1. 二分前,先将搜索范围分为两个部分并定义check函数以检验是否满足其中一个区间的性质

    2. l和r的范围内一定包含了最终的边界点

    3. 此模板更像是边界二分法

      右边界:True: [mid,r];False:[l,mid-1]

      左边界:True:[l,mid];False:[mid+1,r]

    4. 为什么寻找右边界(x⩽c)时,mid = (l+r+1) // 2,而寻找左边界(x⩾c)时,mid = (l+r) // 2

      因为寻找右边界…],需要不断往后移动,所以必须得向后多一位

      因为寻找左边界[…,需要不断迁移,所以必须向前多一位

  • 注意⚠️:所谓的二分算法,就是我们知道当前的候选区间中,一定存在我们要找到的答案,而且我们发现这个区间拥有单调性质此类的性质,那么我们可以不停地缩减候选区间的范围,达到排除无用答案的效果.

数的范围:

image-20240328165614324

n,m=map(int,input().split())
data=list(map(int,input().split()))

def dichotomous(data,n,m):
    while m:
        target=int(input())
        l=0
        r=n-1
        # 先寻找左边界,即>=target的分界点
        while l<r:
            mid=(l+r)//2 # 左边界
            if data[mid]>=target:
                r=mid
            else:
                l=mid+1
        if data[l]!=target:
            print("-1-1")
        else:
            print(l,end=" ") 
            l=0
            r=n-1
            # 寻找右边界,即<=target的分界点
            while l<r:
                mid=(l+r+1)//2 # 右边界
                if data[mid]<=target:
                    l=mid
                else:
                    r=mid-1
            print(l)

if __name__ == "__main__":
    for k in range(m):
        dichotomous(data,n,m)
数的三次方根

image-20240328204002666

# 给定一个浮点数n,求它的三次方根
# 方法一 :没有用到二分法
def cubic_root(n):
    if n>=0:
        return "{:.6f}".format(n ** (1/3))  
    else:
        n = -n
        root=format(n ** (1/3))  
        return "{:.6f}".format(-float(root))

if __name__== '__main__':
    n = float(input())
    print(cubic_root(n))
    
  • 浮点二分

除了上面有提到的整数二分法,这个题就是很典型的浮点数二分

整数二分和浮点数二分的区别:
唯一区别是浮点数没有整除, 区间长度可以严格的缩小一半

l=mid;

r=mid;

过程:

  1. 首先输入x在区间-10000~10000
  2. 寻找p点(也就是x的三次方根)
  3. 二分找边界值
if __name__ == '__main__':
    n = float(input())
    st = 0 # 标记位,用来标记n是否是负数
    if n < 0:
        st = 1
        n = -n
    l, r = 0, 100
    while ((r - l) > 1e-8):
        mid = (l + r) / 2.0
        if (mid**3 >= n): 
            r = mid # 边界移动
        else: 
            l = mid
    if st:
        l = -l
    print("{:.6f}".format(l)) # 保留6位小数

高精度

注意⚠️:其实python自带高精度哈哈哈!

print(int(input())-int(input()))
print(int(input())+int(input()))
print(int(input())*int(input()))
print(int(input())/int(input()))
# 这样输出也是高精度
高精度加法

思路:
模拟手动加法计算

例如计算:567 + 28

  1. 先个位相加: 7 + 8 = 15,所以结果的个位是5,向十位进 1

  2. 再十位相加: 6 + 2 + 1(进位)= 9, 所以十位是 9,向百位进 0

  3. 再百位相加: 5 + 0 = 5, 所以结果的百位是 5

  4. 综上,计算结果为 595

算法

计算 567 + 28

  1. 用 a, b 两个字符串存储输入。a = 567, b = 28

  2. 为了方便计算,将两个数分别 倒序 存放在 A, B 两个整数数组中。 A = [7, 6, 5], B = [8, 2]

  3. 新建整数数组 C 保存结果,整型变量 t 保存进位,初始 t = 0.

  4. 将各个位上的数字相加,求出结果对应位上的数字和进位。

    例如对个位计算: A[0] + B[0] = 7 + 8 = 15, 结果个位上是 5, 进位是 1. 所以 C[0] = 5, 进位 t = 1

  5. 最后把结果数组 C 中就保存了计算倒序结果,倒序输出就是答案

模版:

# 高精度加法
a=list(input())
b=list(input())
def add(a,b):
    a.reverse() # 倒叙相加嘛
    b.reverse()
    c = [0]*max(len(a),len(b)+1) # 生成一个长度为a和b中最长的数+1的数组
    for i in range(len(a)):
        c[i] = int(a[i])
    for i in range(len(b)):
        c[i] += int(b[i])
    for i in range(len(c)-1):
        if c[i] >= 10: # 如果c[i]大于10,则进位
            c[i] -= 10 
            c[i+1] += 1
    while len(c) > 1 and c[-1] == 0:
        c.pop() # 如果最后一位是0,则删除(也就是最高位)
    c.reverse()
    combined = int(''.join(map(str, c))) # 将数组转化为字符串,再转化为整数
    print(combined)

if __name__ == '__main__':
    add(a,b)
    
高精度减法

思路:

  1. 减法的借位处理
  2. 相减为负数处理
  3. 高位为0处理

模版:

def subtract(a, b):
    a.reverse()
    b.reverse()
    c = [0]*max(len(a),len(b)+1)
    for i in range(len(a)):
        c[i] = int(a[i])
    for i in range(len(b)):
        c[i] -= int(b[i])
    for i in range(len(c)-1):
        if c[i] < 0:
            c[i] += 10
            c[i+1] -= 1
    while len(c) > 1 and c[-1] == 0:
        c.pop()
    c.reverse()
    if c[0] == 0 and len(c) > 1:
        c[0] = '-'
        combined = ''.join(map(str, c))
    else:
        combined = int(''.join(map(str, c)))
    return combined

if __name__ == '__main__':
    a = list(input())
    b = list(input())
    combined2 = ''.join(map(str, a))
    combined3 = ''.join(map(str, b))
    if int(combined2) < int(combined3): #判断a和b的大小,如果a<b,则输出负数
        num=0-subtract(b, a)
        print(num)
    else:
        num=subtract(a, b)
        print(num)

高精度乘法

思路:

  1. a/b循环相乘
  2. 注意进位
  3. 注意高位为0处理

模版:

def multiply(a, b):
    a.reverse()
    b.reverse()
    c = [0]*(len(a)+len(b))
    for i in range(len(a)):  # 循环a
        for j in range(len(b)): # 循环b
            c[i+j] += int(a[i]) * int(b[j]) 
    for i in range(len(c)-1): 
        c[i+1] += c[i] // 10 # 除以10的进位,这样直接取整就是进位,不用和10比较
        c[i] %= 10 # 取余
    while len(c) > 1 and c[-1] == 0:
        c.pop()
    c.reverse()
    return ''.join(map(str, c))

if __name__ == '__main__':
    a = list(input())
    b = list(input())
    print(multiply(a, b))
高精度除法

思路:

  1. a变为数组,b不变
  2. 不需要倒置
  3. 余数需要注意
  4. 高位处理

模版:

def divide(a, b):
    r = 0 # 余数
    res = []
    for i in range(len(a)):
        r = r * 10 + int(a[i])
        res.append(str(r // b)) # 商
        r = r % b # 余数
    while len(res) > 1 and res[0] == '0':
        res.pop(0)
    return ''.join(res)

if __name__ == '__main__':
    a = list(input())
    b = int(input())
    print(divide(a, b))

前缀与差分

前缀和:

什么是前缀和

原数组: a[1], a[2], a[3], a[4], a[5], …, a[n]
前缀和 Si为数组的前 i项和
前缀和: S[i] = a[1] + a[2] + a[3] + … + a[i]

注意: 前缀和的下标一定要从 1开始, 避免进行下标的转换

s[0] = 0
s[1] = a[1]
s[2] = a[1] + a[2]

image-20240329161207527

作用:
快速求出元素组中某段的和

一维例题:

image-20240329152010787

模版:

n,m=map(int,input().split())
a=list(map(int,input().split()))

# 计算前缀和
prefix_sum = [0]*(n+1)
for i in range(1, n+1):
    prefix_sum[i] = prefix_sum[i-1] + a[i-1]

def query(l, r):
    return prefix_sum[r] - prefix_sum[l-1] # 返回区间和

if __name__ == '__main__':
    for _ in range(m): # 这里_表示一个临时变量,不会被使用
        l,r=map(int,input().split())
        print(query(l,r))
二维例题:

image-20240329161512632

思路:

  1. S [ i , j ] S[i, j] S[i,j] 即为图1红框中所有数的的和为:

S [ i , j ] = S [ i , j − 1 ] + S [ i − 1 , j ] − S [ i − 1 , j − 1 ] + a [ i , j ] S[i, j]=S[i, j-1]+S[i-1, j]-S[i-1, j-1]+a[i, j] S[i,j]=S[i,j1]+S[i1,j]S[i1,j1]+a[i,j]

  1. ( x 1 , y 1 ) , ( x 2 , y 2 ) \left(x_1, y_1\right),\left(x_2, y_2\right) (x1,y1),(x2,y2) 这一子矩阵中的所有数之和为:

S [ x 2 , y 2 ] − S [ x 1 − 1 , y 2 ] − S [ x 2 , y 1 − 1 ] + S [ x 1 − 1 , y 1 − 1 ] S\left[x_2, y_2\right]-S\left[x_1-1, y_2\right]-S\left[x_2, y_1-1\right]+S\left[x_1-1, y_1-1\right] S[x2,y2]S[x11,y2]S[x2,y11]+S[x11,y11]

image-20240329172937940

模版:

# 二维差分
if __name__ == "__main__":
    n, m, q = map(int, input().split())
    N = 1010 # 范围要大一些
    a = [[0] * N for i in range(N)]  # 二维差分数组
    s = [[0] * N for i in range(N)]  # 前缀和数组
    for i in range(1, n + 1):
        a[i] = [0] + list(map(int, input().split())) + a[m + 1:] #这里是将输入的数据放到a数组中,其中a[m+1:]是为了保证a[i]的长度为m+1
    for i in range(1, n + 1):
        for j in range(1, m + 1):
            s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j]

    while q:
        x1, y1, x2, y2 = map(int, input().split())
        print(s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1])
        q -= 1
差分:

差分数组:

首先给定一个原数组a:a[1], a[2], a[3], a[n];

然后我们构造一个数组b : b[1] ,b[2] , b[3], b[i];

使得 a[i] = b[1] + b[2 ]+ b[3] +, + b[i]

也就是说,a数组是b数组的前缀和数组,反过来我们把b数组叫做a数组的差分数组。换句话说,每一个a[i]都是b数组中从头开始的一段区间和。

构造差分数组:
image-20240329183832713

一维差分:

image-20240329233447747

模版:

# 方法一 (但是这个会超时)因为他的时间复杂度query(l−r) 
from typing import List
n, m = map(int, input().split())
a = list(map(int, input().split()))

def differnance(n: int, m: int, a: List[int]) -> List[int]:
    for i in range(m):
        l, r, c = map(int, input().split())
        for j in range(l-1,r):
            a[j] += c
    print(' '.join(map(str,a[:])))


if __name__ == '__main__':
    differnance(n, m, a)
# 方法二  这个时间复杂度低一些
n, m = map(int, input().split())
a = list(map(int, input().split()))
diff_data = [0] * (n + 1) # 差分数组
diff_data[0] = a[0] # 注意:差分数组的第一个元素等于原数组的第一个元素
for i in range(1, n):
    diff_data[i] = a[i] - a[i - 1]

def differnance(n, m, a):
    data=[0]*(n+1) # 新数组
    for i in range(m):
        l, r, c = map(int, input().split())
        diff_data[l - 1] += c # 差分数组的第l个元素加c
        diff_data[r] -= c # 差分数组的第r+1个元素减c,这样避免影响到r+1之后的元素
    for i in range(1, n + 1):
        data[i] = diff_data[i-1] + data[i - 1] # 通过差分数组还原原数组,这里i-1是因为差分数组的第一个元素等于原数组的第一个元素
    print(' '.join(map(str, data[1:])))

if __name__ == '__main__':  
    differnance(n, m, a)
二维差分:

image-20240330001820623

  • b[x1][ y1 ]+=c ; 对应图1 ,让整个a数组中蓝色矩形面积的元素都加上了c。
  • b[x1,][y2+1]-=c ; 对应图2 ,让整个a数组中绿色矩形面积的元素再减去c,使其内元素不发生改变。
  • b[x2+1][y1]- =c ; 对应图3 ,让整个a数组中紫色矩形面积的元素再减去c,使其内元素不发生改变。
  • b[x2+1][y2+1]+=c; 对应图4,让整个a数组中红色矩形面积的元素再加上c,红色内的相当于被减了两次,再加上一次c,才能使其恢复。

例题:
image-20240330002424632

模版:

# 二维差分
def insert(b ,x1, y1, x2, y2, c):
    b[x1][y1] += c
    b[x2 + 1][y1] -= c
    b[x1][y2 + 1] -= c
    b[x2 + 1][y2 + 1] += c

n,m,q=map(int,input().split())
a=[[0]*(m+2) for i in range(n+2)]
b=[[0]*(m+2) for i in range(n+2)]
for i in range(1,n+1):
    row=list(map(int,input().split())) # 读入一行
    for j in range(1,m+1):
        a[i][j]=row[j-1] # 将元素放入矩阵中
        insert(b,i,j,i,j,a[i][j]) # 原始矩阵插入差分矩阵
while q:
    q-=1
    x1,y1,x2,y2,c=map(int,input().split())
    insert(b,x1,y1,x2,y2,c) # 插入差分矩阵
for i in range(1, n+1):
    for j in range(1, m+1):
        b[i][j] += b[i-1][j] + b[i][j-1] - b[i-1][j-1] # 差分矩阵前缀和
        print(b[i][j], end=' ')
    print()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值