算法
一丶时间复杂度
# 评判规则:量化算法执行的操作 / 执行步骤的数量
# 最重要的项:时间复杂度表达式中最有意义的项
时间复杂度排序
O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
二丶数据结构
# 概念:
对数据(基本数据类型(int,float,char))的组织方式被称作数据结构. 数据结构解决的是一组数据如何进行保存.
三丶python测试代码执行的时长
timeit模块:
# Timer类:该类是timeit模块中专门用于测量python代码的执行速度/时长的。
原型为:class timeit.Timer(stmt='pass',setup='pass')。
stmt参数:表示即将进行测试的代码块语句。
setup:运行代码块语句时所需要的设置。
timeit函数:timeit.Timer.timeit(number=100000),该函数返回代码块语句执行number次的平均耗时。
# 例如:
from timeit import Timer
def test01():
alist = []
for i in range(1000):
alist.append(i)
return alist
def test02():
alist = []
for i in range(1000):
alist.insert(0,i)
return alist
def test03():
alist = []
for i in range(1000):
alist += [i]
return alist
def test04():
alist = list(range(1000))
return alist
if __name__=='__main__':
t=Timer('test01()','from __main__ import test01')
print(t.timeit(1000)) # 执行1000次,所耗费的时间
t1=Timer('test02()','from __main__ import test02')
print(t1.timeit(1000)) # 执行1000次,所耗费的时间
四丶栈
# 特性: 先进后出的数据结构
# 重要的两个概念: 栈顶 栈底
#### 使用python 类 实现栈
# 需求:
Stack() 创建一个空的新栈。 它不需要参数,并返回一个空栈。
push(item)将一个新项添加到栈的顶部。它需要 item 做参数并不返回任何内容。
pop() 从栈中删除顶部项。它不需要参数并返回 item 。栈被修改。
peek() 从栈返回顶部项,但不会删除它。不需要参数。 不修改栈。
isEmpty() 测试栈是否为空。不需要参数,并返回布尔值。
size() 返回栈中的 item 数量。不需要参数,并返回一个整数。
class Stack():
def __init__(self):
self.items=[]
def push(self,item):
'''入栈'''
return self.items.append(item)
def pop(self):
'''出栈'''
return self.items.pop() # 删除最后一个元素 , 利用列表的特性pop方法删除最后一个元素
def peek(self):
'获取栈顶元素的"索引"位置'
return len(self.items)-1
def isEmpty(self):
'''判断是否是空栈'''
return self.items==[] # 返回布尔值
def size(self):
'''返回栈的大小'''
return len(self.items)
五丶队列
# 特性: 先进先出
# 重要的两个概念: 对头 对尾
# python 类 实现 队列
Queue() 创建一个空的新队列。 它不需要参数,并返回一个空队列。
enqueue(item) 将新项添加到队尾。 它需要 item 作为参数,并不返回任何内容。
dequeue() 从队首移除项。它不需要参数并返回 item。 队列被修改。
isEmpty() 查看队列是否为空。它不需要参数,并返回布尔值。
size() 返回队列中的项数。它不需要参数,并返回一个整数。
class Queue():
def __init__(self):
self.items=[]
def enqueue(self,item):
'''入队'''
return self.items.append(item)
def dequeue(self):
'''出队'''
return self.items.pop(0)
def isEmpty(self):
'''判断是否是空队'''
return self.items==[] # 返回布尔值
def size(self):
'''返回队的大小'''
return len(self.items)
# 例题: 烫手的山芋
# 规则如下:
烫手山芋游戏介绍:6个孩子围城一个圈,排列顺序孩子们自己指定。第一个孩子手里有一个烫手的山芋,需要在计时器计时1秒后将山芋传递给下一个孩子,依次类推。规则是,在计时器每计时7秒时,手里有山芋的孩子退出游戏。该游戏直到剩下一个孩子时结束,最后剩下的孩子获胜。请使用队列实现该游戏策略,排在第几个位置最终会获胜。
# 分析: 一秒之后再进行传递 , 7秒的时间,传递次数是6次,即:time-1
# 代码:
class Queue():
def __init__(self):
self.items = []
def enqueue(self,item):
self.items.insert(0,item)
def dequeue(self):
return self.items.pop()
def size(self):
return len(self.items)
#初始化了六个孩子
kids = ['A','B','C','D','E','F']
q=Queue() # 实例化一个队列
for kid in kids:
q.enqueue(kid)
while q.size()>1:
for i in range(6):
kid=q.dequeue()
q.enqueue(kid)
q.dequque() # 传递 7秒时间,剔除一个孩子
print(q.dequque())
六丶双端队列
# 同同列相比,有两个头部和尾部。可以在双端进行数据的插入和删除,提供了单数据结构中栈和队列的特性
# python 实现 双端队列
Deque() 创建一个空的新 deque。它不需要参数,并返回空的 deque。
addFront(item) 将一个新项添加到 deque 的首部。它需要 item 参数 并不返回任何内容。
addRear(item) 将一个新项添加到 deque 的尾部。它需要 item 参数并不返回任何内容。
removeFront() 从 deque 中删除首项。它不需要参数并返回 item。deque 被修改。
removeRear() 从 deque 中删除尾项。它不需要参数并返回 item。deque 被修改。
isEmpty() 测试 deque 是否为空。它不需要参数,并返回布尔值。
size() 返回 deque 中的项数。它不需要参数,并返回一个整数。
class Deque():
def __init__(self):
self.items=[]
def addFront(self,item):
return self.items.insert(0,item)
def addRear(self):
return self.items.append(item)
def removeFront(self):
return self.items.pop(0)
def removeRear(self):
return self.items.pop()
def isEmpty(self):
return self.items == []
def size(self):
return len(self.items)
# 双端队列应用案例:回文检查
# 回文是一个字符串,读取首尾相同的字符,例如,radar toot madam。
q = Deque()
s = 'radar'
for i in s:
q.addFront(i)
while q.size() > 1:
if q.removeFront() != q.removeRear():
print('不是回文')
break
else:
print('是回文')
七丶内存
# 1. 什么是变量
变量就是引用。变量其实表示的就是计算机中的一块内存。(即:变量起始就是内存地址)
# 2.一块内存空间会有两个默认的属性
# 空间大小
bit:位
byte:字节
kb:1020字节
mb
gb
tb
# 内存地址
作用:寻址
八丶顺序表
# 首先明确: 集合中存储的元素是有顺序的,顺序表的结构可以分为两种形式:单数据类型和多数据类型。
# 然后明白: python中的列表和元组就属于多数据类型的顺序表
# 单数据类型: 内存使连续开辟的
# 多数据类型: 内存是非连续开辟的
# 顺序表的弊端:顺序表的结构需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁。
九丶链表
# 什么是链表?
1. 相对于顺序表,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理且进行扩充时不需要进行数据搬迁。
2. 链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是不像顺序表一样连续存储数据,而是每一个结点(数据存储单元)里存放下一个结点的信息(即地址)
# python 实现 链表
is_empty():链表是否为空
length():链表长度
travel():遍历整个链表
add(item):链表头部添加元素
append(item):链表尾部添加元素
insert(pos, item):指定位置添加元素
remove(item):删除节点
search(item):查找节点是否存在
# 构建 Node节点
class Node():
def __init__(self,item):
self.item=item
self.next=None
# 构建链表
class Link():
def __init__(self):
# head的属性永远指向第一个节点的地址,如果链表为空则指向None
self.head=None
def add(self,item):
' 给链表头 添加节点'
node=Node(item)
node.next=self.head # 新节点的next 指向 head节点
self.head=node # head节点 重新被 node节点取代
def append(self,item):
'链表尾部添加元素'
node=Node(item)
# 1. 判断是否是头结点
if self.head==None:
retrun self.head=node
# 2. 循环到最后一个节点,需要pre
cur = self.head
pre=None
while cur:
pre=cur
cur=cur.next
pre.next=node
def search(self,item): # 查找
find=False
cur=self.head
while cur:
if cur.item==item:
find=True
retrun
cur=cur.next
return find
# 指定位置增加
def insert(self,pos,item):
node=Node(item)
if pos==0:
self.add(item)
cur=self.head
pre=None
for i in range(pos) # 循环到指定的位置
pre=cur
cur=cur.next
# 批注: 此时 可以获得上一个节点和当前节点
pre.next=node # 上一个节点的next 指向新的节点
node.next=cur # 新节点的next 执行那个cur(当前的节点)
def remove(self,item):
cur=self.head
pre=None
# 1. 删除的值是第一个节点
if self.head.item==item:
self.head=self.head.next
return
# 2. 删除的值不是第一个节点
while cur:
if cur.item==item: #
pre.next=cur.next
return
pre=cur
cur=cur.next
def is_empty(self):
retrun self.head==None
def length(self):
count=0 # 计数
cur = self.head # 当前节点
while cur:
count+=1 # 自增
cur=cur.next
return cur
def travel(self):
'遍历'
cur = self.head # 记录当前节点
while cur: # 当前节点为空,则说明指向了最后,也就是完成了循环遍历
print(cur) # 输出值
cur=cur.next
return cur
十丶二分查找
# 1.必须是有序的
# 2.对半砍, 时间复杂读nlogn
def search(data,item):
start=0
end=len(data)-1
while start <= end:
mid=(start+end)//2
if data[mid]==item:
return True
elif data[mid]>item:
end=mid-1
elif data[mid]<item:
start=mid+1
data=[i for i in range(1, 10000)]
print(search(data,33))
十一丶二叉树排序
### 分为:
# 普通二叉树
# 排序二叉树
# 包含:
# 1. 根节点
# 2. 左右叶子节点
# 3. 子树:完整的子树和非完整的子树
- 每一颗子树根节点可以作为另一颗子树的左右叶子节点
- 每一个根节点都可以做为一颗子树的根节点
# 普通二叉树实现
class Node():
def __init(self,item):
self.item=item
self.left=None
self.right=None
class Tree():
def __init__(self):
self.root=None
# 插入: 只在空的地方插入值
def insert(self,item):
# 1.实例化一个node节点
node=Node(item)
# 2. 第一个位置是否为空
if self.root==None:
self.root=node
return
# 3. 循环 , 并且判断哪个位置是空的
# 思路: 拿到每一个节点,放入列表里,如果这个节点存在左右叶子节点,把左右叶子节点添加queue列表中,如果这个节点的左或右节点为空,即可插入到这个位置.
cur=self.root # 拿到第一个节点
queue=[cur]
while True:
leaf=self.queue.pop(0)# 从列表中获得第一个元素
if leaf.left==None:
# 左插入
leaf.left=node
break
else:
# 当前节点的左节点都不为空 , 把左节点添加到列表中
queue.append(leaf.left)
if leaf.right==None:
# 右插入
leaf.right=node
break
else:
# 当前节点的右节点都不为空,把右节点添加到列表中
queue.append(leaf.right)
# 遍历(广度遍历:一行一行来)
def travel(self):
cur=self.root
queue=[cur]
while queue: # 当列表为空时,说明遍历完了
leaf=queue.pop(0)
print(leaf.item) # da
if leaf.left!=None:
queue.append(leaf.left)
if leaf.right!=None:
queue.append(leaf.right)
# 二叉树如的遍历
# 广度遍历:横向
# 深度遍历: 纵向. 一定是作用在每一颗子树
# 前序遍历: 根左右
# 中序遍历: 左根右
# 后序遍历: 左右根
# 前序遍历
def front_sort(self,root):
# 根->左->右
# 利用递归的特性:
if root==None:
return
print(root.item)
self.front_sort(root.left)
self.front_sort(root.right)
# 中序遍历
def middle_sort(self,root):
if root==None:
return
self.middle_sort(root.left)
print(root.item)
self.middle_sort(root.right)
# 后序遍历
def after_sort(self,root):
if root==None:
return
self.after_sort(root.left)
self.after_sort(root.right)
print(root.item)
#### 排序二叉树
# 当向排序二叉树中插入节点的时候,让节点和树的根节点进行大小比较。比根节点大的节点需要插入到树的右侧,否则插入到树的左侧
# 特点: 左边的都比根节点小, 右边的都比根节点大
class Node():
def __init__(self,item):
self.item=item
self.left=None
self.right=None
class SortTree():
def __init__(self):
self.root=None
# 有序插入
def inert(self,item):
node=Node(item)
if self.root==None:
self.root=node
cur=self.root
while True:
# a,b,c,e节点为例: a是root根节点, 1.当b节点小于a节点,往根节点的左边进行插入,如果左边的值不为None,则更改根节点为b.(利用局部子树的原理实现)
if cur.left>item: # 往左插入
if cur.left==None:
cur.left=node
return
else:
cur=cur.left
else: # 往右插入
if cur.right==None:
cur.right=None:
return
else:
cur=cur.right
十二丶排序算法
# 冒泡
def sort(data):
for i in range(len(data) - 1):
for j in range(len(data) - 1 - i):
if data[j] > data[j + 1]:
data[j], data[j + 1] = data[j + 1], data[j]
return data
# 选择
def sort(data):
for i in range(len(data)-1):
max_index=0
for j in range(len(data)-1-i):
if data[max_index]<data[j+1]:
max_index=j+1
data[max_index],data[len(data)-1-i]=data[len(data)-1-i],data[max_index]
return data
# 插入
def sort(data):
for i in range(1,len(data)): # 从数据的第二个位置开始进行, 当i=0时,没有必要进行一个比较,有序的列表里只有一个元素.
while i >0:
if data[i]>data[i-1]:
data[i],data[i-1]=data[i-1],data[i]
i-=1
else:
break
return data
# 快排
# : 递归+二分
def sort(data):
first=0
end=len(data)-1
# 希尔排序: 插入排序就是增量为1的希尔排序
### 增量:
# 初始值: 元素个数整除2
# 增量表示分组的组数
def sort(data):
gap=len(data)//2
while gap>0: # 判断增量 ,当增量为 1时,则是插入排序
for i in range(1,len(data)):
while i>0:
if data[i]<data[i-gap]:
data[i],data[i - gap]=data[i-gap],data[i]
i-=gap
else:
break
gap//=2
return data
data=[8,5,2,1,3,7,6,4,9]
print(sort(data))
# 快排
设定一个基数mid,基数的初始值就是乱序序列中的第一个元素值
将除了基数剩下所有的数值跟基数做比较。比较之后需要达到的一个效果就是:
基数左侧放置的都是比它小的数
基数右侧放置的都是比它大的数
#
def sort(data,start,end):
low=start
high=end
if low>high: # 结束递归的条件
return
mid=data[start]
while low<high:
# 从右开始
while low<high:
if data[high]<mid:
# 往左移动
data[low]=data[high]
break
else:
high-=1
while low<high:
if data[low]<mid:
low+=1
else:
data[high]=data[low]
break
if low==high:
data[high]=mid
sort(data,start,high-1)
sort(data,high+1,end)
return data
alist = [6 ,1, 2, 7, 9, 3 , 4, 5, 10, 8]
print(sort(alist,0,len(alist)-1))
六丶堆排序
### 堆排序
# 堆调整
def sift(data,low,high):
i =low # i 是根节点
j=2*i+1 # 获得左孩子节点
tmp=data[i] # 获得根节点的值
while j<=high:
if j<high and data[j]<data[j+1] # j<high是判断是否有右孩子节点, data[j]<data[j+1]右孩子大于左孩子,
j+=1 # j指向右孩子
if data[j] > tmp : # 判断孩子比最高的领导大
data[i]=data[j] # 孩子填充到父亲的空位上
i=j # 孩子成为新父亲
j=2*i+1 # j成为新孩子
else:
break
data[i]=tmp # 最高领导放到父亲位置
def heap_sort(data):
n=len(data)
for i in range(n//2-1,-1,-1): # 找到最后的堆的领导,
sift(data,i,n-1) # 调整整个堆的大小
for j in range(n-1,-1,-1): # 最大值退休
data[0],data[j]=data[j],data[0] # 退休后,存放在最后的位置 ,堆就少一层
sift(data,0,j-1) # 重新调整堆的顺序.
### 归并排序
def megre(li,low,mid,high)
i=0 # 第一个指针
j=mid+1 # 第二个指针
li_data=[]
while i<=mid and j<=high: # 两边都有数
if li[i]<li[j]:
li_data.append(li[i])
i+=1
else:
li_data.append(li[j])
j+=1
while i<=mid:
li_data.append(li[i])
i+=1
while j<=hight:
li_data.append(li[j])
j+=1
li[low:hiht+1]=li_data # 替换
def megre_sort(li,low,high):
if low <high:
mid=(low+high)//2
megre_sort(li,low,mid) # 递归去拆分数据
megre_sort(li,mid+1,high) # 递归去拆分数据
megre(li,low,mid,high)
#### 快速排序 堆排序 归并排序 对比
# 三种算法的时间复杂度都是O(nlogn)
# 运行时间:快速<归并<堆排
# 特点:
# 快速排序: 极端情况下的效率极低
# 归并排序:需要额外的内存,当数据量大时,不得不考虑内存的空间
# 堆排序: 速度相对较慢,但是稳定
# 插入排序
# -*- coding: utf-8 -*-
def insertion_sort(arr):
for i in range(1, len(arr)):
current = arr[i]
print('current:', current)
pre_index = i - 1
print('pre_index:', pre_index)
while pre_index >= 0 and arr[pre_index] > current:
print('数据值是:', arr[pre_index])
arr[pre_index + 1] = arr[pre_index]
pre_index -= 1
print('变化前:', arr)
print(arr[pre_index + 1])
arr[pre_index + 1] = current
print('变化后:', arr)
print('\n')
return arr
if __name__ == '__main__':
data = [9, 2, 3, 1, 5, 8, 4]
print(insertion_sort(data))