1-数据结构和算法-概述(线性:数组,栈,队列,链表,非线性:树,图)(算法是解决问题的思路或逻辑)
为什么学习算法?
数据结构和算法
没有这个数据结构和算法,你也能写出代码,
但是会有问题,
1,你写出的代码可能是执行效率是比较低的,可是对程序运行的效率和开销没有意识,性能低下
2,面对新的问题,你可能无从下手,不知道怎么入手解决这个问题,
3,还有你用到第三方的工具,你可能看不懂,更不用说去优化,二次开发,
数据结构和算法是伴随着你的程序员历程的,是一个持久性的东西,不是一天两天就精通的,需要不断学习,
现在只是3天左右一个入门,之后还需要去不断学习历练数据结构和算法的功力,这是你的内功,
进入大的公司,他们不对你的技术和项目产品感兴趣,看重的是你的基本功,也就是数据结构和算法,
他可以考你各种各样的问题,锻炼你的思维能力,锻炼你的学习能力,以及解决问题的能力,
算法引入
案例:
如果 a+b+c=1000,且 a2+b2=c^2(a,b,c 为自然数),如何求出所有a、b、c可能的组合?
可以使用枚举法
a=0
b=0
c=0-1000
变动一个数,比如c,从0-1000,其他的两个数不动,然后变动b,其他两个数不动,变动a,其他两个数不动,这是最笨的方法,进行穷举,
import time # 计算一下程序使用了多长时间,
start_time = time.time()
for a in range(0,1001):
for b in range(0, 1001):
for c in range(0, 1001):
if a+b+c==1000 and a**2+b**2==c**2:
print("a,b,c:%d,%d,%d"%(a,b,c))
end_time = time.time()
print("times:%d"%(end_time-start_time))
上面的问题我们通过枚举法解出来了,这就是算法,也就是计算的方法,
算法的具体定义:算法就是独立存在的一种解决问题的方法和思想, 算法就是解决问题的方法,
是独立存在的和计算机语言没有关系,重要的是思想,
算法的5个特征:
1,输入,算法可以有0个或者多个输入
2,输出,算法至少要有1个或者多个输出,
3,有穷性,算法必须是一个有限的步骤,无尽的执行下去我们永远也拿不到结果,而且必须是一个可接受的时间才可以否则1万年执行出来没有意义,
4,确定性,就是每一步都是有确定的含义的,不会发生歧义,
5,可行性,也就是每一步都是可以使用计算机实现的,是可行的,
算法优化
上面的案例我们改进一下
使用枚举法是非常慢,我们想要改进一下,就是c=1000-a-b,这样可以去掉一个循环了,你会发现执行时间已经发生了惊人的提高,
import time # 计算一下程序使用了多长时间,
start_time = time.time()
# for a in range(0,1001):
# for b in range(0, 1001):
# for c in range(0, 1001):
# if a+b+c==1000 and a**2+b**2==c**2:
# print("a,b,c:%d,%d,%d"%(a,b,c))
# 时间复杂度:
# T=1000*1000*1000*2(简化计算,一行就是一个步骤)
# T=2000*2000*2000*2
# T=N*N*N*2 假设是a+b+c=N
# T=N^3*2,这就是时间复杂度,本质上不用分析这么细致,后面的是乘2,还是乘10,相比于次方,还是同一个数量级,
for a in range(0,1001):
for b in range(0, 1001):
c = 1000 - a - b
if a**2+b**2==c**2:
print("a,b,c:%d,%d,%d"%(a,b,c))
# 时间复杂度:
# T=1000*1000*(1+max(1,0)) 分支结构这个地方就是两个情况,要么执行,要么不执行,执行就是1步,不执行就是0,取最大就是1,
# 简化,就是T(n)=N*2
end_time = time.time()
print("times:%d"%(end_time-start_time))
衡量算法好坏的指标1:时间复杂度:
所以你会发现,一个问题有多种算法可以实现,而且算法和算法之间效率是不同的,
那么如何衡量算法的效率,
1,最直观的就是执行时间,
2,但是只看时间不不准确的,因为在不同的机器运行时间不通过的,所以提出了一个时间复杂度,
时间复杂度和什么因素有关
- 1,和数据规模有关系,10个数据和100万数据需要的时间是不一样的
- 2,和环境有关,一个高性能的机器和低性能的机器运行的时间是不一样的,
每一个算法在每台机器执行的总时间不同,但是执行的执行步骤是相同的,所以可以通过不同算法的执行步骤来衡量执行效率,这就是时间复杂度,
大O表示法
时间复杂度可以最终写成这种形式,T=N^3,这就是大O记法,这种表示法就是把细枝末节去掉,更直观的表示时间复杂度,就是大O表示法,
一个算法也有3个概念,
最优时间复杂度,算法完成操作最少需要多少步,这是最理想的情况,需要特殊的数据情况,没有太大的意义,
最坏时间复杂度,就是算法完成操作最多需要多少步,这是提供了一种保证,所以我们通常说的时间复杂度就是这个,而且我们只关注这个,
平均复杂度,就是最优和最坏的平均,这个我们不关注,
时间复杂度如何计算,
基本操作,即只有常数项,我们任务复杂度是O(1),(英文字母欧O,不是零)
顺序结构,是使用加法,
循环结构,是使用乘法,
分支结构,是取最大值,
- 常数的,比如就是加法,O(n)
- 如果只有顺序,分支,循环,只关注循环,循环n次,就是O(n)
- 如果有多个循环,一个是循环100次,一个是循环n次,只关注循环次数多的,就是O(n)
- 如果是嵌套循环,需要乘机,就是O(n²)
参见的时间复杂度,
线性的,比如一个循环,
次幂的,比如多个循环,
衡量算法好坏的指标2:空间复杂度
新进时间复杂度,表示算法的执行时间与数据规模之间的增长关系。类比一下,空间复杂度全称就是渐进空间复杂度(asymptotic space complexity) ,表示算法的存储空间与数据规模之间的增长关系
其实和时间复杂度是一样的计算方法,
def tmp(n):
a = [1]*n
for i in a:
print(i)
第二行是 O(n)
数据结构的概念
什么是数据结构?
数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成。
简单来说,数据结构就是设计数据以何种方式组织并存储在计算机中。
比如:列表、集合与字典等都是一种数据结构。
“程序=数据结构+算法”
数据结构,就是现在有一堆数据,我应该怎么把这些组成起来,
案例
比如一个班级的学生,有姓名,有年龄,有家乡,我应该采用什么结构,把整个班级的学生信息存下来,
有几个解决方案,
# 第一种:可以使用列表,保存这个班级的信息,每一个信息使用元组,
li1 = [
("张三", 11, "bejing"),
("李四", 13, "shanghai")
]
# 分析,这种方式找到学生需要遍历,所以时间复杂度,至少是O(n)
# for stu in stus:
# if stu(0)="张三"
# 第二种:可以使用列表,保存这个班级的信息,每一个信息使用字典
li2 = [
{"name": "张三",
"age": 11,
"hometown": "bejing"
}
]
# 第三种:可以使用字典,保存这个班级的信息,里面在用字典,
dic1 = {
"张三": {
"age": 11,
"hometown": "bejing"
}
}
# 这种方式找到学生只需要,一步,所以时间复杂度是O(1)
# dic1["张三"]
所以你采用什么样的数据结构进行保存数据,会和你的算法的时间有关系,会和你的时间复杂度有关系的,
所以算法和你的数据结构是分不开的,
上面的那三种数据组织方式,这就是数据结构,就是一组数据如何保持,
所以列表和字典不是基本的数据类型,int,float,char这些是基本的数据类型,列表和字典已经是数据结构了,元组,集合也是高级的数据结构,
算法是解决问题的思路,
所以提升一下你的思维,
程序 = 数据结构 + 算法,
把数据结构和他相应的数据处理(比如增删改查)放到一起的时候,我们叫做抽象数据类型(ADT )
数据结构的分类
数据结构按照其逻辑结构可分为线性结构、树结构、图结构
1,线性结构:数据结构中的元素存在一对一的相互关系,常见的有栈,队列,单链表,双链表,哈希表,
2,树结构:数据结构中的元素存在一对多的相互关系
3,图结构:数据结构中的元素存在多对多的相互关系