0.时间复杂度
# 如果a+b+c=1000,且a^2+b^2=c^2(a,b,c为自然数),如何求出所有a,b,c可能的组合?
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('time:%d'%(end_time-start_time))
print('finished')
输出
a,b,c:0,500,500
a,b,c:200,375,425
a,b,c:375,200,425
a,b,c:500,0,500
time:203
finished
下图练习第一个O(1),第二个O(n),第三个O(n^2),第四个O(n ^3)
下图为用函数
为列表添加元素,因为函数是基本步骤的封装,所以不能算作1步:
def test1():
li = []
for i in range(10000):
li.append(i)
def test2():
li = []
for i in range(10000):
li = li + [i]
def test3():
li = [i for i in range(10000)]
def test4():
li = list(range(10000))
def test5():
li = []
for i in range(10000):
li.extend([i])
def test6():
li = []
for i in range(10000):
li.insert(0,i)
from timeit import Timer
t1 = Timer("test1()", "from __main__ import test1")
print("append:",t1.timeit(1000))
t2 = Timer("test2()", "from __main__ import test2")
print("+: ",t2.timeit(1000))
t3 = Timer("test3()", "from __main__ import test3")
print("[i for i in range]:",t3.timeit(1000))
t4 = Timer("test4()", "from __main__ import test4")
print("list(range()): ",t4.timeit(1000))
t5 = Timer("test5()", "from __main__ import test5")
print("extend: ",t5.timeit(1000))
t6 = Timer("test6()", "from __main__ import test6")
print("insert(0): ",t6.timeit(1000)) # append从队尾加,insert从对头加,时间差大
append: 0.9385726000000432
+: 203.88772059999974
[i for i in range]: 0.43753339999966556
list(range()): 0.2188015000001542
extend: 1.3811896000006527
insert(0): 46.54819399999997
1.顺序表
程序=数据结构+算法。下图为数据的存储:1个int数占4个字节(B)(1B=8bit),都是int型按顺序存放即顺序表
方便查找
下图左边为顺序表
基本形式,右边为元素外置形式(存地址)
一体式如果存储空间不够,连表头信息都要换。
2.链表
顺序表
要求存储空间必须连续,一旦不够就要动态改变数据区。
线性表
分为顺序表和链表,下图为链表
,不用改变原数据结构,多一个加一个。
如下图实现两数交换:第三行到第四行:先=号右边,再=号左边
如下图若int a=10,a是别名,不代表另一块存储空间
下面为代码实现自己封装的链表:类,对象,属性,方法
:
下图为add头部插元素方法:
下面为指定位置插,下图理解python中=号,间接:
下图为删除节点:
class Node(object):# node=Node(100),这个节点保存100这个数
"""节点"""
def __init__(self,elem): # self在__init__自动跳出,接收elem
self.elem = elem # 节点有elem和next属性,将elem保存到节点对象中
self.next = None
class SingleLinkList(object):
"""单链表"""
def __init__(self,node=None): #注意中间,号 #init方法
self._head = node #_为私有属性,保存头节点信息
def is_empty(self):
"""链表是否为空"""
return self._head == None
def length(self):
"""链表长度"""
cur = self._head # cur游标,用来移动遍历节点
count = 0
while cur != None:
count += 1
cur = cur.next
return count
def travel(self):
"""遍历链表"""
cur = self._head
while cur != None:
print(cur.elem,end=" ")
cur = cur.next
def add(self, item):
"""头部添加节点"""
node = Node(item)#构造新节点node
node.next = self._head#先
self._head = node#后
def append(self, item):
"""尾部添加节点"""
node = Node(item)#构造新节点node
if self.is_empty():
self._head = node#直接加新节点
else:
cur = self._head #指向头节点
while cur.next != None:
cur = cur.next
cur.next = node # 尾部添加新的node
def insert(self, pos, item):
"""在指定位置添加节点"""
#:param pos从0开始
if pos <= 0:
self.add(item) #头插法
elif pos > (self.length()-1):
self.append(item) #pos(0,1,2...)>长度,进行尾插法
else:
pre = self._head
count = 0
# 移动到了指定位置的前一个位置
while count < (pos-1):
count += 1
pre = pre.next
# 当循环退出后,pre指向pos-1位置
node = Node(item)
node.next = pre.next
pre.next = node
def remove(self, item):
"""删除一个节点"""
cur = self._head
pre = None
while cur != None:
if cur.elem == item: #用户想删的item
if cur == self._head: #要删除的刚好是头节点
self._head = cur.next #直接指向下一个
else:
pre.next = cur.next
break
else:
pre = cur
cur = cur.next
def search(self, item):#item为用户传进
"""查找节点是否存在"""
cur = self._head
while cur != None:
if cur.elem == item:
return True
else:
cur = cur.next
return False
if __name__ == "__main__":
ll = SingleLinkList()
print(ll.is_empty())
print(ll.length())
ll.append(1)
print(ll.is_empty())
print(ll.length())
ll.append(2)
ll.add(8)
ll.append(3)
ll.append(4)
ll.append(5)
ll.append(6)
ll.insert(3,9)
ll.travel()
print()#换下一行
ll.remove(9)
ll.travel()
输出
True
0
False
1
8 1 2 9 3 4 5 6
8 1 2 3 4 5 6
下面为单向循环
链表遍历,求长度,添加,删除元素。指定位置插入和单链表一样不用变
下面为双向
链表:下图用了面向对象的继承
3.栈队列
栈:后进先出(和线性表一样都是存储数据,但没有线性表中间尾部取数据等操作)
4.六种排序(冒泡选择,插入希尔,快速归并)
排序算法稳定性:如下图按元组中第一个元素大小排序,(维持之前次序)这组稳定。
如果一个函数在内部调用它自己,那么这函数就叫递归函数
选择排序
从后面无序序列中选一个,放前面最后一位置
插入排序
是从后面无序序列中选一个,插入前面有序序列位置中哪一个(从右往左比对)
希尔排序
思想是插入排序的改进:对54,77,20按插入排序排,然后26,31排序,,,
下图第二行是第一行按gap=4排的
from time import time # 计时装饰器
def timer(func):
def inner(*args,**kwargs):
start = time()
result = func(*args,**kwargs)
end = time()
usedTime = 1000 * (end - start)
print("%s function used %.2f ms" %(func.__name__,usedTime))
return result
return inner
@timer
def bubble_sort(alist): #冒泡排序
n = len(alist)
for j in range(n-1):
for i in range(n-1-j):
if alist[i]>alist[i+1]:
alist[i],alist[i+1]=alist[i+1],alist[i]
if __name__ == "__main__":
blist = list(range(1,3000 + 1))
import random
blist = random.sample(blist, k=len(blist))#从blist中随机获取k个元素
alist = blist[:1000]
#print(alist)
bubble_sort(alist)
print(alist)
输出
@timer
def select_sort(alist):#选择(遍历)排序,从右边选择最小放左边
n = len(alist)
for j in range(n-1):#j:0到n-2
min_index=j #假的最小值下标动,从第一个开始遍历
for i in range(j+1,n):
if alist[min_index]>alist[i]:
min_index=i
alist[j],alist[min_index]=alist[min_index],alist[j]
#这里min_index为真的最小值下标
if __name__ == "__main__":
blist = list(range(1,3000 + 1))
import random
blist = random.sample(blist, k=len(blist))
alist = blist[:1000]
select_sort(alist)
print(alist)
输出
@timer
def insert_sort(alist):#插入排序
n=len(alist)
for j in range(1,n):#从右边的无序序列中取出多少个元素执行这样的过程
#j=[1,2,3,n-1]
#i代表内层循环起始值
i=j
#执行从右边的无序序列中取出第一个元素,即i位置的元素
#然后将其插入到前面的正确位置中
while i>0:
if alist[i]<alist[i-1]:
alist[i],alist[i-1]=alist[i-1],alist[i]
i-=1
else:
break
if __name__ == "__main__":
blist = list(range(1,3000 + 1))
import random
blist = random.sample(blist, k=len(blist))
alist = blist[:1000]
insert_sort(alist)
print(alist)
输出
@timer
def shell_sort(alist):#希尔排序
n=len(alist) #n=9
gap=n//2 #gap=4
while gap>0:#gap变化到0之前,插入算法执行的次数
#与普通的插入算法的区别就是gap步长
for j in range(gap,n):
i=j #j=[gap,gap+1,gap+2,gap+3,..,n-1]
while i>0:
if alist[i]<alist[i-gap]:
alist[i],alist[i-gap]=alist[i-gap],alist[i]
i-=gap
else:
break
gap//=2
if __name__ == "__main__":
blist = list(range(1,3000 + 1))
import random
blist = random.sample(blist, k=len(blist))
alist = blist[:1000]
shell_sort(alist)
print(alist)
输出
前面的排序方法都分为左右两部分。快排中若low指的元素比54大停走,high指的元素比54小停走,则low和high指的元素互相交换,继续往中间走。重合时low指的前一个元素就是54位置固定了,54的左右边分别继续low和high操作。
def quick_sort(alist,first,last):#快速排序
if first>=last:
return
mid_value=alist[first]
low=first #low和high为游标,first为第一个元素,last为最后一元素
high=last
while low<high:#没有相遇
while low<high and alist[high]>=mid_value:
high-=1 #high左移
alist[low]=alist[high]#元素交换,游标不交换
while low<high and alist[low]<mid_value:
low+=1
alist[high]=alist[low]
#从循环退出时,low==high
alist[low]=mid_value
#对low左边的列表执行快速排序
quick_sort(alist,first,low-1)
#对low右边的列表执行快速排序
quick_sort(alist,low+1,last)
def main():
blist = list(range(1,3000 + 1))
import random
blist = random.sample(blist, k=len(blist))
alist = blist[:1000]
quick_sort(alist,0,len(alist)-1)
print(alist)
main()
下图拆分两部分,直到只有一个元素
上图先26和17比,17小拿出来right指针往后移。26再和93比,26小将26拿出来排在17后
5.二分查找
下面为搜索:二分查找:对象只能是顺序表:(0+8)/2=4找到下标为4的数字7。(0+3)/2=1(取整)对应的3。(2+3)/2=2(取整)对应的4。
item为要查找的元素:
非递归和快排差不多,用first和last指明查找的范围
下面为二分查找时间复杂度,2的几次方=n(序列长度)。
如果按遍历来查找,时间复杂度为O(n)。
6.树的遍历
下面为广度
即层次
即横向
遍历:
下图先序(先根):根左右
中序:左根右(从左到右即从下到上)
后序:左右根(从左到右即从下到上)
7.python对文件操作
1.增:写入文件内容给文本文件
def writeTextFile(filePath, fileContent, encoding='utf8'):
with open(filePath, 'w', encoding=encoding) as file:
file.write(fileContent)
2.删
3.改:批量修改图片大小
import os
from PIL import Image
def getFilePathList(dirPath, partOfFileName=''):
allFileName_list = list(os.walk(dirPath))[0][2]
fileName_list = [k for k in allFileName_list if partOfFileName in k]
filePath_list = [os.path.join(dirPath, k) for k in fileName_list]
return filePath_list
def batchResizeImage(oldDirPath, newDirPath, height, width):
if not os.path.isdir(newDirPath):
os.mkdir(newDirPath)
jpgFilePath_list = getFilePathList(oldDirPath, '.jpg')
for jpgFilePath in jpgFilePath_list:
image = Image.open(jpgFilePath)
resized_image = image.resize((height, weight), Image.ANTIALIAS)
jpgFileName = os.path.split(jpgFilePath)[1]
saveFilePath = os.path.join(newDirPath, jpgFileName)
resized_image.save(saveFilePath)
oldDirPath = 'source_images'
newDirPath = 'train_images'
height = 640
width = 640
batchResizeImage(oldDirPath, newDirPath, height, width)
4.查:查询文件夹中的文件
import os
def getFileNameList(dirPath, partOfFileName=''):
allFileName_list = list(os.walk(dirPath))[0][2]
fileName_list = [k for k in allFileName_list if partOfFileName in k]
return fileName_list
def getFilePathList(dirPath, partOfFileName=''):
allFileName_list = list(os.walk(dirPath))[0][2]
fileName_list = [k for k in allFileName_list if partOfFileName in k]
filePath_list = [os.path.join(dirPath, k) for k in fileName_list]
return filePath_list
查:读取文件
def readTextFile(filePath, encoding='utf8'):
with open(filePath, encoding=encoding) as file:
return file.read()
查:搜索文件夹路径内含有指定内容的代码文件
import os
# 传入3个参数:文件夹路径dirPath、指定内容partOfFileContent、代码文件后缀名suffixOfFileName
def searchFileContent(dirPath, partOfFileContent, suffixOfFileName=''):
dirPath = os.path.expanduser(dirPath)
walk_list = list(os.walk(dirPath))
result_list = []
for walk in walk_list:
filePath_list = [os.path.join(walk[0], k) for k in walk[2] \
if k.rsplit('.', maxsplit=1)[1]==suffixOfFileName.strip('.')]
for filePath in filePath_list:
with open(filePath, encoding='=utf8') as file:
fileContent = file.read()
if partOfFileContent in fileContent:W
print(filePath)
result_list.append(filePath)
return result_list