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

本文涉及到的代码均已放置在我的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实现基础的数据结构(三)

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
使用 JavaScript 编写的记忆游戏(附源代码)   项目:JavaScript 记忆游戏(附源代码) 记忆检查游戏是一个使用 HTML5、CSS 和 JavaScript 开发的简单项目。这个游戏是关于测试你的短期 记忆技能。玩这个游戏 时,一系列图像会出现在一个盒子形状的区域中 。玩家必须找到两个相同的图像并单击它们以使它们消失。 如何运行游戏? 记忆游戏项目仅包含 HTML、CSS 和 JavaScript。谈到此游戏的功能,用户必须单击两个相同的图像才能使它们消失。 点击卡片或按下键盘键,通过 2 乘 2 旋转来重建鸟儿对,并发现隐藏在下面的图像! 如果翻开的牌面相同(一对),您就赢了,并且该对牌将从游戏中消失! 否则,卡片会自动翻面朝下,您需要重新尝试! 该游戏包含大量的 javascript 以确保游戏正常运行。 如何运行该项目? 要运行此游戏,您不需要任何类型的本地服务器,但需要浏览器。我们建议您使用现代浏览器,如 Google Chrome 和 Mozilla Firefox, 以获得更好、更优化的游戏体验。要玩游戏,首先,通过单击 memorygame-index.html 文件在浏览器中打开游戏。 演示: 该项目为国外大神项目,可以作为毕业设计的项目,也可以作为大作业项目,不用担心代码重复,设计重复等,如果需要对项目进行修改,需要具备一定基础知识。 注意:如果装有360等杀毒软件,可能会出现误报的情况,源码本身并无病毒,使用源码时可以关闭360,或者添加信任。
使用 JavaScript 编写的 Squareshooter 游戏及其源代码   项目:使用 JavaScript 编写的 Squareshooter 游戏(附源代码) 这款游戏是双人游戏。这是一款使用 JavaScript 编写的射击游戏,带有门户和强化道具。在这里,每个玩家都必须控制方形盒子(作为射手)。这款射击游戏的主要目标是射击对手玩家以求生存。当它射击对手时,它会获得一分。 游戏制作 该游戏仅使用 HTML 和 JavaScript 开发。该游戏的 PC 控制也很简单。 对于玩家 1: T:朝你上次动作的方向射击 A:向左移动 D:向右移动 W:向上移动 S:向下移动 对于玩家2: L:朝你上次移动的方向射击 左箭头:向左移动 右箭头:向右移动 向上箭头:向上移动 向下箭头:向下移动 游戏会一直进行,直到您成功射击对手或对手射击您为止。游戏得分显示在顶部。所有游戏功能均由 JavaScript 设置,而布局和其他次要功能则由 HTML 设置。 如何运行该项目? 要运行此项目,您不需要任何类型的本地服务器,但需要浏览器。我们建议您使用现代浏览器,如 Google Chrome 和 Mozilla Firefox。要运行此游戏,首先,通过单击 index.html 文件在浏览器中打开项目。 演示: 该项目为国外大神项目,可以作为毕业设计的项目,也可以作为大作业项目,不用担心代码重复,设计重复等,如果需要对项目进行修改,需要具备一定基础知识。 注意:如果装有360等杀毒软件,可能会出现误报的情况,源码本身并无病毒,使用源码时可以关闭360,或者添加信任。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值