算法心得(1)

一、回溯算法

利用递归和for循环进行回溯算法 ,让整个程序去遍历所有的可能结果,

for i in range(4):
#a层会从0开始先判断0是否符合条件,符合了则进入b层,如果b不符合就放弃下面的所有,返回a层,在a层从0的下一个元素开始继续判断是否符合条件

典型题目:2n皇后

在知道2n皇后之前可以先看看n皇后的代码

n皇后:

n = int(input())
ans = []
temp_W = [None for i in range(n)]   #临时放一下皇后

def valid_W(temp_W,row): #判断当前的位置上放皇后是否合法
    for j in range(row): 
     #下面这个判断条件是:判断对角线,判断是否是白皇后自己的列
        if abs(j-row) == abs(temp_W[row]-temp_W[j]) or temp_W[row] == temp_W[j]:
            return False
    return True

def dfs_W(temp_W,row):#  #temp记录当前所有合法的皇后的位置,row是继续往下一行里面放皇后
    if row == n:  #当前进行到n,表示找到了白皇后的一种排列方式,跳出递归
        ans.append(temp_W[:])# 保存
        return
    else:
        for col in range(n):#col是列,就是要遍历这一行的所有列,看在某个位置上是否合法
            temp_W[row] = col
            if valid_W(temp_W,row):
                dfs_W(temp_W,row+1)
dfs_W(temp_W,0)
print(len(ans))

2n皇后:

n = int(input())# 行数
a = []# 总棋盘
temp_W = [None for i in range(n)] # 白皇后步数临时存放点
temp_B = temp_W.copy() # 黑皇后临时存放点
total = 0# 总共可行的次数
for i in range(n):
    a.append(input().split())

def check_W(temp_W,row):#用来检查白皇后在该点是否合法:该点是否为1,是否在对角上
    if a[row][temp_W[row]] == '1':# 判断该点是否是1,
        for i in range(row): # 从第0行到row-1行,看是否在对角线上或者是否在一列上
            if abs(i-row)==abs(temp_W[i]-temp_W[row]) or temp_W[i]==temp_W[row]:
                return False
        return True
def check_B(temp_B,temp_W,row):# 用来检查黑皇后是否合法,该点是否为1,该点是否在对角线上,该点是否白皇后已存在
    if a[row][temp_B[row]]=='1' and temp_W[row]!=temp_B[row]:# 判断该点是否是1并且白皇后是否已存在
        for i in range(row):
            if abs(i-row)==abs(temp_B[i]-temp_B[row]) or temp_B[i]==temp_B[row]:
                return False
        return True
def find_W(temp_W,row):# 递归依次查找全部的点是否可以让白皇后下
    if row == n:# 如果到了最后一行,表示现在的temp_W已经可以让白皇后正确摆下了,接下来就是让黑皇后放下
        find_B(temp_B,0)
    else:
        for col in range(n):# 在第n行的整列查看所有的可以供白皇后放下的点
            temp_W[row]= col
            if check_W(temp_W,row):
                find_W(temp_W,row+1)
def find_B(temp_B,row):
    global total
    if row == n:
        total+=1
        return
    else:
        for col in range(n):
            temp_B[row] = col
            if check_B(temp_B,temp_W,row):
                find_B(temp_B,row+1)
find_W(temp_W,0)
print(str(total))

二、中间件

中间件的作用是来辅助整个程序运行,它的作用是用来将这一次的运行完美的执行下去,但是坚决不能打扰到下一次的运行。

典型题目:矩阵幂乘

m,n = map(int,input().split())
numpy = []
#c = [[0 for i in range(m)] for y in range(m)]
#如果c放外面会导致c里面的元素不会清零,因此会使整个值变得非常大,远离正常值
#c放外面说明c是全局的变量,因此在def循环运行的时候,c里面的元素不变的,但是因为c里面的元素是使用c+=这样
#的情况,因此会使得c内的值远大于理想的值
for i in range(m):
    numpy.append(list(map(int,input().split())))
def juzhengchengfa(numpy_1,numpy_2,m):
	# 这里和上面标注的c放的位置不一样会影响结果的最后呈现
    c = [[0 for i in range(m)] for y in range(m)]
    for i in range(m):
        for j in range(m):
            for k in range(m):
                c[i][j] += numpy_1[i][k]*numpy_2[k][j]
    return c
numpy_1=numpy.copy()
if n == 0:
    for i in range(m):
        for j in range(m):
            if i==j:
                numpy_1[i][j] = 1
            else:
                numpy_1[i][j] = 0
else:
    for i in range(n-1):
        print(numpy_1)# 这行在程序运行时可以删除,这里只是起到了讲解的作用
        numpy_1 = juzhengchengfa(numpy_1,numpy,m)
for i in range(m):
    print('{}'.format(' '.join(list(map(str,numpy_1[i])))))

# 把c放在def外面的情况
>>>
2 5
1 2
3 4
[[1, 2], [3, 4]]
[[7, 10], [15, 22]]
[[44, 490], [96, 1070]]
[[1558, 18030], [3402, 39370]]
57206 662210
124914 1445990
# 把c放在def里面的情况
>>>
2 5
1 2
3 4
[[1, 2], [3, 4]]
[[7, 10], [15, 22]]
[[37, 54], [81, 118]]
[[199, 290], [435, 634]]
1069 1558
2337 3406

例子解析:

# 当c放在外面的时候,
第一次c为[[0,0],[0,0]],经过一次def计算之后变成了[[7, 10], [15, 22]]
此时进入第二个def计算,原本的计算公式为c[0][0]+=1*7+2*15 结果应该为37,但是加上了原先的7使得整个结果变成了44.因此下次写的时候一定要注意这点

三、return的位置

return在函数中起到了结束函数运行的作用,并且返回一个值给调用函数的对象。return的位置如果不好或者没有return在关键位置,那么整个代码可能会出现严重的bug

典型题目:分解质因数

a,b = map(int,input().split())
def go(n,total):
    if n == 1:
        return total
    for i in range(2,n+1):
        if n%i==0:
            total.append(i)
            return go(n//i,total)
        #if n%i== 0:
         #   total.append(i)
          #  n //= i
           # if n ==1:
            #    return total
            #else:
             #   return go(n,total)# 这个return是必须要有的,没有这个,go(4,[])进去的时候
#执行else语句,他不在有返回值,当一次次循环出来以后,整个结果是没有返回值的,因为就算确保
# n==1可以return,但是他的上面那一层接受这个返回值以后,就不在往他的父层返回参数,使得最后整个函数形成一个没有返回值的东西。
# 然后这个return不能每一次循环都返回,不然的话会导致所给的值淤积,因此得出结论:
#如果使用递归的时候,想要获得某个值,必须要return这个递归函数。当发现很多数据重复时
# 就要查看return这个函数放置的位置是否正确
def spl(a,b):
    for n in range(a,b+1):
        total = []# 存放输出
        s = go(n,total)
        s = list(map(str,s))
        s = '*'.join(s)
        print('{}={}'.format(n,s))
spl(a,b)

结论:

如果使用递归的时候,想要获得某个值,必须要return这个递归函数。当发现很多数据重复时就要查看return这个函数放置的位置是否正确

四、线段交

当两条线段分别平行于x和y轴时,可以利用线段交来计算两条直线的焦点

典型题目:矩形面积交

x1,y1,x2,y2 = map(float,input().split())
x3,y3,x4,y4 = map(float,input().split())
if x1>x2:
    x1,x2 = x2,x1
if y1>y2:
    y1,y2 = y2,y1
if x3>x4:
    x3,x4 = x4,x3
if y3>y4:
    y3,y4 = y4,y3
temp_x1 = max(x1,x3)
temp_x2 = min(x2,x4)
temp_y1 = max(y1,y3)
temp_y2 = min(y2,y4)
if temp_x2 - temp_x1 <0 or temp_y2 - temp_y1 <0:
    res = 0
else:
    res = (temp_y2-temp_y1)*(temp_x2-temp_x1)
print('{:.2f}'.format(res))

说起矩形的面积,一开始肯定会想到两个脚相交的图像,然后求这个小矩形的面积,例如第一个矩形的对角线点(1,1,3,3)第二个是(2,2,4,4),那么中间的小矩形的坐标就位(2,2,3,3),怎么得来的呢?就是第一矩形左下角的x与第二个矩形左下角x比较,谁大就是谁,y也是一样这样一波操作就可以得到中间的矩形。

那么如果给的不是左下角的怎么办?因为是矩形,所以有一个定义,矩形的四个点最小的点在左下角,最大的在右上角,根据这个我们可以比较给定的两个点,将x1,x2比较那个小就放前面,y同理,这样我们就得到了前面为左下角的点后面为右上角的点。

上面的操作都是根据相交来的,那么如果不相交呢?如果不想交我们可以根据第一个关系得出实际上的相交矩形左下角的点在实际上右上角点的右边,也就是一般情况下右上角的点减去左下角的点为正,现在变成了负。

五、最小公倍数

给定几个数,寻找这里面最大的数,然后将这个数循环迭代加一,知道他能被所有数取余为0为止

a,b,c = map(int,input().split())
def minbeishu(a,b,c):
    d = max(a,b,c)
    while True:
    	if d%a==0 and d%b==0 and d%c == 0:
    		return d
    	else:
    		d+=1
d = minbeishu(a,b,c)
print(d)

六、最大公约数

如果是两个值的话可以使用math的gcd方法,如果是多个值,可以先找道最小值,然后从0到xin进行-1的迭代,所有值都能被这个数取余就是最大公约数

# 法1
import math
a,b = map(int,input().split())
d = math.gcd(a,b)
print(d)
# 法2
a,b = map(int,input().split())
def find_max(a,b):
	c = min(a,b)
	for i in range(c,1,-1):
		if a%i ==0 and b%i==0:
			return i
c = find_max(a,b)
print(c)

七、n的阶乘

第一种方法,直接调用math函数的factorial函数,

第二种直接循环相乘

第三种,利用列表,将一个个数存放进列表里面

# 第一种
import math
a = int(input())
print(math.factorial(a))
# 第二种
a = int(input())
n = 1
for i in range(1,n+1):
	n*=i
print(n)
# 第三种
n = int(input())
def n_n(n):
    a = [1]
    for i in range(1,n+1):
        a = [x*i for x in a]
        while 1: 
            for z in range(len(a)):
                if a[z] >10:
                    try:
                        a[z+1] += a[z]//10
                    except:
                        a.append(a[z]//10)
                    a[z] %= 10
            b=0
            for z in range(len(a)):
                if a[z] > 10:
                    b+=1
            if b == 0:
                break
    return a
a = n_n(n)[::-1]
a = list(map(str,a))
a = ''.join(a)
print(a)

八、列表的有序排序

如果想要获得一个山型数组,首先的思路是利用列表的切片与翻转,然后就是找何处开始切,这个毋庸置疑肯定是最大点,这样才能形成一个先上后下的一个列表。那么还有一个问题在翻转,将一个列表进行翻转是没有返回值的,那么就不能使用赋值号去接受这个翻转后的列表,你就得重新copy一个新的列表在这个列表上进行翻转,为什么要copy因为python的内存机制使得当用赋值号去赋值列表的时候,他们两个指针是同时指向同一块内存的,这样当你操作第一个指针时,第二个指针通用发生改变。

典型例题:有效的山脉数组

class Solution:
    def validMountainArray(self, A: List[int]) -> bool:
        if len(A) < 3:
            return False
        else:
            if A[0]>=A[1] or A[-2]<=A[-1]:
                return False
            else:
                a_max = max(A)
                index = A.index(a_max)
                first = A[:index]
                second = A[index:]
                first.sort()
                second.sort(reverse=True)
                if A == first+second:
                    if len(second) != len(set(second)) or len(first) != len(set(first)):
                        return False
                    return True
            return False

上面这个代码,是Leecode的题目,首先如果整个列表长度小于三,那整个列表肯定是不可能形成山脉状的,因此可以直接返回,下面如果的if是确保开头两个数和结尾两个数符合山脉数组的类型,可以减少运算,下面的else是整个核心,先找到最大的值,然后用index去查看这个最大值在列表中的位置,利用这个索引位置将整个列表分为前和后两部分,将前面进行正序排列,后面进行反向排列,再将这两个数组拼接起来,当然这时在这两个切片内部可能存在相同的值,可以利用列表能与数组相互转化的性质又因为数组可以去重,因此可以看变换前后的长度来查看是否存在相同的值,当前面这些都符合了,那整个数组就是山型数组。

发布了106 篇原创文章 · 获赞 21 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/jiangSummer/article/details/104636430