• 数据结构


     数据结构基础

     
    数据结构
    
      定义:简单来说,数据结构就是设计数据以何种方式组织并存储在计算机中。比如:列表、集合与字典等都是一种数据结构。
    
      PS:“程序=数据结构+算法”
    
      列表:在其他编程语言中称为“数组”,是一种基本的数据结构类型。
        关于:列表的存储问题!
    数据结构的分类:
      线性结构:数据结构中的元素存在一对一的相互关系
      树结构:数据结构中的元素存在一对多的相互关系
      图结构:数据结构中的元素存在多对多的相互关系
    栈:   栈(Stack)是一个数据集合,可以理解为只能在一端进行插入或删除操作的列表。   栈的特点:后进先出(last
    -in, first-out)   栈的概念:     栈顶     栈底   栈的基本操作:     进栈(压栈):push     出栈:pop     取栈顶:gettop
    复制代码

    复制代码
    #利用python简单的实现栈操作
    class
    Stack(object): def __init__(self): self.stack=[] def isEmpty(self): return self.stack==[] def push(self,item): self.stack.append(item) def pop(self): if self.isEmpty(): raise IndexError,'pop from empty stack' return self.stack.pop() def peek(self): return self.stack[-1] def size(self): return len(self.stack)
    复制代码
    复制代码
    栈的应用——括号匹配问题
    括号匹配问题:给一个字符串,其中包含小括号、中括号、大括号,求该字符串中的括号是否匹配。
    例如:
      ()()[]{}    匹配
      ([{()}])    匹配
      [](    不匹配
      [(])    不匹配
    复制代码
    复制代码
    def Matching(exp="{[()]}"):
        stack = []
        for i in exp:
            if i in {'(','[','{'}:
                stack.append(i)
            if i == ')':
                if len(stack)>0 and stack[-1] == '(':
                    stack.pop()
                else:
                    return False
            if i == ']':
                if len(stack)>0 and stack[-1] == '[':
                    stack.pop()
                else:
                    return False
            if i == '}':
                if len(stack)>0 and stack[-1] == '{':
                    stack.pop()
                else:
                    return False
        if len(stack)==0:
            return True
        else:
            return False
    复制代码
    复制代码
    队列
      队列(Queue)是一个数据集合,仅允许在列表的一端进行插入,另一端进行删除。
        进行插入的一端称为队尾(rear),插入动作称为进队或入队
        进行删除的一端称为队头(front),删除动作称为出队
      队列的性质:先进先出(First-in, First-out)
    
      双向队列:队列的两端都允许进行进队和出队操作。
    复制代码

     

    复制代码
    队列实现:
      使用方法:from collections import deque
      创建队列:queue = deque(li)
      进队:append
      出队:popleft
      双向队列队首进队:appendleft
      双向队列队尾进队:pop
    
    队列的实现原理
      普通队列:
        初步设想:列表+两个下标指针
        创建一个列表和两个变量,front变量指向队首,rear变量指向队尾。初始时,front和rear都为0。
        进队操作:元素写到li[rear]的位置,rear自增1。
        出队操作:返回li[front]的元素,front自减1。
    复制代码

    复制代码
     环形队列:
        改进方案:将列表首尾逻辑上连接起来。
        环形队列:当队尾指针front == Maxsize + 1时,再前进一个位置就自动到0。
        实现方式:求余数运算
        队首指针前进1:front = (front + 1) % MaxSize
        队尾指针前进1:rear = (rear + 1) % MaxSize
        队空条件:rear == front
        队满条件:(rear + 1) % MaxSize == front
    复制代码


     队列的内置模块:

    复制代码
    使用方法:from collections import deque
        创建队列:queue = deque(li)
        进队:append
        出队:popleft
        双向队列队首进队:appendleft
        双向队列对尾出队:pop
    复制代码

     链表:

    复制代码
    链表中的每一个元素都是一个对象,每个对象称之为一个节点,包含有数据域key和指向下一个节点的指针next。通过各个节点之间的相互连接,最终串联成一个链表。
    节点定义:
     class Node(object):
        def __init__(self,item):
            self.item = item
            self.next = None
    复制代码

    建立链表有两种方式(头插法和尾插法):

    复制代码
    链表可以有遍历操作:
    链表节点的插入和删除:
    1、插入
        p.next = curNode.next     #要插入的下一个为curNode的下一个,也就是3
        curNode.next = p          #curNode的下一个为p,也就是刚刚插入的那个,
    2、删除
        p = curNode.next       #p为curNode的下一个节点
        curNode.next = curNode.next.next    #curNode的下一个节点=curNode.next.next(变相的赋值)
        del p    #最后删除中间那个碍眼的家伙
    复制代码

    双向链表:

    复制代码
    定义:双链表中的每个节点都有两个指针:一个指向后边的节点、一个指向前面的节点。
    代码定义:
    class Node(object):
        def __init__(self,item=None):
            self.item = item      #当前节点
            self.next = None     #下一个
            self.prior = None     #前一个
    复制代码

    复制代码
    双链表节点的插入和删除(感觉很绕)
    插入:
        p.next = curNode.next    #p的下一个=curNode的下一个
        curNode.next.prior = p    #curNode的下一个的前一个=p
        p.prior = curNode          #q的前一个就是curNode
        curNode.next = p            #之后curNode的下一个就是p
    删除:
        p = curNode.next             #p是curNode的下一个
        curNode.next = p.next          #赋值   curNode的下一个就是p的下一个
        p.next.prior = curNode          #p的下一个的前一额就是curNode
        del p                            #删除p,赋值成功
    复制代码

    复制代码
    链表-复杂度分析:
    列表与链表:
        暗元素值查找
        按下表查找
        在某元素后插入
        删除某元素
    复制代码

    哈希表:

    复制代码
    哈希表(hash table,又称为散列表),是一种线性表的存储结构。哈希表有一个顺序表(也就是数组)和一个哈希函数组成(哈希函数就是加密算法,顺序表就是加密后的值)。
    哈希函数h(k) 将元素k作为自变量,返回元素的存储下标。 简单的哈希函数: 除法哈希:h(k)
    = k mod m #对m取余 乘法哈希:h(k) = floor(m(kA mod 1)) 0<A<1
    复制代码

    假设有一个长度为7的数组,河西函数h(k) = K%7.元素集合{14,22,3,5}的存储方式如下图(制作图片链接https://visualgo.net/en)。

    复制代码
    哈希冲突:
        由于哈希表的大小是有限的,而要存储的值得总数量是无线的,因此对于任何哈希函数,都会出现两个不同的元素映射到同一个位置上的情况,这种情况就叫做哈希冲突。
    解决哈希冲突--开放寻址法:
        开放寻址法:如果哈希函数返回的位置已经有值,则可以向后探查新的位置来存储这个值。
            线性探查:如果位置i被占用,则探查i+1,i+2·····
            二次探查:如果位置i被占用,则探查i+1²,i-1²,i+2²···
            二度哈希:有n个哈希函数,当使用第一个哈希函数h1发生冲突时,则尝试使用h3,h3等
    拉链法:哈希表每个位置都链接一个链表,当冲突发生时,冲突的元素讲被加到该位置链表的最后(图示略)

    复制代码

    哈希表在python中的应用

    字典与集合都是通过哈希表来实现的
    在python中的字典;
        ls = {"name":"username","password":"password"}
    使用哈希表存储字典,通过哈希函数将字典的键映射为下标
    在字典键值对数量不多的情况下,几乎不会发生类似于哈希冲突,此时查找一个元素的时间复杂度为O(1)

    二叉树:

    复制代码
    二叉树的链式存储:将二叉树的节点定义为一个对象,节点之间通过类似链表的链接方式来链接。
    节点定义:
     class BiTreeNode(object):
        def __init__(self,data):
            self.data = data
            self.lchild = None      #左边的孩纸
            self.rchild = None     #右边的孩纸
    二叉树的遍历方式:
        前序遍历:
        中序遍历:
        后序遍历:
        层次遍历:

    from collections import deque

    
    


    class BiTreeNode:
      def __init__(self, data):
        self.data = data
        self.lchild = None
        self.rchild = None

    
    

    a = BiTreeNode('A')
    b = BiTreeNode('B')
    c = BiTreeNode('C')
    d = BiTreeNode('D')
    e = BiTreeNode('E')
    f = BiTreeNode('F')
    g = BiTreeNode('G')

    
    

    e.lchild = a
    e.rchild = g
    a.rchild = c
    c.lchild = b
    c.rchild = d
    g.rchild = f

    
    

    root = e

    
    

    #前序遍历
    def pre_order(root):
    if root:
    print(root.data, end='')
    pre_order(root.lchild)
    pre_order(root.rchild)
    #中序遍历
    def in_order(root):
    if root:
    in_order(root.lchild)
    print(root.data, end='')
    in_order(root.rchild)

    
    

    #后续遍历
    def post_order(root):
    if root:
    post_order(root.lchild)
    post_order(root.rchild)
    print(root.data, end='')

    
    

    #层次遍历
    def level_order(root):
    queue = deque()
    queue.append(root)
    while len(queue) > 0:
    node = queue.popleft()
    print(node.data,end='')
    if node.lchild:
    queue.append(node.lchild)
    if node.rchild:
    queue.append(node.rchild)

    pre_order(root)
    print("")
    in_order(root)
    print("")
    post_order(root)
    print("")
    level_order(root)

    遍历完之后比对他们的打印结果你会发现都是不同的
    复制代码

    二叉搜索树:

    复制代码
    二叉搜索树是一颗二叉树且满足性质:
    设x是二叉树的一个节点。如果y是x左子树的一个节点,那么y.key<= x.key;
    如果y是x右子树的一个节点,那么y.key>=x.key.
    二叉搜索树的创建
    二叉搜索树的遍历(采用中序序列遍历)
    二叉搜索树的查询、插入、删除操作
    ps:随机化的二叉搜索树,AVL树
    复制代码

    AVL树:avl树是一个自平衡的二叉搜索树。

    具有的性质:

            根的左右子树的高度之差的绝对值不能超过1

            根的左右子树都是平衡二叉树

    AV领的实现方式:旋转(在我上边留的网站自己测试,很诡异,了解即可):

    B树(B-Tree):B树是一颗自平衡的多路搜索树。常用于数据库的索引。

       

    python中的位运算符:

    位运算符要比乘除法更快,深得广大程序员的喜爱

    数据结构-实例代码

    栈的应用-迷宫问题

    给一个二维列表,表示迷宫(0表示通道,1表示围墙),给出算法,求一条走出迷宫的路径。

    复制代码
    from collections import deque
    两种思路:基于堆栈、基于队列
    基于堆栈:在一个迷宫节点(x,y)上,可以进行四个方向的探查:maze[x-1][y],maze[x+1][y],maze[x][y-1],maze[x][y+1]
    解决思路:从一个节点开始,任意找下一个可以走的点,当找不到的时候,退回上一个点寻找其他方向的点
    方法:创建一个空的栈,首先从入口位置进栈,当栈不空的时候循环:获取栈顶元素,寻找下一个可以走的相邻
    的方块,说明当前的额位置是死胡同,进行回溯(就是讲当前的位置出栈,看前边的点有没有可以走的路)
      
    基于队列:
    思路:从一个节点开始,寻找所有下面能继续走的点,继续寻找,知道找到出口。
    方法:创建一个空的队列,从起点位置进队,在队列不为空的时候循环:出队一次。如果当前位置为出口,结束算法 否则找出当前方块的四个相邻的方块中可以走的方块,全部进队。
    maze
    = [ [1,1,1,1,1,1,1,1,1,1], [1,0,0,1,0,0,0,1,0,1], [1,0,0,1,0,0,0,1,0,1], [1,0,0,0,0,1,1,0,0,1], [1,0,1,1,1,0,0,0,0,1], [1,0,0,0,1,0,0,0,0,1], [1,0,1,0,0,0,1,0,0,1], [1,0,1,1,1,0,1,1,0,1], [1,1,0,0,0,0,0,0,0,1], [1,1,1,1,1,1,1,1,1,1] ] dirs = [ lambda x,y:(x-1,y), # lambda x,y:(x,y+1), # lambda x,y:(x+1,y), # lambda x,y:(x,y-1), # ] def solve_maze(x1, y1, x2, y2):
      """基于栈实现""" stack
    = [] stack.append((x1,y1)) maze[x1][y1] = 2 while len(stack) > 0: # 当栈不空循环 cur_node = stack[-1] if cur_node == (x2,y2): #到达终点 for p in stack: print(p) return True for dir in dirs: next_node = dir(*cur_node) if maze[next_node[0]][next_node[1]] == 0: #找到一个能走的方向 stack.append(next_node) maze[next_node[0]][next_node[1]] = 2 # 2表示已经走过的点 break else: #如果一个方向也找不到 stack.pop() else: print("无路可走") return False def solve_maze2(x1,y1,x2,y2):
    """基于队列来实现""" queue
    = deque() path = [] # 记录出队之后的节点 queue.append((x1,y1,-1)) maze[x1][y1] = 2 while len(queue) > 0: cur_node = queue.popleft() path.append(cur_node) if cur_node[0] == x2 and cur_node[1] == y2: #到终点 real_path = [] x,y,i = path[-1] real_path.append((x,y)) while i >= 0: node = path[i] real_path.append(node[0:2]) i = node[2] real_path.reverse() for p in real_path: print(p) return True for dir in dirs: next_node = dir(cur_node[0], cur_node[1]) if maze[next_node[0]][next_node[1]] == 0: queue.append((next_node[0], next_node[1], len(path)-1)) maze[next_node[0]][next_node[1]] = 2 # 标记为已经走过 else: print("无路可走") return False solve_maze2(1,1,8,8) #输入起点的位置和终点的位置
    复制代码

    。。。。。。。。。。有时间更新

  • 相关阅读:
    python-44-初识队列
    python-43-进程锁/信号量/事件
    python-42-Process多进程
    python-41-初识hmac与socketserver模块
    python-40-初识socket与struct
    python-39-hashlib与logging模块
    python-38-用于面向对象的内置函数
    python-37-各种反射
    python-36-封装与面向对象函数
    python-35-多态与初识封装
  • 原文地址:https://www.cnblogs.com/guoxiaoyan/p/9439888.html
Copyright © 2020-2023  润新知