python基本数据结构


数据结构

数据结构是指互相之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成。

分类

逻辑分类:线性结构;树结构;图结构

列表

列表中的元素是如何存储的:顺序存储

列表的基本操作:按下标查找、插入元素、删除元素...

这些操作的很少见复杂度时多少:查找O(1)、查找 删除O(n)

栈(Stack)是一个数据集合,可以理解为只能在一端进行插入或删除操作的列表。

栈的特点:后进先出LIFO(last-in-first-out)

栈的概念:栈顶、栈底

栈的基本操作:

  1. 进栈(压栈):push
  2. 出栈:pop
  3. 取栈顶:gettop()

class Stack:
    def __init__(self):
        self.stack = []
    def push(self,ele):
        self.stack.append(ele)
    def pop(self):
        if not self.is_empty():
            self.stack.pop()
    def get_top(self):
        if not self.is_empty():
            return self.stack[-1]
    def is_empty(self):
        return len(self.stack)==0

class Check_symbol:
    def __init__(self,check_str):
        self.res = self.__check(check_str)
    @property
    def result(self):
        return self.res
        
    def __check(self,check_str):
        stack = Stack()
        for v in check_str:
            match = {"}":"{","]":"[","}":"{"}
            if v in {"{","[","("}:
                stack.push(v)
            else:
                if v not in match:
                    return False
                if stack.get_top() == match[v]:
                    stack.pop()
                else:
                    return False
        if stack.is_empty():
            return True
        else:
            return False
    

c = Check_symbol("")
print(c.result)

队列

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

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

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

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

队列的实现

环形队列

class Queue:
    def __init__(self,size):
        self.queue = [0 for _ in range(size+1)]
        self.size = size+1
        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 IndexError("queue is filled.")
    def pop(self):
        if not self.is_empty():
            self.front = (self.front+1) % self.size
            return self.queue[self.front]
        else:
            raise IndexError("queue is empty.")
    # 判断队空
    def is_empty(self):
        return self.rear == self.front
    # 判断队满
    def is_filled(self):
        return (self.rear+1) % self.size == self.front

q = Queue(4)
q.push(1)
q.push(2)
q.push(3)
q.push(4)
print(q.pop())
print(q.pop())
print(q.pop())
print(q.pop())

迷宫问题

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

解法一:栈 -- 深度优先搜索

回溯法

思路:从一个节点开始,任意找下一个能走的点,当找不到能走的点时,退回上一个点寻找是否有其他方向的点。

使用栈存储当前路径

解法二:队列 -- 广度优先

思路:从一个节点开始,找出所有能走的点,当前节点出列,并记录该节点信息(由哪个节点走到),上层节点全部出列后 开始下一层循环,直到找到出口位置,并从记录中找到该路线位置信息。

广度优先可以找到最优解。
from collections import deque

maze_obj = [
    [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, 0, 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, 1, 0, 1, 1, 0, 1],
    [1, 1, 0, 0, 0, 0, 1, 1, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
]

dirs = [
    lambda point: (point[0] + 1, point[1]),
    lambda point: (point[0], point[1] + 1),
    lambda point: (point[0] - 1, point[1]),
    lambda point: (point[0], point[1] - 1),
]


def print_path(path):
    end_point = path[-1]  # 出口点
    realpath = []  # 实际路径
    while end_point[-1] != -1:
        realpath.append(end_point)
        end_point = path[end_point[-1]]
    loop_count = 0
    for curIndex in range(len(realpath) - 1, -1, -1):
        print(realpath[curIndex][0])
        loop_count+=1
    print("loop_count:",loop_count)


def maze_path(maze, start_point, end_point):
    maze[start_point[0]][start_point[1]] =2
    q = deque()
    q.append((start_point, 0))
    path = []
    path.append((start_point, -1))
    while len(q) > 0:
        n = len(q)
        # for i in range(n):
        # print(q)
        curNode = q.popleft()
        path.append(curNode)
        curPoint = curNode[0]
        if curPoint == end_point:
            # print(path)
            print_path(path)
            return True
        for fun in dirs:
            nextPoint = fun(curPoint)
            if maze[nextPoint[0]][nextPoint[1]] == 0:
                q.append((nextPoint, len(path) - 1))
            maze[nextPoint[0]][nextPoint[1]] = 2  # 标记已经走过
    else:
        print("没有出路")
        return True


maze_path(maze_obj, (1, 1), (7, 8))

哈希表

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

  1. insert(key,value):插入键值对
  2. get(key):如果存在键为key的键值对则返回其value,否则范围空值
  3. delete(key):删除键为key的键值对

哈希表(Hash Table,又称为散列表),是一种线性表的存储结构。哈希表由一个直接寻址表和一个哈希函数组成。哈希函数h(k)将元素关键字k作为自变量,返回元素的存储下标。

假设有一个长度为7的哈希表,哈希函数h(k)=k%7。元素集合{14,22,3,5}的存储方式如下:
i:[ 0, 1, 2, 3, 4, 5, 6] 

v:[14, 22, null, 2, 3, null, 5, null]
哈希冲突

使用上诉方式可能造成哈希冲突,比如再向哈希表中插入一个key=0的元素,按照h(k)算法 应该放在i=0的位置上,而i=0的位置上已经有值了。

解决哈希冲突
开放寻址法

开放寻址法:如果哈希函数返回的位置已经有值了,则可以向后探查新的位置来存储这个值。

  1. 线性探查:如果位置i被占用,则探查i+1, i+2, ...
  2. 二次探查:如果位置i被占用,则探查i+1^2, i-1^2, i+2^2, i-2^2, ...
  3. 二度哈希:有n个哈希函数,当使用第1个哈希函数h1发生冲突时,则尝试使用h2(k),h3(k)
拉链法

拉链法:哈希表每个位置都链接一个链表,当冲突发生时,冲突的元素将被加到该位置链表的最后。

常见哈希函数
  1. 除法哈希法: h(k) = k%m
  2. 乘法哈希法: h(k) = floor(m(Akey%1))
  3. 全域哈希法:

'''
哈希表--拉链法
'''

class LinkList:
    class Node:
        def __init__(self, item):
            self.item = item
            self.next = None

    class LinkListIterator:
        def __init__(self, node):
            self.node = node

        def __iter__(self):
            return self

        def __next__(self):
            if self.node:
                cur_node = self.node
                self.node = self.node.next
                return cur_node.item
            else:
                raise StopIteration

    def __init__(self, iterable=None):
        self.head = None
        self.tail = None
        if iterable:
            self.extend(iterable)

    def append(self, obj):
        node = self.Node(obj)
        if not self.head:
            self.head = node
            self.tail = node
        else:
            self.tail.next = node
            self.tail = node

    def extend(self, iterable):
        for obj in iterable:
            if not self.find(obj):
                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, maxsize=100):
        self.maxsize = maxsize
        self.T = [LinkList() for _ in range(maxsize)]

    def insert(self, k):
        i = self.h(k)
        if self.find(k):
            print("Duplicated Insert.")
        else:
            self.T[i].append(k)

    def h(self, k):
        return k % self.maxsize

    def find(self, k):
        i = self.h(k)
        return self.T[i].find(k)


h = HashTable()
h.insert(1)
h.insert(1)
h.insert(101)
print(",".join(map(str,h.T)))

树是一种数据结构 比如:目录结构

树是一种可以递归定义的数据结构

树是由n个节点组成的集合:

  1. 如果n=0,那这是一棵空树
  2. 如果n>0,那存在1个节点作为树的根节点,其他节点可以分为m个集合,每个集合本身又是一棵树。
一些概念

根节点,叶子节点

树的深度

树的度

孩子节点,父节点

子树

class Node:
    def __init__(self, name):
        self.name = name
        self.children = []
        self.parent = None

    def __repr__(self):
        return self.name


class Tree:
    def __init__(self, rootName):
        self.root = Node(rootName)
        self.curNode = self.root

    def append(self, name):
        appendNode = Node(name)
        self.curNode.children.append(appendNode)
        appendNode.parent = self.curNode

    def getChildren(self, name):
        print(self.curNode.children)
        for children in self.curNode.children:
            if children.name == name:
                self.curNode = children
                return self.curNode
        else:
            print("name is wrong!")
            return None


doc = Tree("html")
doc.append("head")
doc.append("body")
body = doc.getChildren("body")
print("--》 ",body)

二叉树

二叉树的子节点最多只有两个,分别为 左节点 右节点。

class BiTreeNode:
    def __init__(self, data):
        self.data = data
        self.lchild = Node #左节点
        self.rchild = Node # 右节点


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

print("child node data:",e.lchild.rchild.data)

二叉搜索树

二叉树中,如果全部左子节点的值小于其父节点的值,全部右子节点的值大于其父节点的值,则该二叉树为二叉搜索树。

class BITreeNode:
    def __init__(self, val):
        self.val = val
        self.lchild = None  # 左节点
        self.rchild = None  # 右节点
        self.parent = None  # 父节点


class BST:
    def __init__(self, val):
        self.root = BITreeNode(val)

    def insert(self, node, val):
        '''插入  递归解法'''
        # print(val,f"node:{node}")
        if not node:
            node = BITreeNode(val)
        elif val < node.val:
            node.lchild = self.insert(node.lchild, val)
            node.lchild.parent = node
        elif val > node.val:
            node.rchild = self.insert(node.rchild, val)
            node.rchild.parent = node
        return node

    def insert_no_res(self, val):
        '''插入  循环解法'''
        node = self.root
        if not node:
            self.root = BITreeNode(val)
            return
        while True:
            if val < node.val:
                if node.lchild:
                    node = node.lchild
                else:
                    node.lchild = BITreeNode(val)
                    node.lchild.parent = node
                    break
            elif val > node.val:
                if node.rchild:
                    node = node.rchild
                else:
                    node.rchild = BITreeNode(val)
                    node.rchild.parent = node
                    break
            else:
                break

    def pre_order(self, node):
        '''前序遍历'''
        if node:
            print(node.val, end=",")
            self.pre_order(node.lchild)
            self.pre_order(node.rchild)

    def in_order(self, node):
        '''中序遍历'''
        if node:
            self.in_order(node.lchild)
            print(node.val, end=",")
            self.in_order(node.rchild)

    def post_order(self, node):
        '''后序遍历'''
        if node:
            self.post_order(node.lchild)
            self.post_order(node.rchild)
            print(node.val, end=",")

    def level_order(self, node):
        '''层次遍历'''
        from collections import deque
        queue = deque()
        queue.append(node)
        while queue:
            node = queue.popleft()
            print(node.val, end=",")
            if node.lchild:
                queue.append(node.lchild)
            if node.rchild:
                queue.append(node.rchild)

    def query(self, node, val):
        '''查询,返回节点实体'''
        if node:
            if node.val == val:
                return node
            else:
                if node.lchild and val < node.val:  # 若目标值小于节点值,则往节点左节点查找
                    return self.query(node.lchild, val)
                if node.rchild and val > node.val:
                    return self.query(node.rchild, val)  # 若目标值大于节点值,则往节点右节点查找
        return None

    def excute(self, node, val):
        if self.query(node, val):
            return True
        else:
            return False

    def delete(self, val):
        '''
        删除一共有三种情况:
            1.删除的节点为叶子节点:
                把该节点删除,父节点对应其的向下关系删除
            2.删除的节点有一个子节点:
                把该节点删除,父节点对应的向下关系对应到唯一的子节点
            3.删除的节点有两个子节点:
                把该节点删除,父节点对应的向下关系对应到左节点下的最大值节点或者对应到右节点下的最小值节点
        :param val:
        :return:
        '''
        del_node = self.query(self.root, val)
        if del_node:  # 如果有节点
            children_count = self.get_children_count(del_node)
            if children_count == 0 or children_count == 1:
                self.upd_parent_relation(del_node)
            elif children_count == 2:
                self.upd_parent_left_relation(del_node)
            del_node = None
        else:
            return False

    def get_children_count(self, node):
        '''查看节点有多少子节点'''
        return (1 if node.lchild else 0) + (1 if node.rchild else 0)

    def upd_parent_relation(self, node):
        new_node = node.lchild
        if not node.lchild:
            new_node = node.rchild
        if node.parent.lchild == node:  # 若待删除节点为父节点的左子节点
            node.parent.lchild = new_node  # 更新父节点的左子链关系
        else:  # 若为右子节点
            node.parent.rchild = new_node  # 更新右子链关系

    def upd_parent_left_relation(self, node):
        new_node = node.lchild
        while new_node.rchild:
            new_node = new_node.rchild
        if node.parent.lchild == node:  # 若待删除节点为父节点的左子节点
            node.parent.lchild = new_node  # 更新父节点的左子链关系
        else:  # 若为右子节点
            node.parent.rchild = new_node  # 更新右子链关系


b = BST(5)
b.insert_no_res(4)
b.insert_no_res(6)
b.insert_no_res(3)
b.insert_no_res(2)
b.level_order(b.root)
print()

# print(b.excute(b.root, 2))
c = b.delete(4)
# print(b.excute(b.root, 2))
b.level_order(b.root)
print()
b.pre_order(b.root)

AVL树

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

AVL树:

  1. 根的左右子树的高度之差的绝对值不能超过1
  2. 根的左右子树都是平衡二叉树
优质内容筛选与推荐>>
1、模拟--P1540 机器翻译
2、CentOS7 磁盘管理
3、新写一个组件for AS3
4、mysql 第二天
5、读取 配置文件中文乱码的问题


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号