2.8.四大数据结构
2.8.1.单向队列(List)
在排队的时候,我们总是从队尾开始排列,然后逐渐向前移动,直到到达第一个;随后完成任务离开队伍.这样,就构成了一个单向队列.在单向队列中,总是遵循先进先出,后进后出的原则,可以用这样一张图来演示:
代码实现:
class mylist(object):
def __init__(self,*args):
self.objects=args
def _print(self):#依次打印
if type(self.objects)!=tuple:
print(self.objects)
else:
for i in self.objects:
print(str(i)+" ",end="")
def _list(self):#转为列表
if type(self.objects)!=tuple:
return [self.objects]
result=[]
for i in self.objects:
result.append(i)
return result
def _add(self,new): #向队首加入元素
result=[new]
for i in self.objects:
result.append(i)
return result
def _pop(self): #从队尾删除元素
result=[]
for i in range(len(self.objects)-1):
result.append(self.objects[i])
return result
def _isNone(self):#判断类型 为空返回0 单数据返回1 多数据返回2
if self.objects==None:return 0
if self.objects==[] or ():return 0
if type(self.objects)!=tuple:return 1
if type(self.objects)==tuple:return 2
2.8.2.双向队列(Queue)
如果说单向队列限制高,那么双向队列就显得比较随意了.无论在队列的哪一段,都可以删除或增加.相较于单向队列,其增加了水平自由度,但仍不可以从当中增加数据.
代码实现:
class myqueue(object):
def __init__(self,**args):
self.objects=args
def _print(self):#同单向队列的_print
if type(self.objects)!=tuple:
print(self.objects)
else:
for i in self.objects:
print(str(i)+" ",end="")
def _list(self):#同单向队列的_list
if type(self.objects)!=tuple:
return [self.objects]
result=[]
for i in self.objects:
result.append(i)
return result
def _adds(self,new):#从队首加入
result=[new]
for i in self.objects:
result.append(i)
return result
def _addw(self,new): #从队尾加入
result=[]
for i in self.objects:
result.append(i)
result.append(new)
return result
def _delw(self):#从队尾删除
result=[]
for i in range(len(self.objects)-1):
result.append(self.objects[i])
return result
def _dels(self): #从队首删除
result=[]
for i in range(1,len(self.objects)):
result.append(self.objects[i])
return result
def _isNone(self):#同单向队列的_isNone
if self.objects==None:return 0
if self.objects==[] or ():return 0
if type(self.objects)!=tuple:return 1
if type(self.objects)==tuple:return 2
2.8.3.栈(Stack)
想象一个管子.如果我们将一个蓝球放进去,再放一个红球进去,那我们想要取出蓝球时就必须先取出红球.于是栈应运而生.其特点为先进后出,后进先出.
代码实现:
class mystack(object):
def __init__(self,*args):
self.objects=args
def _print(self):#同前二者的_print
if type(self.objects)!=tuple:
print(self.objects)
else:
for i in self.objects:
print(str(i)+" ",end="")
def _list(self):#同前二者的_list
if type(self.objects)!=tuple:
return [self.objects]
result=[]
for i in self.objects:
result.append(i)
return result
def _add(self,new):#同单向队列的_add
result=[new]
for i in self.objects:
result.append(i)
return result
def _del(self): #同双向队列的_dels
result=[]
for i in range(1,len(self.objects)):
result.append(self.objects[i])
return result
def _isNone(self):
if self.objects==None:return 0
if self.objects==[] or ():return 0
if type(self.objects)!=tuple:return 1
if type(self.objects)==tuple:return 2
2.8.4.链表(Link)
我们常见的list就是一种link,直接使用index进行索引,可以在任何一个位置添加或删除元素,并使用index()
方法查找数据.想必各位应该都很熟悉吧?此处不再赘述.
2.9.八大基础算法
2.9.0.算法复杂度
实现一个目标所需要使用的底层步骤数(注意!不是基层!),用 O ( n ) O(n) O(n)表示.看个例子:
a=[1,2,3,...,n]
b=[]
for i in range(0,n+1):
b.append(a[n-i-1])
b列表完全倒序了a列表.它使用了n次计算(每次将a的一个值存入b中),因此其算法复杂度为O(n)=n,第一个n没有实际意义,仅仅为了表达一个式子;而第二个n为列表a中元素的数量n.
2.9.1.穷举算法
小白最喜欢的算法,也是最最常用的算法.在数据量不大事时,直接使用for循环遍历每一个数据来寻求答案,例如:
ans=[1,2,3,4,5]
to=2
for i in range(len(ans)):
if ans[i]==to:
print(i)
break
简单粗暴,简洁明了.它的算法复杂度为O(n)=n.
2.9.2.归纳(递推)算法
逐步计算得到结果.通过已知条件来一步步往后推得到结果.例如斐波那契数列:
def get(n):
a1=a2=1
a=[a1,a2]
for i in range(2,n+1):
a.append(a[i-2]+a[i-1])
return a,a[n]
利于调试,依次计算.算法复杂度O(n)=n-p.p为前推数(即推导一个数据需要其他数据的量).
2.9.3.递归(循环)算法
在函数中调用自己,在必要时跳出,和循环类似.同样求斐波那契数列第n项:
def get(n,a):
if i>=2:
result=get(n-2)+get(n-1)
else:
result=1
return result
缺点是不易于理解,算法复杂度O(n)=2n.但利于复杂函数对死循环(While True)的优化.
2.9.4.分支算法
即二叉树.在每一个节点判断一次条件,直到到达某一个无法继续向下扩展的位置.例如:
1 | |||||||
---|---|---|---|---|---|---|---|
1.1 | - | - | - | 1.2 | - | - | |
1.11 | - | 1.12 | - | 1.21 | - | 1.22 | |
1.111 | 1.112 | 1.121 | 1.122 | 1.211 | 1.212 | 1.221 | 1.222 |
常与递归结合.每到一次分叉就判断条件,复杂度不定,但一般为 2 log 2 n 2\log_2n 2log2n.
2.9.5.动态规划
结合树.多个枝干可能指向同一结果.同样可以与递归结合,依次判断.注意,在一些支点处,判断条件可能有重叠.这将致使它们指向相同结果.应当实时调整它们避免大量重复计算.
2.9.6.贪心算法
不从整体的最优解考虑,而是做出当前的局部最优的选择.在每一个小区块中取得满足条件的最优解,然后汇总.其本质是将求问题的整体最优解转化为一系列局部最优的选择(即贪心选择).在大数据分析中,这很有用!
2.9.7.回溯算法
在使用分支算法时,会出现多个结果的问题.如果最终只能保留一个,那么需要在某些时候从次枝叶返回/回溯到上一个节点,考虑放宽判断条件或者直接跳过(continue)某一分支.
2.9.8.模拟算法
在真正计算之前用其他判断的条件来模拟计算条件.一般而言会放宽条件.如果放宽后仍没有结果,那么可以直接跳过该分支.
2.10.本章总结
2.10.1.习题讲解
import os
import pandas
import matplotlib.pyplot as plt
os.rename("text.txt","text.csv")
df=pandas.read_csv("text.csv")
with open("result.txt","w",encoding="utf-8") as w:
w.write(df)
plt.scatter(df)
plt.show()
2.10.2.本章小结
本章为Python教程的第二章.通过学习错误与试错(try…except)、迭代器与可迭代对象、惰性计算、匿名函数(lambda)、数组(array)、数据表(dataframe)、上传自己的模块和包、数据可视化(matplotlib)、四大数据机构和八大基础算法,想必各位对于Python本身已经有了非常好的理解程度.
下一章,我们将一起走进数据的世界,从权重算法开始学习,我们将学到有关机器学习的内容和简单数据库(MySQL)的使用.如果有机会,我们还会一起走进真正的大数据库(Spark),徜徉在百万级数据库中,从select和App对象层层深入.
那么所有Python入门的内容到此为止了.接下来,让我们准备好,开往数据科学的列车,即将出发!
2.11.补充阅读:一些有趣的算法
2.11.1.排序的10种可能性
声明:由于博主对于此方面并不是特别熟悉,算法来自此教程.
2.11.1.1.冒泡排序
从第一个数据开始,依次比对它(a)和在它之后的数据(b).若a<b,则进入下一组比对;反之则调换a和b的位置.算法实现:
def bubble(l):
for i in range(1, len(l)):
for j in range(0, len(l)-i):
if l[j] > l[j+1]:
l[j], l[j + 1] = l[j + 1], l[j]
return l
2.11.1.2.选择排序
不断遍历数组,依次挑出最小的数并放置在数组开头.算法实现:
def select(l):
for i in range(len(l)-1):
mi=i
for j in range(i+1, len(l)):
if arr[j] < l[mi]:
mi = j
if i != mi:
l[i],l[mi]=l[mi],l[i]
return l
2.11.1.3.插入排序
依次遍历数组,插入到合适的位置.算法实现:
def insertionSort(arr):
for i in range(len(arr)):
preIndex = i-1
current = arr[i]
while preIndex >= 0 and arr[preIndex] > current:
arr[preIndex+1] = arr[preIndex]
preIndex-=1
arr[preIndex+1] = current
return arr
以上3种是相对实用的排序算法,如果对其他的感兴趣,也可以前往前文的网址查看.
2.11.2.KNN算法
如果要判断一个数的类型,可以根据它的邻居来推断.俗称物以类聚.KNN算法可以对一个数据组中的某一数据进行猜测.例如有列表:(为了方便展示用表格形式展示):
index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|
number | 1 | 1.1 | 1.12 | 2 | 3 | ? | 3.1 | 4 | 4.3 | 4.4 | 5 |
type | int | float | float | int | int | ? | float | int | float | float | int |
现在index为5的位置类型未知,而其他位置的类型均为已知.现在我们分别取样观察:
1.取邻居数n为1.那么也就是取2n,2个邻居.它们就是index为4的int和index为6的float.显然此时两个类型数量相等.
2.取n为2,即index=3,4,6,7.类型分别为int,int,float,int.此时int比float多,可以初步判断index=5的数也为int.
3.取n为3,即index=2,3,4,6,7,8.类型包括3个int和3个float,相等.
4.取n为4,即index=1,2,3,4,6,7,8,9.类型包括3个int和5和float.float又比int多了,因此判断index=5的数也为float.
5.取n为5,即index=0,1,2,3,4,6,7,8,9,10.现在又是int和float各5个,相等.
现在告诉你答案,index=5的数是个float.这样一来,在同样的模型下邻居数n就可以放心的取4了,因为它会相对可靠.这就是KNN算法,又称取领算法.它在人工智能领域中非常非常有用.请尝试用Python实现它(给定列表).