zoukankan      html  css  js  c++  java
  • Day 75 数据结构

    数据结构

    数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成。 简单来说,数据结构就是设计数据以何种方式组织并存储在计算机中。 比如:列表、集合与字典等都是一种数据结构。 N.Wirth: “程序=数据结构+算法”。

    数据结构的分类

    数据结构按照其逻辑结构可分为线性结构、树结构、图结构

    线性结构:数据结构中的元素存在一对一的相互关系

    树结构:数据结构中的元素存在一对多的相互关系

    图结构:数据结构中的元素存在多对多的相互关系

    使用一般的列表结构即可实现栈

    进栈:li.append

    出栈:li.pop

    取栈顶:li[-1]

    栈的应用——括号匹配问题

    括号匹配问题:给一个字符串,其中包含小括号、中括号、大括号,求该字符串中的括号是否匹配。
    例如:

    ()()[]{} 匹配

    ([{()}]) 匹配

    []( 不匹配

    [(]) 不匹配

    def match(s):
        match = {'}': '{', ']': '[', ')': '('}
        li = []
        for ch in s:
            if ch in {'{', '(', '['}:
                li.append(ch)
            else:  # ch in {'}',')',']'}
                if len(li) == 0:
                    return False
                elif len(li) > 0 and li[-1] == match[ch]:
                    li.pop()
                else:  # li[-1] not in match
                    return False
        if len(li):
            return False
        else:
            return True
    
    
    print(match('{[{()}]}'))

    队列

    队列(Queue)是一个数据集合,仅允许在列表的一端进行插入,另一端进行删除。

    进行插入的一端称为队尾(rear),插入动作称为进队或入队

    进行删除的一端称为队头(front),删除动作称为出队

    队列的性质:先进先出(First-in, First-out)

    class Queue:
        def __init__(self, size=100):
            self.size = size
            self.queue = [0 for _ in range(100)]
            self.rear = 0  # 队尾指针
            self.front = 0  # 队首指针
    
        def push(self, element):
            if not self.is_filled():
                self.rear = (self.rear + 1) % self.size
                self.queue[self.rear] = element
            else:
                raise ImportError('队列已满')
    
        def pop(self):
            if not self.is_empth():
                self.front = (self.front + 1) % self.size
                return self.queue[self.front]
            else:
                raise ImportError('队列已空')
    
        # 判断队空
        def is_empth(self):
            return self.rear == self.front
    
        # 判断队满
        def is_filled(self):
            return (self.rear + 1) % self.size == self.front
    
    
    
    q = Queue(5)
    for i in range(4):
        q.push(i)
    
    print(q.is_filled())
    print(q.pop())
    print(q.pop())
    print(q.pop())
    print(q.pop())
    print(q.push(15))
    print(q.pop())
    View Code

    双向队列

    双向队列的两端都支持进队和出队操作

    双向队列的基本操作: 队首进队、队首出队、队尾进队、队尾出队

    队列的实现

    from collections import deque
    q = deque([1,2,3,4,5],5) # 非空对列 长度为 5,长度大于 5 时,会自动八队首移出
    q.append(7)         # 队尾进队
    print(q.popleft())  # 队首出队
    
    q.appendleft(1)     # 队首进队
    print(q.pop())      # 队尾出对
    
    # 读取文件中后 5 行数据
    def tail(num):
        with open('./info.txt','r',encoding='utf-8')as f:
            q = deque(f,num)
            return q
    
    for i in tail(5):
        print(i,end='')
    View Code

    队列的实现方式——环形队列

     

     栈和队列的应用——迷宫问题

     

    栈——深度优先搜索

    # 0 代表路,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 - 1, y),
        lambda x, y: (x, y + 1),
        lambda x, y: (x, y - 1),
    ]
    
    
    def maze_path(x1, y1, x2, y2):
        stack = []
        stack.append((x1, y1))
        while len(stack) > 0:
            curCode = stack[-1]  # 当前节点
            if curCode[0] == x2 and curCode[1] == y2:
                # 走到了终点
                for p in stack:
                    print(p)
                return True
            # 查找 4 个方向
            for dir in dirs:
                nextCode = dir(curCode[0], curCode[1])
                if maze[nextCode[0]][nextCode[1]] == 0:
                    stack.append(nextCode)
                    maze[nextCode[0]][nextCode[1]] = 2  # 表示已经走过了
                    break
            else:
                maze[nextCode[0]][nextCode[1]] = 2
                stack.pop()
    
        else:
            print('没有路')
            return False
    
    maze_path(1,1,8,8)
    View Code

    队列——广度优先搜索

    from collections import deque
    
    # 0 代表路,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 - 1, y),
        lambda x, y: (x, y + 1),
        lambda x, y: (x, y - 1),
    ]
    
    def print_r(path):
        real_path = []
        i = len(path) - 1
        while i >= 0:
            real_path.append(path[i][0:2])
            i = path[i][2]
        real_path.reverse()
        for node in real_path:
            print(node)
    
    
    def maze_path_duque(x1, y1, x2, y2):
        queue = deque()
        path = []
        queue.append((x1,y1,-1))    # 设置起点坐标
        while len(queue) > 0:        # 当队列不为空循环
            cur_node = queue.popleft()
            path.append(cur_node)
            if cur_node[0] == x2 and cur_node[1] == y2:
                print_r(path)     # 到达终点 打印坐标
                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  # 走过的路径
    
        return False
    
    maze_path_duque(1,1,8,8)
    View Code

    链表

    创建与遍历链表

    class Node:
        def __init__(self,item):
            self.item = item
            self.next = None
    
    
    # a = Node(1)
    # b = Node(2)
    # a.next = b
    # print(a.next.item)
    
    # 头插法
    def create_linklist_head(li):
        head = Node(li[0])      # 创建空列表,设置头节点
        for val in li[1:]:
            node = Node(val)
            node.next = head
            head = node
        return head
    
    # 尾插法
    def create_linklist_tail(li):
        head = Node(li[0])
        tail = head
        for val in li[1:]:
            node = Node(val)
            tail.next = node
            tail = node
        return head
    
    
    def look(lk):
        while lk:
            print(lk.item,end=',')
            lk = lk.next
    
    # lk_head = create_linklist_head([1,2,3,4,5])
    lk_tail = create_linklist_tail([1,2,3,4,5])
    print(look(lk_tail))
    View Code

    链表的插入和删除

     

    双链表

    双链表的插入与删除

     

    链表在插入和删除的操作上明显快于顺序表

    链表的内存可以更灵活的分配,试利用链表重新实现栈和队列

    链表这种链式存储的数据结构对树和图的结构有很大的启发性

    哈希表

    哈希表一个通过哈希函数来计算数据存 储位置的数据结构,通常支持如下操作:

    insert(key, value):插入键值对

    (key,value) get(key):如果存在键为key的键值对则返回其value,否则返回空值

    delete(key):删除键为key的键值对

    直接寻址表

     

    哈希

     

     

    哈希冲突

     

     

     哈希表——常见哈希函数

    除法哈希法: h(k) = k % m

    乘法哈希法: h(k) = floor(m*(A*key%1))

    全域哈希法: ha,b(k) = ((a*key + b) mod p) mod m a,b=1,2,...,p-1

    class LinkList:
        class Node:
            def __init__(self, item=None):
                self.item = item
                self.next = None
    
        class LinkListIterator:
            def __init__(self, node):
                self.node = node
    
            def __next__(self):
                if self.node:
                    cur_node = self.node
                    self.node = cur_node.next
                    return cur_node.item
                else:
                    raise StopIteration
    
            def __iter__(self):
                return self
    
        def __init__(self, iterable=None):
            self.head = None
            self.tail = None
            if iterable:
                self.extend(iterable)
    
        def append(self, obj):
            s = LinkList.Node(obj)
            if not self.head:
                self.head = s
                self.tail = s
            else:
                self.tail.next = s
                self.tail = s
    
        def extend(self, iterable):
            for obj in iterable:
                self.append(obj)
    
        def find(self, obj):
            for n in self:
                if n == obj:
                    return True
            else:
                return False
    
        def __iter__(self):
            return self.LinkListIterator(self.head)
    
        def __repr__(self):
            return "<<"+", ".join(map(str, self))+">>"
    
    # 类似于集合的结构
    class HashTable:
        def __init__(self, size=101):
            self.size = size
            self.T = [LinkList() for i in range(self.size)]
    
        def h(self, k):
            return k % self.size
    
        def insert(self, k):
            i = self.h(k)
            if self.find(k):
                print("Duplicated Insert.")
            else:
                self.T[i].append(k)
    
        def find(self, k):
            i = self.h(k)
            return self.T[i].find(k)
    
    
    ht = HashTable()
    
    ht.insert(0)
    ht.insert(1)
    ht.insert(3)
    ht.insert(102)
    ht.insert(508)
    
    #print(",".join(map(str, ht.T)))
    print(ht.find(203))
    View Code

    哈希表的应用——集合与字典

    字典与集合都是通过哈希表来实现的。

    a = {'name': 'Alex', 'age': 18, 'gender': 'Man'}

    使用哈希表存储字典,通过哈希函数将字典的键映射为下标。假设h('name') = 3, h('age') = 1, h('gender') = 4,则哈希表存储为[None, 18, None, 'Alex', 'Man']

    如果发生哈希冲突,则通过拉链法或开发寻址法解决

    Python对字典的关键字有什么要求?如何使一个自定义的类的对象成为字典的键?

    Python对集合中的元素有什么要求?如何利用集合对大量的对象去重?

    树的实例:模拟文件系统

    class Node:
        def __init__(self, name, type='dir'):
            self.name = name
            self.type = type   #"dir" or "file"
            self.children = []
            self.parent = None
            # 链式存储
    
        def __repr__(self):
            return self.name
    
    
    class FileSystemTree:
        def __init__(self):
            self.root = Node("/")
            self.now = self.root
    
        def mkdir(self, name):
            # name 以 / 结尾
            if name[-1] != "/":
                name += "/"
            node = Node(name)
            self.now.children.append(node)
            node.parent = self.now
    
        def ls(self):
            return self.now.children
    
        def cd(self, name):
            # "/var/python/"
            if name[-1] != "/":
                name += "/"
            if name == "../":
                self.now = self.now.parent
                return
            for child in self.now.children:
                if child.name == name:
                    self.now = child
                    return
            raise ValueError("invalid dir")
    
    
    
    tree = FileSystemTree()
    tree.mkdir("var/")
    tree.mkdir("bin/")
    tree.mkdir("usr/")
    
    tree.cd("bin/")
    tree.mkdir("python/")
    
    tree.cd("../")
    
    print(tree.ls())
    View Code

    二叉树

    二叉树的链式存储:将二叉树的节点定义为一个对象,节点之间通过类似链表的链接方式来连接。

    二叉树遍历

    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')
    
    root = e
    e.lchild = a
    e.rchild = g
    a.rchild = c
    c.lchild = b
    c.rchild = d
    g.rchild = f
    
    # print(root.rchild.rchild.data)
    
    
    # 前序遍历
    def front_order(root):
        if root:
            print(root.data,end=',')
            front_order(root.lchild)
            front_order(root.rchild)
    # front_order(root)
    
    # 中序遍历
    def middle_order(root):
        if root:
            middle_order(root.lchild)
            print(root.data,end=',')
            middle_order(root.rchild)
    
    # middle_order(root)
    
    # 后序遍历
    def back_order(root):
        if root:
            middle_order(root.lchild)
            print(root.data, end=',')
            middle_order(root.rchild)
    
    # middle_order(root)
    
    # 层级遍历
    def level_order(root):
        qu = deque()
        qu.append(root)
        while len(qu)>0:
            node = qu.popleft()
            print(node.data,end=',')
            if node.lchild:
                qu.append(node.lchild)
            if node.rchild:
                qu.append(node.rchild)
    
    
    level_order(root)
    View Code

    二叉树:插入、查询、删除

     

     

     

     

    import random
    
    class BiTreeNode:
        def __init__(self, data):
            self.data = data
            self.lchild = None   # 左孩子
            self.rchild = None  # 右孩子
            self.parent = None
    
    class BST:
        def __init__(self, li=None):
            self.root = None
            if li:
                for val in li:
                    self.insert_no_rec(val)
    
        def insert(self, node, val):
            if not node:
                node = BiTreeNode(val)
            elif val < node.data:
                node.lchild = self.insert(node.lchild, val)
                node.lchild.parent = node
            elif val > node.data:
                node.rchild = self.insert(node.rchild, val)
                node.rchild.parent = node
            return node
    
        def insert_no_rec(self, val):
            p = self.root
            if not p:               # 空树
                self.root = BiTreeNode(val)
                return
            while True:
                if val < p.data:
                    if p.lchild:
                        p = p.lchild
                    else:           # 左孩子不存在
                        p.lchild = BiTreeNode(val)
                        p.lchild.parent = p
                        return
                elif val > p.data:
                    if p.rchild:
                        p = p.rchild
                    else:
                        p.rchild = BiTreeNode(val)
                        p.rchild.parent = p
                        return
                else:
                    return
    
        def query(self, node, val):
            if not node:
                return None
            if node.data < val:
                return self.query(node.rchild, val)
            elif node.data > val:
                return self.query(node.lchild, val)
            else:
                return node
    
        def query_no_rec(self, val):
            p = self.root
            while p:
                if p.data < val:
                    p = p.rchild
                elif p.data > val:
                    p = p.lchild
                else:
                    return p
            return None
    
        def pre_order(self, root):
            if root:
                print(root.data, end=',')
                self.pre_order(root.lchild)
                self.pre_order(root.rchild)
    
        def in_order(self, root):
            if root:
                self.in_order(root.lchild)
                print(root.data, end=',')
                self.in_order(root.rchild)
    
        def post_order(self, root):
            if root:
                self.post_order(root.lchild)
                self.post_order(root.rchild)
                print(root.data, end=',')
    
    
        def __remove_node_1(self, node):
            # 情况1:node是叶子节点
            if not node.parent:
                self.root = None
            if node == node.parent.lchild:  #node是它父亲的左孩子
                node.parent.lchild = None
            else:   #右孩子
                node.parent.rchild = None
    
        def __remove_node_21(self, node):
            # 情况2.1:node只有一个左孩子
            if not node.parent: # 根节点
                self.root = node.lchild
                node.lchild.parent = None
            elif node == node.parent.lchild:
                node.parent.lchild = node.lchild
                node.lchild.parent = node.parent
            else:
                node.parent.rchild = node.lchild
                node.lchild.parent = node.parent
    
        def __remove_node_22(self, node):
            # 情况2.2:node只有一个右孩子
            if not node.parent:
                self.root = node.rchild
            elif node == node.parent.lchild:
                node.parent.lchild = node.rchild
                node.rchild.parent = node.parent
            else:
                node.parent.rchild = node.rchild
                node.rchild.parent = node.parent
    
    
        def delete(self, val):
            if self.root:   # 不是空树
                node = self.query_no_rec(val)
                if not node: # 不存在
                    return False
                if not node.lchild and not node.rchild: #1. 叶子节点
                    self.__remove_node_1(node)
                elif not node.rchild:       # 2.1 只有一个左孩子
                    self.__remove_node_21(node)
                elif not node.lchild:       # 2.2 只有一个右孩子
                    self.__remove_node_22(node)
                else:   # 3. 两个孩子都有
                    min_node = node.rchild
                    while min_node.lchild:
                        min_node = min_node.lchild
                    node.data = min_node.data
                    # 删除min_node
                    if min_node.rchild:
                        self.__remove_node_22(min_node)
                    else:
                        self.__remove_node_1(min_node)
    
    
    
    
    #
    #
    # tree = BST([1,4,2,5,3,8,6,9,7])
    # tree.in_order(tree.root)
    # print("")
    #
    # tree.delete(4)
    # tree.delete(1)
    # tree.delete(8)
    # tree.in_order(tree.root)
    View Code

    AVL树

    AVL树——插入

    插入一个节点可能会破坏AVL树的平衡,可以通过旋转操作来进行修正。

    插入一个节点后,只有从插入节点到根节点的路径上的节点的平衡可能被改变。我们需要找出第一个破坏了平衡条件的节点,称之为K。K的两颗子树的高度差2。

    不平衡的出现可能有4种情况

    AVL插入——右旋

    AVL插入——左旋

    AVL插入——右旋-左旋

    AVL插入——左旋-右旋

     二叉搜索树扩展应用——B树

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

  • 相关阅读:
    quartz 中JobExecutionContext的使用
    Memcached 集群架构方面的问题
    Spring+Quartz 集群
    Spring Quartz 持久化解决方案
    不同版本(2.3,2.4,2.5) web.xml 的web-app头信息
    Spring Framework 4.0.0发布,首次支持Java 8
    Serializable java序列化
    Quartz 有状态的JobDataMap
    JobDataMap 不能被序列化如何解决研究中
    Spring-3.2.5 + Quartz-2.2.1 集群实例(Tomcat+Memcached+Quartz集群session共享)
  • 原文地址:https://www.cnblogs.com/ysging/p/12914672.html
Copyright © 2011-2022 走看看