没有学习过数据结构算法之类专业毕业的,因为特地学习了下,收货挺多,记录下~
我们编写计算机程序的目的是解决我们实际的应用问题
首先
计算机科学研究的是什么
计算机科学不仅仅是对计算机的研究
计算机科学主要研究的是问题、问题解决过程以及问题的解决方案
为了更好地处理机器相关性或独立性,引入了抽象的概念
那么什么是抽象呢,我们举个例子
比如说汽车,从司机观点来看,汽车是一台可以带人去往目的地的代步工具
从抽象角度说,司机看到汽车的逻辑层次
这些操作机构(方向盘、油门、挡位)就称为“接口Interface”
抽象我们可以通过编程实现
而编程是通过一种程序设计语言,将抽象的算法实现为计算机可以执行的代码的过程,没有算法,编程无从谈起
图灵奖获得者Niklaus Wirth的著名公式:算法+数据结构=程序
程序设计语言需要为算法的实现提供实现过程和数据的机制,具体表现为控制结构和数据类型
程序设计语言均有语句对应控制结构,比如说顺序处理、分支选择、循环迭代
程序设计语言也提供最基本的数据类型来表示数据,比如整数、字符等等,对于复杂问题而言,直接使用这些基本数据类型不利于算法的表达
因此我们引发出来一种抽象数据类型ADT(Abstract Data Type),ADT是对数据进行处理的一种逻辑描述,并不涉及如何实现这些处理;并且它建立了一种对数据的封装,封装技术将可能的处理实现细节隐蔽起来,能有效的控制算法的复杂度
刚提到了接口,我们也可以理解为接口的两端就是抽象与实现
我们为什么要学习和研究算法呢
什么是算法分析
何为计算资源?
一个算法所实施的操作数量或步骤数可作为独立于具体程序/机器的度量指标
赋值语句是一个合适的选择
问题规模影响算法执行时间
T(n) = 1+n
当n增大时,常数1在最终结果中显得越来越无足轻重,所以可以去掉1,保留n作为主要部分,运行时间的数量级就是O(n)
再比如 T(n)=5n^2+27n+1005
当n很小时,常熟1005起决定性作用
但当n越来越大,n^2项就越来越重要了,其他两项对结果的影响越来越小
同样,n^2项中的系数5,对于其增长速度来说也影响不大
所以可以在数量级中去掉27n+1005,以及系数5的部分,确定为O(n^2)
影响算法运行时间的其他因素:
常见的大O数量级函数
通常当n很小的时候,难以确定其数量级
当n增长到较大时,容易看出其主要变化量级,比如说对数、常数、线性、平方、立方、指数
其他算法复杂度表示法
大O表示法,表示了所有上限中最小的那个上限
大Ω表示法,表示了所有下限中最大的那个下限
大表示法,如果上下限相同,那么就可以用大
所谓”变位词“是指两个词之间存在组成字母的重新排列关系
如:heart和earth,python和typhon
为了简单起见,假设参与判断的两个词仅由小写字母构成,而且长度相等
解题目标:写一个bool函数,以两个词作为参数,返回这两个词是否变位词
解法一:逐字检查
问题规模:包含字符个数是n
且主要部分在于两重循环
所以总的执行次数是1+2+3+4+5…+n,所以数量级是0(n^2)
def anagramSolution1(s1, s2):
alist = list(s2)
pos1 = 0
stilloK = True
while pos1 < len(s1) and stilloK:
pos2 =0
found = False
while pos2 < len(alist) and not found:
if s1[pos1] == alist[pos2]:
found = True
else:
pos2=pos2+1
if found:
alist[pos2] = None
else:
stilloK = False
pos1= pos1+1
return stilloK
print(anagramSolution1('python','typhon'))
解法二
排序比较:将两个字符串都按照字母顺序排好序,再逐个字符对比是否相同,如果相同则是变位词,有任何的不同就不是变位词
def anagramSolution2(s1, s2):
alist1 = list(s1)
alist2 = list(s2)
#转为列表
alist1.sort()
alist2.sort()
pos = 0
#分别排序
matches = True
while pos < len(s1) and matches:
if alist1[pos] == alist2[pos]:
pos = pos+1
else:
matches = False
return matches
print(anagramSolution2('python','typhon'))
解法三
暴力法:穷尽一个所有可能的组合,将S1出现的字符进行全排列,再查看S2是否出现在全排列列表中,这里最大的困难是产生S1所有字符的全排列,对于越长的字符串,所需的时间和复杂度也越来越大,因此,暴力法恐怕不算是一个好的算法
解法四
计数比较:对比两个词中每个字符出现的次数,如果26个字母出现的次数都相同的话,这两个字符串就一定是变位词;
具体做法:为每个词设定一个26位的计数器,先检查每个词,在计数器中设定好每个字母出现的次数;计数完成后,进入比较阶段,看两个字符串的计数器是否相同,如果相同,则两个字符串是变位词
计数比较中有3个循环迭代,但不像解法1那样存在嵌套循环
前两个循环用于对字符串进行计数,操作次数等于字符长度n
第3个循环用于计数器比较,操作总次数是26次
所以总的操作次数T(n) = 2n+26,其数量级为O(n),这是一个线性数量级的算法,是4个变位词判断中性能最优的
def anagramSolution4(s1, s2):
C1= [0]* 26
C2= [0]* 26
for i in range(len(s1)):
pos = ord(s1[i])- ord('a')
C1[pos] = C1[pos] +1
for i in range(len(s2)):
pos = ord(s2[i]) - ord('a')
C2[pos] = C2[pos] + 1
stilloK = True
j = 0
while j < 26 and stilloK:
if C1[j] == C2[j]:
j = j+1
else:
stilloK = False
return stilloK
print(anagramSolution4('apple','pleap'))
值得注意的是,本算法依赖于两个长度为26的计数器列表,来保存字符计数,这相比前3个算法需要更多的存储空间,牺牲存储空间换取运行时间,或者相反,这种在时空之间的取舍和权衡,在选择问题解法的过程中经常会出现。