第一部分 小顶堆
#异常类
class BinHeapError(ValueError):
pass
#初始化堆
class BinHeap:
'''采用list储存数据:(若某节点的下标为i)
1.其左子节点的下标为2*i
2.其右子节点的下标为2*i+1
3.其父节点的下标为i//2 '''
def __init__(self, elist):
# 保证下标从1开始
self.data = [0] + elist # [0]无用的数据
'''调用自身的buildHeap函数将elist转换为堆'''
if elist is not None:
self.buildHeap(self.data)
# 判断堆是否为空,返回布尔值
def is_Empty(self):
return self.data == [0]
# 查看堆顶的值
def peek(self):
if self.is_Empty():
raise BinHeapError("in peek")
return self.data[1]
#入堆
def enqueue(self, elem):
"""将elem加入到连续表尾部,然后调用siftup进行一次向上筛选"""
self.data.append(elem)
self.siftup(elem, len(self.data)-1)
#向上筛选
def siftup(self, elem, last):
"""将加入的节点与父节点做比较,如果父节点大于加入的节点,交换两者的数据"""
i, j = last, last//2
#i:当前节点下标 j:当前节点父节点下标
while i > 0 and elem < self.data[j]:
self.data[i],self.data[j] = self.data[j],self.data[i]
'''更新当前节点的下标和当前节点的父节点下标,准备进入第下次循环'''
i, j = j, j//2
#出堆
def dequeue(self):
if self.is_Empty():
raise BinHeapError("in dequeue")
""" 用pop()将尾部数据放置到堆顶,然后调用siftdown进行一次向下筛选
e0:要移除的数据 e:放置在堆顶的数据 """
e0, e = self.data[1], self.data.pop()
self.siftdown(e, 1, len(self.data)-1)
return e0
#向下筛选
def siftdown(self, e, begin, end):
""" 通过向下筛选找到e应该所在位置的下标i
i:当前节点下标 j:当前节点的父节点下标
newList与self.data指向同一个列表 """
newList, i , j = self.data, begin, begin*2
while j <= end: #要带等号
"""比较当前节点的左右子节点大小,获得最小的子节点下标"""
if j+1 <= end and newList[j+1] < newList[j]: #要带等号
j += 1
if e < newList[j]:
#找到插入位置i
break
newList[i] = newList[j]
'''更新当前节点的下标和当前节点的父节点下标,准备进入第下次循环'''
i, j = j, 2*j
newList[i] = e
#调整为堆
def buildHeap(self, list):
""" 从下标为end//2开始,通过向下筛选一个个建堆,时间复杂度nlogn"""
end = len(list) - 1
for i in range(end//2, 0, -1):
self.siftdown(list[i], i, end)
#测试
if __name__ == '__main__':
import random
list=[]
for k in range(10):
list.append(random.randint(0,100))
print(list)
Bt = BinHeap(list)
print(Bt.data, Bt.is_Empty())
Bt.enqueue(10)
print(Bt.data)
第二部分 二叉排序树
在接下来的前根序遍历中采用栈来缓存
from Stack_List import SStack
每一个节点的类(属性)
class TreeNode:
#初始化节点类
def __init__(self, key, val, left=None, right=None, parent=None):
self.key = key
self.payload = val
self.leftChild = left
self.rightChild = right
self.parent = parent
#判断当前节点的连接情况
def hasLeftChild(self):
return self.leftChild
def hasRightChild(self):
return self.rightChild
def isLeftChild(self):
return self.parent and self.parent.leftChild==self
def isRightChild(self):
return self.parent and self.parent.rightChild==self
#判断当前节点的位置
def isRoot(self):
return not self.parent
def isLeaf(self):
return not(self.leftChild or self.rightChild)
def hasAnyChildren(self): #只有一棵子树
return self.leftChild or self.rightChild
def hasBothChildren(self): #有两棵子树
return self.rightChild and self.leftChild
# 更改当前节点的数据
def replaceNodeData(self, key, value, lc, rc):
self.key = key
self.payload = value
self.leftChild = lc
self.rightChild = rc
if self.hasLeftChild(): #为左子节点增加父节点指向
self.leftChild.parent = self
if self.hasRightChild(): #为右子节点增加父节点指向
self.rightChild.parent = self
#前根序遍历
def _Traversal(self, t):
'''能力有限,暂时不会用栈实现中根序遍历'''
st = SStack()
while t is not None or not st.is_Empty():
while t is not None: #沿左支下行
print(t.payload)
st.push(t.rightChild) #右分支入栈
t = t.leftChild
t = st.pop() #回溯
实现二叉排序树
class BinarySearchTree:
# 初始化二叉排序树
def __init__(self):
self.root = None
self.size = 0
#辅助函数
def length(self):
return self.size
def __len__(self):
return self.size
def Traversal(self):
return self.root._Traversal(self.root)
增加数据
def put(self, key, val):
'''有根节点: 调用_put函数
没有根节点:设为根节点'''
if self.root:
self._put(key, val, self.root)
else:
self.root = TreeNode(key, val)
self.size += 1 #节点个数加1
def _put(self, key, val, currentNode):
'''key小于当前节点的key:
当前节点有左子树: 递归调用_put(递归左子树)
当前节点没有左子树:设为当前节点的左子节点
key大于当前节点的key:
当前节点有右子树: 递归调用_put(递归右子树)
当前节点没有右子树:设为当前节点的右子节点'''
if currentNode.key > key:
if currentNode.hasLeftChild():
self._put(key, val, currentNode.leftChild)
else:
currentNode.leftChild=TreeNode(key, val, parent=currentNode)
else:
if currentNode.hasRightChild():
self._put(key, val, currentNode.rightChild)
else:
currentNode.rightChild=TreeNode(key, val, parent=currentNode)
def __setitem__(self, key, value): #索引赋值
'''调用自身的put函数'''
self.put(key, value)
查找数据
def get(self, key):
'''有根节点: 调用_get,找到key对应的res
1.找到了res: 返回res的值
2.没有找到res:返回None
没有根节点:返回None'''
if self.root:
res = self._get(key, self.root)
if res:
return res.payload
else:
return None
else:
return None
def _get(self, key, currentNode):
'''currentNode是空: 返回None
key等于当前节点的key:返回当前节点
key小于当前节点的key:递归调用_get(递归左子树)
key大于当前节点的key:递归调用_get(递归右子树)'''
if not currentNode:
return None
elif key == currentNode.key:
return currentNode
elif key < currentNode.key:
return self._get(key, currentNode.leftChild)
else:
return self._get(key, currentNode.rightChild)
def __getitem__(self, key): #索引
'''调用自身的get函数'''
return self.get(key)
def __contains__(self, key): #相当与in函数
if self._get(key, self.root):
'''调用自身的_get函数'''
return True
else:
return False
删除数据
def delete(self, key):
'''有两个及以上的节点:调用_get找到要删除的节点
1.找到了nodeToRemove: 调用remove函数删除
2.没有找到了nodeToRemove:报错
一个结点(只有根节点):
1.根节点的key等于key:根节点设为None
2.根节点的key不等于key:报错
没有节点:报错'''
if self.size > 1:
nodeToRemove = self._get(key, self.root)
if nodeToRemove:
self.remove(nodeToRemove)
self.size = self.size - 1 #节点个数减1
else:
raise KeyError("Error,key not in tree")
elif self.size==1 and self.root.key==key:
self.root = None
self.size = self.size-1 #节点个数减1
else:
raise KeyError("Error,key not in tree")
def __delitem__(self, key):
'''调用自身的delete函数'''
self.delete(key)
def remove(self, currentNode):
'''1.当前节点是叶节点
2.当前节点有两棵子树
3.当前节点只有一棵子树'''
if currentNode.isLeaf():
'''当前节点是叶节点:
1.当前节点是左子节点:将当前节点的父节点的左树设为None
2.当前节点是右子节点:将当前节点的父节点的有树设为None'''
if currentNode==currentNode.parent.leftChild:
currentNode.parent.leftChild = None
else:
currentNode.parent.rightChild = None
elif currentNode.hasBothChildren():
'''当前节点有两棵子树:
1.调用findSuccessor函数找到当前节点右子树上key最小的节点succ
2.调用spliceOut函数摘除succ
3.将succ替代掉当前节点'''
succ = self.findSuccessor(currentNode)
self.spliceOut(succ)
currentNode.key = succ.key
currentNode.payload = succ.payload
else:
'''当前节点只有一棵子树:
1.当前节点没有父节点:
1.将根节点指向当前节点的左子节点
左子树: 2.当前节点是父节点的左子节点:
1.当前节点的左子节点的父节点指向当前节点的父节点
2.当前节点的父节点的左子树指向当前节点的左子节点
3.当前节点是父节点的右子节点:
1.当前节点的左子节点的父节点指向当前节点的父节点
2.当前节点的父节点的右子树指向当前节点的左子节点'''
if currentNode.hasLeftChild():
if currentNode.isRoot():
self.root = currentNode.leftChild
elif currentNode.isLeftChild():
currentNode.leftChild.parent = currentNode.parent
currentNode.parent.leftChild = currentNode.leftChild
else:
currentNode.leftChild.parent = currentNode.parent
currentNode.parent.rightChild = currentNode.leftChild
else:
'''1.当前节点没有父节点:
1.将根节点指向当前节点的右子节点
右子树: 2.当前节点是父节点的左子节点:
1.当前节点的右子节点的父节点指向当前节点的父节点
2.当前节点的父节点的左子树指向当前节点的右子节点
3.当前节点是父节点的右子节点:
1.当前节点的右子节点的父节点指向当前节点的父节点
2.当前节点的父节点的右子树指向当前节点的右子节点'''
if currentNode.isRoot():
self.root = currentNode.rightChild
elif currentNode.isLeftChild():
currentNode.rightChild.parent = currentNode.parent
currentNode.parent.leftChild = currentNode.rightChild
else:
currentNode.rightChild.parent = currentNode.parent
currentNode.parent.rightChild = currentNode.rightChild
def findSuccessor(self, currentNode): #当前节点有两棵子树
current = currentNode.rightChild
while current.hasLeftChild(): #一直往左走,找到key最小的current
current = current.leftChild
return current
def spliceOut(self, succ): #当前节点有两棵子树
'''移除的节点要不没有子节点,要不只有右子树
移除的节点是左子节点:
1.移除的节点是叶节点:移除节点的父节点的左子树设为None
2.移除的节点是右节点:
1.移除节点的右子节点的父节点指向移除节点的父节点
2.移除节点的父节点的左子树指向移除节点的右子树'''
if succ.isLeftChild():
if succ.isLeaf():
succ.parent.leftChild = None
else:
succ.rightChild.parent = succ.parent
succ.parent.leftChild = succ.rightChild
else:
'''移除的节点是右子节点:
1.移除的节点是叶节点:移除节点的父节点的右子树设为None
2.移除的节点是右节点:
1.移除节点的右子节点的父节点指向移除节点的父节点
2.移除节点的父节点的右子树指向移除节点的右子树'''
if succ.isLeaf():
succ.parent.rightChild = None
else:
succ.rightChild.parent = succ.parent
succ.parent.rightChild = succ.rightChild
测试用例(建议读者自己设置测试用例)
if __name__ == '__main__':
# import random
# list = []
# for i in range(10):
# list.append(random.randint(0,100))
# print(list)
BT = BinarySearchTree()
for i in [57,36,89,7,43,65,96,18,52,60,74]:
BT.__setitem__(i, i)
# if BT.__contains__(10):
# print(BT.__getitem__(10))
# BT.__delitem__(57)
BT.Traversal()
第三部分 递归迷宫
list1 = [1,1,1,1,1,1,1,1,1,1,1,1,1,1]
list2 = [1,0,0,0,1,1,0,0,0,1,0,0,0,1]
list3 = [1,0,1,0,0,0,0,1,0,1,0,1,0,1]
list4 = [1,0,1,0,1,1,1,1,0,1,0,1,0,1]
list5 = [1,0,1,0,0,0,0,0,0,1,1,1,0,1]
list6 = [1,0,1,1,1,1,1,1,1,1,0,0,0,1]
list7 = [1,0,1,0,0,0,0,0,0,0,0,1,0,1]
list8 = [1,0,0,0,1,1,1,0,1,0,1,1,0,1]
list9 = [1,0,1,0,1,0,1,0,1,0,1,0,0,1]
list10= [1,0,1,0,1,0,1,0,1,1,1,1,0,1]
list11= [1,0,1,0,0,0,1,0,0,1,0,0,0,1]
list12= [1,1,1,1,1,1,1,1,1,1,1,1,1,1]
maze = [list1,list2,list3,list4,list5,list6,list7,list8,list9,list10,list11,list12]
dirs = [(0,1), (1,0), (0,-1), (-1,0)] #0右 1下 2左 3上
def mark(maze, pos): #标记已经走过的路
maze[pos[0]][pos[1]] = 2
def passable(maze, pos): #检查是否可以行进
return maze[pos[0]][pos[1]] == 0
#基于栈的迷宫求解算法
from Stack import Stack
def maze_solver(maze, start, end):
if start == end:
print(start)
return
st = Stack()
mark(maze, start)
st.push((start, 0))
while not st.isEmpty():
pos, nxt = st.pop() #出栈
for i in range(nxt, 4): #0右 1下 2左 3上
nextp = (pos[0] + dirs[i][0],pos[1] + dirs[i][1])
if nextp == end:
st.push((pos,i))
st.push(end)
print(st.elem)
return
if passable(maze, nextp):
st.push((pos, i)) #记录该点的行进方向
mark(maze, nextp) #标记已经走过的点
st.push((nextp, 0)) #下一个点入栈
break
print('No path found')
maze_solver(maze, (1,1), (10,12))