0,1背包问题:我写笔记风格就是想到哪里写哪里,有很多是旧的也没删除,代码内部可能有很多重复的东西,但是保证能运行出最后效果
'''学点高大上的遗传算法'''
'''首先是Np问题的定义:
npc:多项式复杂程度的非确定性问题,
首先是基本的0-1背包问题。 '''
'''给定N个物品和一个背包,物品i的质量是Wi,其价值位Vi,背包的容量为C,问应该
如何选择装入背包的物品,使得转入背包的物品的总价值为最大?
在选择物品的时候,对每种物品i只有两种选择,即装入背包或不装入背包。不能将
物品i装入多次,也不能只装入物品的一部分。因此,该问题被称为0-1背包问题。'''
'''如果穷举就有2^n个需要测试,很大'''
'''其实,0-1背包是DP的一个经典实例,可以用动态规划求解。
DP求解过程可以这样理解:对于前i件物品,背包剩余容量为j时,所取得的最大价值(此时称为状态3)只依赖于两个状态。
状态1:前i-1件物品,背包剩余容量为j。在该状态下,只要不选第i个物品,就可以转换到状态3。
状态2:前i-1件物品,背包剩余容量为j-w[i]。在该状态下,选第i个物品,也可以转换到状态3。
因为,这里要求最大价值,所以只要从状态1和状态2中选择最大价值较大的一个即可。'''
'''算法复杂度基本是O(N)'''
bag_weight=95800
stone=[]
#stone[i] means (weight, value)
stone.append((100,100))
stone.append((200,105))
stone.append((300.9,400))
stone.append((400.56,900.56))
stone.append((500,1000.598))
stone=sorted(stone) #default sorted by first column of stone ,very convinient
print (stone)
#creat a two-dim list,row i means using the first i stone
#column j means the bag_weight
'''只想到用递归来写,递推还不知道怎么弄'''
#网上说法是不能用递推来写,因为不知道重量的变化,变化太多,不知道要存多少才行.
#下面写出打印的最好的石头组合的index,现在是这个问题比较难!!!!!!!!!!!因为递归问题很难区分这次和上次
#来的index,反复访问很难弄,下面用函数的双返回值来处理这个问题,但是空间开销也是相当大.目测O(N^2)
zuhe=[0]*len(stone)
def value(num,tmpbag_weight):
if num==1:
if tmpbag_weight>=stone[0][0]:
return 100
else:
return 0
firstcase=0
if tmpbag_weight>=stone[num-1][0]:
firstcase=0
firstcase+=stone[num-1][1]
firstcase+=value(num-1,tmpbag_weight-stone[num-1][0])
secondcase=value(num-1,tmpbag_weight)
return max(firstcase,secondcase)
print (value(len(stone),bag_weight))
print (zuhe)
###########
print ('妈的我居然写出来了55555555')
def value(num,tmpbag_weight):
if num==1:
if tmpbag_weight>=stone[0][0]:
zuhe=[0]
return stone[0][1],zuhe
else:
return 0,[]
firstcase=0
if tmpbag_weight>=stone[num-1][0]:
firstcase=0
firstcase+=stone[num-1][1]
a,b=value(num-1,tmpbag_weight-stone[num-1][0])
firstcase+=a
c,d=value(num-1,tmpbag_weight)
secondcase=c
if firstcase>secondcase:
b.append(num-1)
return max(firstcase,secondcase),b
else:
return max(firstcase,secondcase),d
print (value(len(stone),bag_weight))
2.kmp
'''匹配字符串bf算法'''
def search(a,b):#if a in b :return first index else:return None
for i in range(len(b)):
#直接b切片就行了,记住几个常用计数长度更方便
#range[i,i+len(a)] 标示角标从i一直到i+len(a)-1一共len(a)个元素
#切片也类似b[i:i+len(a)]也表示len(a)个元素
if i+len(a)-1==len(b):
return None
if b[i:i+len(a)]==a:
return i
print (search('ffg','qfgsdfgsdffg'))
'''下面是KMP算法--看毛片算法'''
'''把复杂度从O(mn)下降到O(m+n)'''
#这个写的很详细,KMP还是要从例子的图形里面分析出来.注意next[0]=-1,是一种人为的规定,为了识别,你定义-2也一样.
'''http://www.cnblogs.com/yjiyjige/p/3263858.html'''
'''先学一下c语言里面的++'''
'''首先要说:a++ = a 和 ++a = a + 1 并不是两个合法的语句。这两个式子
是说 a++ 的值就是 a 的值,而 ++a 的值等于 a+1。 注意:我说的是++a和a++的值。
你的这两个表达式不合法!
在C语言中前置和后置自增运算符返回的结果是右值,不能放在等号左侧。
顺便提一下:在C++中,前置自增的结果是左值,可以放在等号左侧。
1.int main() {
int b=0;
int a=3;
++a=b;
a+=1;
return 0;
} 答案a是1
2.int main() {
int b=0;
int a=3;
b=++a;
a+=1;
return 0;
} 答案a是5
3.int main() {
int b=0;
int a=3;
a++=b;
a+=1;
return 0;
} 第四句话不合法,无法运行'''#c++什么鬼
#要先写获取next数组这个函数
#先用暴力方法实现next数组的求法.
'''理解:
待处理数组,也就是我们要配出的字符串,比如一个长串里面找一个短的串,那么待处理数组就是这个短的设为p
'''
'''p=abcaab 那么我们的next数组就是-1,0,0,0,1,1
next[i]的招法是符合下面条件的最大的index
1.next[i]<i
2.设next[i]=k,有p[:k]=p[i-k:i]'''
'''http://www.cnblogs.com/tangzhengyue/p/4315393.html
这个地址里面有一个长的面试题:说的是给一个字符串求他的next数组,下面我来尝试这个next方法
上面写的对不对.
他给的P:
abcaacbbcbadaabcacbd
答案:-10001100000101123400他给的答案做进一步优化了,不止考虑上面3个条件,
也考虑了当前位置,所以把一些项优化到-1了'''
def next_rough(a):
shuchu=[]
shuchu.append(-1)
for i in range(1,len(a)):
for j in range(i-1,-1,-1):
#这个j循环测试看哪个是k
if a[:j]==a[i-j:i]:
shuchu.append(j)
break
return shuchu
print (next_rough('abcaacbbcbadaabcacbd'))
#成功得到了上面的next数组
#显然这个方法太慢了,应该是O(N^2)的.当然当N非常小的时候还是很快的.
#正确的方法有点复杂的,利用的是动态规划来处理这个next数组
#先用next数组进行最后的kmp出结果把.
def kmp(a,b):
i=0
j=0
nextshuzu=next_rough(a)
while 1:
if j==len(a):
return i-len(a) #开始index
if i>len(b)-1:
return None
if b[i]==a[j]:
i+=1
j+=1
else:
if nextshuzu[j]==-1:
i+=1
j=0
else:
j=nextshuzu[j]
print (kmp('43aa','3ht54543aa43a4a6'))
print (kmp('1','23'))
#效果还不错.
#最后应该用动态规划来写next数组了.
#http://www.cnblogs.com/yjiyjige/p/3263858.html里面写的真的很详细.
#像这种情况,如果你从代码上看应该是这一句:k = next[k];为什么是这样子?你看下面应该就明白了。
#上面这句话在博客里面的说没说完,其实k=next[k]这个吊代码,把2次匹配next给用一个代码写明白了,确实叼.
#下面只需要改成python实现即可.
def next_true(a):
shuchu=[]
shuchu.append(-1)
j=0
k=-1
while (j<len(a)-1):
if (k==-1 or a[j]==a[k]):
j+=1
k+=1
shuchu.append(k)
else:
k=shuchu[k]
return shuchu
print (next_true('abcaacbbcbadaabcacbd'))
#从结果看完美解决了.
3.遗传算法解背包问题,一写一个晚上没了,干了200行,我理解是这东西就是个类似随机查找的东西,本质很low,但是实用性强,有点像神经网落,普世通用性强.描述性强
缺陷当然是慢!!写写还挺有意思,复习了很多的random函数的使用.这东西不推荐手写大型的,因为有库直接能调用遗传算法.当然现在还有遗传加神经网络的综合体,有点
强.
'''学点高大上的遗传算法'''
import random
print (random.randint(10, 11))#python 的这个随机正数是包含收尾的,这点要注意.很特俗
'''首先是Np问题的定义:
npc:多项式复杂程度的非确定性问题,
首先是基本的0-1背包问题。 '''
'''给定N个物品和一个背包,物品i的质量是Wi,其价值位Vi,背包的容量为C,问应该
如何选择装入背包的物品,使得转入背包的物品的总价值为最大?
在选择物品的时候,对每种物品i只有两种选择,即装入背包或不装入背包。不能将
物品i装入多次,也不能只装入物品的一部分。因此,该问题被称为0-1背包问题。'''
'''如果穷举就有2^n个需要测试,很大'''
'''其实,0-1背包是DP的一个经典实例,可以用动态规划求解。
DP求解过程可以这样理解:对于前i件物品,背包剩余容量为j时,所取得的最大价值(此时称为状态3)只依赖于两个状态。
状态1:前i-1件物品,背包剩余容量为j。在该状态下,只要不选第i个物品,就可以转换到状态3。
状态2:前i-1件物品,背包剩余容量为j-w[i]。在该状态下,选第i个物品,也可以转换到状态3。
因为,这里要求最大价值,所以只要从状态1和状态2中选择最大价值较大的一个即可。'''
'''算法复杂度基本是O(N)'''
bag_weight=1200
stone=[]
stone.append((100,100))
stone.append((200,105))
stone.append((300,400))
stone.append((400,900))
stone.append((500,1000))
# 举个例子,使用遗传算法解决“0-1背包问题”的思路:0-1背包的解可以编码为一串0-1
#字符串(0:不取,1:取) ;首先,随机产生M个0-1字符串,然后评价这些0-1字
#符串作为0-1背包问题的解的优劣;然后,随机选择一些字符串通过交叉、突变等操作产生下
#一代的M个字符串,而且#较优的解被选中的概率要比较高。这样经过G代的进化后就可能会产生
#出0-1背包问题的一个“近似最优解”。
#遗传算法的问题
'''1.编码:需要将问题的解编码成字符串的形式才能使用遗传算法。最简单的一种编码方式是二进制编码
,即将问题的解编码成二进制位数组的形式。例如,问题的解是整数,
那么可以将其编码成二进制位数组的形式。将0-1字符串作为0-1背包问题的解就属于二进制编码。
2.遗传算法有3个最基本的操作:选择,交叉,变异。
下面处理第一个选择问题:
1.问题:有n个物体,他们被选择的概率是p1,...pn.和为1.
求一个函数每次根据这个概率随机出一个物体.
这个用random(0,1)函数随便写很简单--他们管这个叫轮盘赌算法下面我定义他是rws(Roulette
Wheel Selection )函数.
第二个问题交叉:
http://www.cnblogs.com/heaad/archive/2010/12/23/1914725.html
这里面只写了原理没有实现,最后用一个库包来实现的,我打算还是自己手写一次
根据他里面的思想,手动实现遗传算法.
原理写下来:
交叉(Crossover):2条染色体交换部分基因,来构造下一代的2条新的染色体。例如:
变异(Mutation):在繁殖过程,新产生的染色体中的基因会以一定的概率出错,称为
变异。变异发生的概率记为Pm 。例如:
'''
import random
a=[0.1,0.2,0.3,0.4]#物体被选择的概率,这个概率就是这个物体的效果函数
def rws(a):
#生成累加概率
b=random.random()
for i in range(len(a)):
j=(sum(a[:i+1]))
if b<j:
return i #return the index of selection one in a
#效果还可以
import numpy as np
def crossover_and_mutation(a,b,mutation_rate):#代表一次进化,a,b都是二进制的数
#我认为把a,b都转化成2进制然后转化为字符串就容易变化多了.所以初始化的时候就弄成2进制的
#直接字符串操作,很方便.
#生成2个需要变化的开始index和结尾index
kaishi=random.randint(0,len(stone))
jieshu=random.randint(0,len(stone))
start=min(kaishi,jieshu)
end=max(kaishi,jieshu)
#开始交换
a=a[:start]+b[start:end+1]+a[end+1:]#切片的角标非常贴心,如果角标超了自动返回空list.
b=b[:start]+a[start:end+1]+b[end+1:]
#继续开始突变mutation
#生成一个突变的index,每一次突变基因的个数=mutation_rate*len(stone)
## np.random.randint(low[, high, size]) 返回随机的整数,位于半开区间 [low, high)。
#下面这行就是闭区间的随机取一个
#再加一行,让mutation_rate变动来模拟是否突变:
mutation_rate=random.random()*mutation_rate
mutation_index=np.random.random_integers(0,len(stone)-1,int(mutation_rate*len(stone)))
mutation_index=set(mutation_index)
#开始突变:
after_mut_a=''
for i in range(len(a)):
if i in mutation_index and a[i]=='1':
after_mut_a+='0'
continue #这地方要加入continue
if i in mutation_index and a[i]=='0':
after_mut_a+='1'
continue
else:
after_mut_a+=a[i]
#b完全类似
mutation_index=np.random.random_integers(0,len(stone)-1,int(mutation_rate*len(stone)))
#开始突变:
after_mut_b=''
for i in range(len(b)):
if i in mutation_index and b[i]=='1':
after_mut_b+='0'
continue
if i in mutation_index and b[i]=='0':
after_mut_b+='1'
continue
else:
after_mut_b+=b[i]
return after_mut_a,after_mut_b
print (crossover_and_mutation('01001','01101',0.5))
print ('上面是一次进化的结果')
c=2**len(stone)-1
print (c)
def init(n):#初始化n这么多个生物
creature=[]
for i in range(n):
t=random.randint(0, c)
t=bin(t)
t=t[2:]
q=len(stone)-len(t)
t='0'*q+t
creature.append(t)
return creature
#下面写一个二进制的价值函数,也就是这个二进制是否符合背包的规则,和他的价值
def value_of_pick(a):
weight=0
for i in range(len(a)):
weight+=int(a[i])*stone[i][0]
value=0
for i in range(len(a)):
value+=int(a[i])*stone[i][1]
if weight>bag_weight:
value/=4
return value
def all_value(a_all):
b=[]
for i in range(len(a_all)):
b.append(value_of_pick(a_all[i]))
return b
#下面开始跑遗传
a_all=init(10)
all_the_value=all_value(a_all)
he=sum(all_the_value)
for i in range(len(all_the_value)):
all_the_value[i]/=he
#上步是归一化
#先选2个,然后进化
for i in range(50):#进化50次
first=rws(all_the_value)
second=rws(all_the_value)
#这里假设自己和自己也能繁殖..
a,b=crossover_and_mutation(a_all[first],a_all[second],0.3)
a_all.append(a)
a_all.append(b)
all_the_value=all_value(a_all)
he=sum(all_the_value)
for i in range(len(all_the_value)):
all_the_value[i]/=he
jieguo=(all_value(a_all))
print (max(jieguo))
#下面我们找到这个答案的具体的pick方法.
index_final=np.argmax(np.array(jieguo))
print (a_all[index_final])
print ('总结取法',end=''),print(a_all[index_final],end=''),print('价值',end=''),print(max(jieguo))
4.hash表数据结构的实现
'''Hash表'''
'''核心就是hash函数的设计:
特点:在构造Hash函数时应尽量考虑关键字的分布特点来设计函数
使得Hash地址随机均匀地分布在整个地址空间当中。
1)直接定址法
取关键字或者关键字的某个线性函数为Hash地址,即address(key)=a*key+b;如
知道学生的学号从2NoneNoneNone开始,最大为4NoneNoneNone,则可以将address(key)=key-2NoneNoneNone作为Hash地址。
2)平方取中法
对关键字进行平方运算,然后取结果的中间几位作为Hash地址。假如有以下关
键字序列{421,423,436},平方之后的结果为{177241,178929,19NoneNone96},那么可以取{72,89,NoneNone}
作为Hash地址。
3)折叠法
将关键字拆分成几部分,然后将这几部分组合在一起,以特定的方式进行转化形
成Hash地址。假如知道图书的ISBN号为89None3-241-23,可以将address(key)=89+None3+24+12+3作为Hash地址。
4)除留取余法
如果知道Hash表的最大长度为m,可以取不大于m的最大质数p,然后对关键字进
行取余运算,address(key)=key%p。
在这里p的选取非常关键,p选择的好的话,能够最大程度地减少冲突,p一般取不
大于m的最大质数。'''
'''5.Hash表的优缺点
Hash表存在的优点显而易见,能够在常数级的时间复杂度上进行查找,并且插入数据
和删除数据比较容易。但是它也有某些缺点,比如不支持排序,一般比用线性表存储需要更
多的空间,并且记录的关键字不能重复。'''
a=[1,2,3,4,51,2,3,4,51,2,3,4,551,2,3,4,551,2,3,4,551,2,3,4,551,2,3,4,551,2,3,4,5]
class hashnode():
def __init__(self,data):
self.data=data;
self.next=None
#下面写的是取mod的方法:一般除数取比len(表)小的最大素数。
#获取除数
import math
b=len(a)
if b==1:
c=1
if b==2:
c=2
if b==3:
c=2
if b>3:
def sushu(b):
for i in range(b-1,2,-1):
k=None
for j in range(2,int(math.sqrt(i))+2):
if i%j==None:
break
if j==int(math.sqrt(i))+1:
k=1
if k==1:
return i
c=sushu(b)#除数就是c
#下面建立hash 表
hashlist=[None]*c
print (c)
for i in range(len(a)):
hashtmp=a[i]%c
if hashlist[hashtmp]==None:
hashlist[hashtmp]=hashnode(a[i])
else:
head=hashlist[hashtmp]
while 1:
if head.next!=None:
head=head.next
continue
else:
head.next=hashnode(a[i])
break
print (hashlist[2].next.next.data)
print (66666666)
#然后添加元素。直接复制上面的部分代码即可。都类似的。
def add(one):
hashtmp=one%c
if hashlist[hashtmp]==None:
hashlist[hashtmp]=hashnode(one)
else:
head=hashlist[hashtmp]
while 1:
if head.next!=None:
head=head.next
continue
else:
head.next=hashnode(one)
break
add(11)
print (hashlist[0])
def delme(two):
hashtmp=two%c
if hashlist[hashtmp]==None:
pass
else:
head=hashlist[hashtmp]
while 1:
if head.data==two:
head.data=None
if head.next!=None:
head=head.next
continue
if head.next==None:
break
delme(11)
print (hashlist[0])
#下面写查询,这个写的是全部查询,找到所有等于a的hashlist里面的元素的个数
def search(a):
hashtmp=a%c
b=0
head=hashlist[hashtmp]
while 1:
if head.data==a:
b+=1
if head.next!=None:
head=head.next
continue
if head.next==None:
break
return b
print (search(2))
#曾,删,改,查,建立,都有了
#下面写个修改。这个修改貌似很多数据结构不写这个方法。
def change(two,three):
hashtmp=two%c
if hashlist[hashtmp]==None:
pass
else:
head=hashlist[hashtmp]
while 1:
if head.data==two:
head.data=three
if head.next!=None:
head=head.next
continue
if head.next==None:
break
change(2,3)
#因为把所有的2都改成了3所以下面的搜索会变成0
print (search(2))
print (search(3)) #其实这个改有很大问题,因为他没有改变hash的东西,是否需要重新建立改完东西的hash索引,这个
#是需要讨论的,但是本质都不难。这里面写的change只是把原来放2的地方数据都改成了3,。所以你找3还是找不到新修改
#的那8个3,还是最开始的那8个3.ps:出事数据里面是8个2 ,8个3的。
5.位图法的实现
##位图大发好神奇
''' 1、给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何
快速判断这个数是否在那40亿个数当中
首先,将这40亿个数字存储到bitmap中,然后对于给出的数,判断是否
在bitmap中即可。
位图和位向量
所谓位向量法,其实就是使用一串二进制串来表示一组数字集合的方法。
比如,我们知道一个byte = 8bit,表示成二进制串就是:00000000.这里我们就得到
了八个可用的符号位,假如我们作如下规定
1)符号位为1的,说明存在于我们的数字集合中
2)符号位为0的,说明不在我们的数字集合中
那么,二进制串01010101,表示的数字集合为{1,3,5,7} (注意,计算机通常都是从0
开始计算的)
。。
我们知道位图的数据结构就是一个数组,而位图的操作(算法)基本依赖于下面3个元操作
set_bit(char x, int n); //将x的第n位置1,可以通过x |= (1 << n)来实现
clr_bit(char x, int n); //将x的第n位清0,可以通过x &= ~(1 << n)来实现
get_bit(char x, int n); //取出x的第n位的值,可以通过(x >> n) & 1来实现
'''
#下面用python手动实现。python所有的数本质都是2进制,可以直接用位运算。
#举一个例子,一个集合里面有数字1,3,5 那么我们用二进制010101 来表示。也就是第一个0表示没有数字0,然后1表示
#有数字1,0表示没有2, 1表示有数字3, 下一位0又表示没有数字4,最后一个1表示有数字5.
#这样用一个数字就表示了函数数字1,3,5这个信息了。问题是python的二进制是从右边开始算的。所以我们再反转一下
#用101010来表示1,3,5存在这个信息就行了。
b=[1,3,4,5,19,8]
def init(a):#首先建立第一个数字存在的信息
return 1<<a
chushihua=init(b[0])
for i in range(len(b)):
if i>=1:
chushihua=chushihua |(1<<b[i])
print (bin(chushihua))#效果不错
#下面是查询一个数字是否在chushihua里面
def search(chushihua,a):
return (chushihua>>a) &1
print (search(chushihua,8))
print (search(chushihua,19))
print (search(chushihua,9))
print (search(chushihua,20))
#成功解决这个问题。
#还有一个删除函数。比如我有一个操作把所有等于8的数从集合中删除。下面来给他写出来
#思路也不难,比如删除8:我就做一个011111111,然后跟chushihua取交就完了。这地方有那么一点点的复杂。trick
def delme(chushihua,a):
chushihua=(chushihua &(~(1<<a)))
return chushihua
chushihua=delme(chushihua,8)
#删除8之后找8
print('删除8之后找8')
print (bin(chushihua))
print (search(chushihua,8))
#下面没事闲的测试一下,利用random
import numpy as np #这模块有int32限制。。python 这个东西有问题,太大的数会出现错误。
b=np.random.random_integers(0,1000,100)#先弄10万个大数。
b=[50,3,4,324,32,4,32,4,324,324,32,4,32,4,32,432,4,3,4,3,4,3,4,3,43,4,3,43,4,3,4,3,3,3,3,3,3,3,3,3,3,34,4,4,4,4,4,4,4,5,3,4,324,32,4,32,4,324,324,32,4,32,4,32,432,4,3,4,3,4,3,4,3,43,4,3,43,4,3,4,3,3,3,3,3,3,3,3,3,3,34,4,4,4,4,4,4,4,5,3,4,324,32,4,32,4,324,324,32,4,32,4,32,432,4,3,4,3,4,3,4,3,43,4,3,43,4,3,4,3,3,3,3,3,3,3,3,3,3,34,4,4,4,4,4,4,4,5,3,4,324,32,4,32,4,324,324,32,4,32,4,32,432,4,3,4,3,4,3,4,3,43,4,3,43,4,3,4,3,3,3,3,3,3,3,3,3,3,34,4,4,4,4,4,4,4,5,3,4,324,32,4,32,4,324,324,32,4,32,4,32,432,4,3,4,3,4,3,4,3,43,4,3,43,4,3,4,3,3,3,3,3,3,3,3,3,3,34,4,4,4,4,4,4,4]
b=b*99900
#下面就是上面的代码copy
def init(a):#首先建立第一个数字存在的信息
return 1<<a
chushihua=init(b[0])
for i in range(len(b)):
if i>=1:
chushihua=chushihua |(1<<b[i])
# python 这个东西有问题,太大的数会出现错误。!!!!!!!
#这地方不太清楚,会不会是数字个数太多,导致误差,然后random时候会bug
#后面我改用自己创造的数据然后*99900,发现效果很好,这个bug先放着问问别人。
#下面是查询一个数字是否在chushihua里面
print ('先用自带的in测试一下')
print (15 in b)
print ('再用我的函数测试一下')
print (search(chushihua,15))
print (search(chushihua,19))
print (search(chushihua,9))
print (search(chushihua,20))
#成功解决这个问题。
#还有一个删除函数。比如我有一个操作把所有等于8的数从集合中删除。下面来给他写出来
#思路也不难,比如删除8:我就做一个011111111,然后跟chushihua取交就完了。这地方有那么一点点的复杂。trick
def delme(chushihua,a):
chushihua=(chushihua &(~(1<<a)))
return chushihua
chushihua=delme(chushihua,8)
#最后很明显的看出来,我写的search比自带的int快99999999呗哈哈
6.最长公共子序列问题
'''最长公共子序列:显然动态规划'''
'''经过画图分析,显然是2阶动态规划问题'''
'''设f(a,b,i,j)表示a从0到i的子串,和b从0到j的子串这2个子串的最长公共序列的长度'''
'''那么f(a,b,i,j)是从f(a,b,i-1,j),f(a,b,i,j-1),f(a,b,i-1,j-1)里面开发出来的'''#具体直接写代码。
def f(a,b,i,j):
if i==0:
if a[0] in b:
return 1
else:
return 0
if j==0:
if b[0] in a:
return 1
else:
return 0
case1=case2=case3=0
if a[i]==b[j]:
case1=1+f(a,b,i-1,j-1)
if a[i]!=b[j]:
case2=f(a,b,i-1,j)
case3=f(a,b,i,j-1)
return max(case1,case2,case3)
a='343242dsfd'
b='dsfds'
print (f(a,b,len(a)-1,len(b)-1))
#上面很简单,3分钟搞定。男的是要把这个解解出来,而不仅仅是输出一个数字。
#这个跟背包问题很像,递归里面开数组。不是太好写。# 下面开始尝试。keyword:多返回值函数
def f(a,b,i,j):
if i==0:
if a[0] in b:
return 1,a[0]
else:
return 0,''
if j==0:
if b[0] in a:
return 1,b[0]
else:
return 0,''
case1=case2=case3=0
if a[i]==b[j]:
case1=1+f(a,b,i-1,j-1)[0]
case1_data=f(a,b,i-1,j-1)[1]+a[i]
if a[i]!=b[j]:
case2=f(a,b,i-1,j)[0]
case2_data=f(a,b,i-1,j)[1]
case3=f(a,b,i,j-1)[0]
case3_data=f(a,b,i,j-1)[1]#如果有相同长度的解,应该返回最前面的。那么case2和case3如果相同呢????????
#我认为这个是一个相当男的bug了,弄点诡异数据来测一测。
#然而经过我长时间的分析,这个bug其实是没有的,具体很复杂,不容易说明白,
#结论我先说一下,就是这个代码
if case1>case2 and case1>case3:
return case1,case1_data
if case2>=case1 and case2>=case3: #这个代码case2》=case3为什么这么写可以,也就是说如果相等,我还是
#只需要返回case2即可,舍弃case3.我尝试一下说明白这个取舍问题。
#道理就是你的这种取舍,考虑的前提就是case2里面最后一个元素被使用到了,然而
#你如果发生bug那么就必然发生这种情况,下面再让i+1,j+1后,如果用case3里面的情况
#会添加一个元素得到的长度比case2里面的长。但是这是不可能的,原因就是上一步
#已经保证了case2里面最后一个元素使用过了。总之很难说清楚的东西。
return case2,case2_data
if case3>=case1 and case3>=case2:
return case3,case3_data
a='babax'
b='bbaxy'
print (f(a,b,len(a)-1,len(b)-1))
7.最长公共子串
'''最长公共子串'''
'''也是动态规划'''
def com(a,b,i,j): #得到a,b的公共子串并且子串以a[i]和b[j]作为结尾的长度,
#然后把他们保存起来用下面的get函数,然后我们再取这个矩阵最大的就行了
if i==0:
if b[j]==a[i]:
return 1
else:
return 0
if j==0:
if a[i]==b[j]:
return 1
else:
return 0
if a[i]==b[j]:
return com(a,b,i-1,j-1)+1
else:
return 0
def get(a,b):
save=[]
for i in range(len(a)):
for j in range(len(b)):
save.append(com(a,b,i,j))
return max(save)
print (get('aa3232','aaa783232'))
#效果还不错。
#下面还是继续写,把这个最长子串的组合写出来。
def com(a,b,i,j): #得到a,b的公共子串并且子串以a[i]和b[j]作为结尾的长度,
#然后把他们保存起来用下面的get函数,然后我们再取这个矩阵最大的就行了
if i==0:
if b[j]==a[i]:
return 1,a[i]
else:
return 0,''
if j==0:
if a[i]==b[j]:
return 1,a[i]
else:
return 0,''
if a[i]==b[j]:
return com(a,b,i-1,j-1)[0]+1,com(a,b,i-1,j-1)[1]+a[i]
else:
return 0,''
def get(a,b):
save=[]
for i in range(len(a)):
for j in range(len(b)):
save.append(com(a,b,i,j))
return max(save)
print (get('aa3232','aaa783232'))
#一样轻松写出。
8.全域散列法,随机hash的思想
'''全域散列,随机hash技术'''
'''全域散列法在执行的开始时,就从一组精心设计的函数总,随机的选择一个作为散列函数,
随机化保证了没有哪一个输入会导致最坏性能情况的发生.'''
'''构造这个函数族很复杂,也不是非常复杂:
原范围是p,分类后的曹数是m,也就是把p范围内的数都分布到m个曹当中
h(k)_(a,b)=((ak+b)modp)modm
那么因为p,m去定.a跑遍1到p-1,b跑遍1到p就构造了p(p-1)这么多个函数.他的性质是Pr(h(k)_(a,b)=h(k)_(a,b)).
'''
9.二叉搜索树:
#coding:utf-8
#首先是二叉搜索树的定义:
'''若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
若它的右子树不空,则右子树上所有结点的
值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。'''
'''二叉搜索树''''''二叉搜索树里面的元素都是不重复的
所以当插入一个元素时候先搜索他是否在树里面,如果在就不插入了,
不果不在就插入'''
class node():
def __init__(self,data):
self.left=None
self.right=None
self.data=data
#不能把类写在另一个类里面
class ercha():
def __init__(self):
self.head=node(None)
def input(self,a):
old_head=self.head
while 1:
if self.head.data==None:
self.head.data=a
self.head=old_head
return
if self.chazhao(a)==None:
if a<self.head.data and self.head.left==None:
self.head.left=node(a)
self.head=old_head
return
if a<self.head.data and self.head.left!=None:
self.head=self.head.left
continue
if a>self.head.data and self.head.right==None:
self.head.right=node(a)
self.head=old_head
return
if a>self.head.data and self.head.right!=None:
self.head=self.head.right
continue
def chazhao(self,a):
old_head=self.head
while 1:
if self.head==None:
self.head=old_head
return None
if a==self.head.data:
new_head=self.head
self.head=old_head
return new_head
if a>self.head.data:
self.head=self.head.right
continue
if a<self.head.data:
self.head=self.head.left
def delet(self,a):
if self.chazhao(a)==None:
print ('错误')
return None
else:
if self.chazhao(a).left==None and self.chazhao(a).right==None:
t=self.chazhao(a)
# t=None这里面写这个不好使.对象不能直接赋值?
t.data=None
if self.chazhao(a).left==None and self.chazhao(a).right!=None:
pass
def listhua(self,a):#把root=a的子树,中序遍历的结果都转化成list来查询
#这个递归代码不能实际使用,因为速度O(N)了,跟需要logn矛盾.
b=[]
if a==None:
return b
if a!=None:
if a.left==None and a.right==None:
return [a]
else:
a1=self.listhua(a.left)
a2=[a]
a3=self.listhua(a.right)
if a1==None:
a1=[]
if a3==None:
a3=[]
return a1+a2+a3
#删除还是没写明白
shu=ercha()
shu.input(3)
shu.input(2)
shu.input(6)
shu.input(9)
listhuazhihou=shu.listhua(shu.chazhao(3))
print (listhuazhihou[0].data)
#自己写的不对,还是贴个别人的吧
class TreeNode:
def __init__(self,val):
self.val=val;
self.left=None;
self.right=None;
def insert(root,val):
if root is None:
root=TreeNode(val);
else:
if val<root.val:
root.left=insert(root.left,val); #递归地插入元素
elif val>root.val:
root.right=insert(root.right,val);
return root;
def query(root,val):
if root is None:
return ;
if root.val is val:
return 1;
if root.val <val:
return query(root.right,val); #递归地查询
else:
return query(root.left,val);
def findmin(root):
if root.left:
return findmin(root.left);
else:
return root;
def delnum(root,val):
if root is None:
return ;
if val<root.val:
return delnum(root.left,val);
elif val>root.val:
return delnum(root.right,val);
else: # 删除要区分左右孩子是否为空的情况
if(root.left and root.right):
tmp=finmin(root.right); #找到后继结点
root.val=tmp.val;
root.right=delnum(root.right,val); #实际删除的是这个后继结点
else:
if root.left is None:
root=root.right;
elif root.right is None:
root=root.left;
return root;
#測试代码
root=TreeNode(3);
root=insert(root,2);
root=insert(root,1);
root=insert(root,4);
#print query(root,3);
print (query(root,1))
root=delnum(root,1)
print (query(root,1))