您的位置:首页 > 理论基础 > 数据结构算法

python实现基础的数据结构(二)

2019-09-15 20:01 148 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/qq_36949074/article/details/100862494

本文涉及到的代码均已放置在我的github中 -->链接

python实现基础的数据结构(一)
python实现基础的数据结构(二)
python实现基础的数据结构(三)

python语法较为简洁,正好最近在复习数据结构,索性就用python去实现吧😀
本文实现的有线性表、栈、队列、串、二叉树、图、排序算法。参照教材为

数据结构(C语言版)清华大学出版社
,还有网上的一些大神的见解。由于包含代码在内,所以内容很多,分了几篇,话不多说,我们直接步入正题🙈

注:本文实验环境:Anaconda 集成 python 3.6.5 ( 故代码为 python3 )
由于本文代码过多,故每一步不做太详细的介绍,我尽量在代码中解释细节,望体谅

串是由零个或者多个字符组成的优先序列,一般记为是s = ‘a1a2…an’ ,python对字符串的操作已经比较成熟和简单,所以我们这里着重介绍串的模式匹配算法,这就不得不提KMP匹配算法,这种算法和正则表达式有类似之处,一个用了DFA的思想,一个用了NFA的思想。
KMP算法的思想是,当匹配失败时,可以利用已经知晓的一部分文本内容,避免从头开始重新匹配。这个匹配的过程可以使用有限状态自动机(DFA)。可以看看这位大神的描述。
每当一趟匹配过程中出现字符比较不等时,不需要回溯I指针,而是利用已经的带的“部分匹配”的结果将模式向右滑动尽可能远的一段距离后,继续进行比较。
即尽量利用已经部分匹配的结果信息,尽量让i不要回溯,加快模式串的滑动速度。当然咱会用就行了🤣,附上代码:

#串  python中串较为简单故这里只说重要的串的模式匹配
#串的KMP算法

def kmp(mom_string,son_string):

# 传入一个母串和一个子串
if type(mom_string) is not str or type(son_string) is not str:
return '其中有不为字符串的'
if len(son_string) == 0:
return '子串为空'
if len(mom_string) == 0:
return '母串为空'

#求next数组
next = [-1]*len(son_string)
if len(son_string)>1:
next[1] = 0
i,j = 1,0
while i < len(son_string)-1:
if j == -1 or son_string[i] == son_string[j]:
i += 1
j += 1
next[i] = j
else:
j=next[j]

# kmp框架
m = s = 0                #母指针和子指针初始化为0
while(s < len(son_string) and m < len(mom_string)):
# 匹配成功,或者遍历完母串匹配失败退出
if s == -1 or mom_string[m] == son_string[s]:
m += 1
s += 1
else:
s = next[s]

if s == len(son_string):#匹配成功
return m-s
return '匹配失败'

# 测试
mom_string='ababababca'
son_string='babd'
print('母串为:' + str(mom_string))
print('子串为:' + str(son_string))
try:
n=kmp(mom_string,son_string)+1
print('子串出现在母串的第' + str(n) + '位')
except:
print(kmp(mom_string,son_string))

输出结果为:

母串为:ababababca
子串为:babd
匹配失败

二叉树

二叉树是一种树形结构,特点是每个节点之多只有两颗子树,而且有左右之分,次序不能任意颠倒,我们这里重点说一说二叉树的七种遍历,前序遍历、中序遍历、后序遍历和层次遍历(前三种有递归和非递归),二叉树比较熟悉,不多介绍,上代码:

#树  重点是二叉树
#二叉树的遍历(递归和非递归(堆栈实现))
class Node(object):
"""节点类"""
def __init__(self, elem=-1, lchild=None, rchild=None): #elem初始化为-1 用于判断是否为空树
self.elem = elem        #根节点
self.lchild = lchild    #左子树
self.rchild = rchild    #右子树

class Tree(object):
"""树类"""
def __init__(self):
self.root = Node()  #树的节点
self.myQueue = []

def add(self, elem):
"""为树添加节点"""
node = Node(elem)
if self.root.elem == -1:  # 如果树是空的,则对根节点赋值
self.root = node
self.myQueue.append(self.root)
else:
treeNode = self.myQueue[0]  # 此结点的子树还没有齐。
if treeNode.lchild == None:
treeNode.lchild = node
self.myQueue.append(treeNode.lchild)
else:
treeNode.rchild = node
self.myQueue.append(treeNode.rchild)
self.myQueue.pop(0)  # 如果该结点存在右子树,将此结点丢弃。

def front_digui(self, root):
"""利用递归实现树的先序遍历"""
if root == None:
return
print (root.elem)
self.front_digui(root.lchild)
self.front_digui(root.rchild)

def middle_digui(self, root):
"""利用递归实现树的中序遍历"""
if root == None:
return
self.middle_digui(root.lchild)
print (root.elem)
self.middle_digui(root.rchild)

def later_digui(self, root):
"""利用递归实现树的后序遍历"""
if root == None:
return
self.later_digui(root.lchild)
self.later_digui(root.rchild)
print (root.elem)

def front_stack(self, root):
"""利用堆栈实现树的先序遍历"""
if root == None:
return
myStack = []
node = root
while node or myStack:
while node:                     #从根节点开始,一直找它的左子树
print (node.elem)
myStack.append(node)
node = node.lchild
node = myStack.pop()            #while结束表示当前节点node为空,即前一个节点没有左子树了
node = node.rchild                  #开始查看它的右子树

def middle_stack(self, root):
"""利用堆栈实现树的中序遍历"""
if root == None:
return
myStack = []
node = root
while node or myStack:
while node:                     #从根节点开始,一直找它的左子树
myStack.append(node)
node = node.lchild

node = myStack.pop()            #while结束表示当前节点node为空,即前一个节点没有左子树了
print (node.elem)
node = node.rchild                  #开始查看它的右子树

def later_stack(self, root):
"""利用堆栈实现树的后序遍历"""
if root == None:
return
myStack1 = []
myStack2 = []
node = root
myStack1.append(node)
while myStack1:                   #这个while循环的功能是找出后序遍历的逆序,存在myStack2里面
node = myStack1.pop()
if node.lchild:
myStack1.append(node.lchild)
if node.rchild:
myStack1.append(node.rchild)
myStack2.append(node)
while myStack2:                         #将myStack2中的元素出栈,即为后序遍历次序
print (myStack2.pop().elem)

def level_queue(self, root):
"""利用队列实现树的层次遍历"""
if root == None:
return
myQueue = []
node = root
myQueue.append(node)
while myQueue:
node = myQueue.pop(0)
print (node.elem)
if node.lchild != None:
myQueue.append(node.lchild)
if node.rchild != None:
myQueue.append(node.rchild)

if __name__ == '__main__':
"""主函数"""
elems = range(10)           #生成十个数据作为树节点
tree = Tree()          #新建一个树对象
for elem in elems:
tree.add(elem)           #逐个添加树的节点
print ('二叉树的操作:')
print ('队列实现层次遍历:')
tree.level_queue(tree.root)

print ('递归实现先序遍历:')
tree.front_digui(tree.root)
print ('递归实现中序遍历:' )
tree.middle_digui(tree.root)
print ('递归实现后序遍历:')
tree.later_digui(tree.root)

print ('堆栈实现先序遍历:')
tree.front_stack(tree.root)
print ('堆栈实现中序遍历:')
tree.middle_stack(tree.root)
print ('堆栈实现后序遍历:')
tree.later_stack(tree.root)

输出结果为:

二叉树的操作:
队列实现层次遍历:
0
1
2
3
4
5
6
7
8
9
递归实现先序遍历:
0
1
3
7
8
4
9
2
5
6
递归实现中序遍历:
7
3
8
1
9
4
0
5
2
6
递归实现后序遍历:
7
8
3
9
4
1
5
6
2
0
堆栈实现先序遍历:
0
1
3
7
8
4
9
2
5
6
堆栈实现中序遍历:
7
3
8
1
9
4
0
5
2
6
堆栈实现后序遍历:
7
8
3
9
4
1
5
6
2
0

在计算机科学中,一个图就是一些顶点的集合,这些顶点通过一系列边结对(连接)。顶点用圆圈表示,边就是这些圆圈之间的连线。顶点之间通过边连接。类似于这种

在图里面主要有两部分,图的遍历和最短路径算法

图的遍历

图的遍历有三种算法,DFS(递归和堆栈)和BFS。
DFS是深度优先搜索,类似于二叉树里面的先序遍历,其实就是暴力把所有的路径都搜索出来,它运用了回溯,保存这次的位置,深入搜索,都搜索完了便回溯回来,搜下一个位置,直到把所有最深位置都搜一遍。
BFS是广度优先搜索,类似与二叉树的层序遍历,从某点开始,走四面可以走的路,然后在从这些路,在找可以走的路,直到最先找到符合条件的,这个运用需要用到队列(queue),需要稍微掌握这个才能用BFS。

#图的遍历  深度优先(递归和堆栈)和广度优先

def BFS(graph, s):    #广度优先与二叉树的层序遍历类似
'''广度优先'''
queue = []
result = []
queue.append(s)
seen = set()
seen.add(s)
while len(queue) > 0:
vertex = queue.pop(0)
nodes = graph[vertex]
for node in nodes:
if node not in seen:
queue.append(node)
seen.add(node)
result.append(vertex)
return result

def DFS(graph, s):    #深度优先与二叉树的先序遍历类似
'''深度优先堆栈法'''
result = []
stack = []
stack.append(s)
seen = set()
seen.add(s)
while len(stack) > 0:
vertex = stack.pop()
nodes  = graph[vertex]
for node in nodes:
if node not in seen:
stack.append(node)
seen.add(node)
result.append(vertex)
return result

def DFS1(graph, s, queue=[]):
'''深度优先递归法'''
queue.append(s)
for i in graph[s][::-1]:
if i not in queue:
DFS1(graph, i, queue)
return queue

graph = {
'a' : ['b', 'c'],
'b' : ['a', 'c', 'd'],
'c' : ['a','b', 'd','e'],
'd' : ['b' , 'c', 'e', 'f'],
'e' : ['c', 'd'],
'f' : ['d']
}
print('该图为:')
print(graph,'\n')
print('从a出发的广度优先搜索结果:')
print(BFS(graph, 'a'),'\n')
print('从a出发的深度优先搜索结果(堆栈):')
print(DFS(graph, 'a'),'\n')
print('从a出发的深度优先搜索结果(递归):')
print(DFS1(graph, 'a'))

输出结果为:

该图为:
{'a': ['b', 'c'], 'b': ['a', 'c', 'd'], 'c': ['a', 'b', 'd', 'e'], 'd': ['b', 'c', 'e', 'f'], 'e': ['c', 'd'], 'f': ['d']}

从a出发的广度优先搜索结果:
['a', 'b', 'c', 'd', 'e', 'f']

从a出发的深度优先搜索结果(堆栈):
['a', 'c', 'e', 'd', 'f', 'b']

从a出发的深度优先搜索结果(递归):
['a', 'c', 'e', 'd', 'f', 'b']

最短路径

用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。算法最具有代表性的有两种,Dijkstra算法和Floyd算法,前者由于遍历的节点更多,效率比较低,后者是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,虽然它不返回路径本身的细节,但是可以通过对算法的简单修改来重建路径。看代码(python里面的inf是无穷大的意思):

#图  最短路径问题,包含dijkstra、Floyd两种算法

inf = float('inf')
matrix_distance = [[0,1,12,inf,inf,inf],
[inf,0,9,3,inf,inf],
[inf,inf,0,inf,5,inf],
[inf,inf,4,0,13,15],
[inf,inf,inf,inf,0,4],
[inf,inf,inf,inf,inf,0]]

def dijkstra(matrix_distance, source_node):
inf = float('inf')
# init the source node distance to others
dis = matrix_distance[source_node]
node_nums = len(dis)
flag = [0 for i in range(node_nums)]
flag[source_node] = 1
for i in range(node_nums-1):
min = inf
#find the min node from the source node
for j in range(node_nums):  #比较相邻节点的最短路径
if flag[j] == 0 and dis[j] < min:
min = dis[j]
u = j

flag[u] = 1  #访问过的置为1
#update the dis
for v in range(node_nums):
if flag[v] == 0 and matrix_distance[u][v] < inf:
if dis[v] > dis[u] + matrix_distance[u][v]:
dis[v] = dis[u] + matrix_distance[u][v]
return dis

def Floyd(dis):
#min (Dis(i,j) , Dis(i,k) + Dis(k,j) )
nums_vertex = len(dis[0])
for k in range(nums_vertex):
for i in range(nums_vertex):
for j in range(nums_vertex):
if dis[i][j] > dis[i][k] + dis[k][j]:
dis[i][j] = dis[i][k] + dis[k][j]
return dis

print('该有向图为:')
for i in matrix_distance:
for j in i:
print (j, end = '  ')
print()
print()
print('从v1节点出发的到各个点的最短路径(dijkstra算法)')
print(dijkstra(matrix_distance, 0))
print('从每个点依次出发到各个点对应的所有最短路径(Floyd算法)')
print(Floyd(matrix_distance))

输出结果为:

该有向图为:
0  1  12  inf  inf  inf
inf  0  9  3  inf  inf
inf  inf  0  inf  5  inf
inf  inf  4  0  13  15
inf  inf  inf  inf  0  4
inf  inf  inf  inf  inf  0

从v1节点出发的到各个点的最短路径(dijkstra算法)
[0, 1, 8, 4, 13, 17]
从每个点依次出发到各个点对应的所有最短路径(Floyd算法)
[[0, 1, 8, 4, 13, 17], [inf, 0, 7, 3, 12, 16], [inf, inf, 0, inf, 5, 9], [inf, inf, 4, 0, 9, 13], [inf, inf, inf, inf, 0, 4], [inf, inf, inf, inf, inf, 0]]

下一篇内容:十种排序算法
python实现基础的数据结构(三)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: