用python实现基本数据结构【04/4】

这篇具有很好参考价值的文章主要介绍了用python实现基本数据结构【04/4】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

用python实现基本数据结构【04/4】,python技能小结,python,数据结构,开发语言

说明

        如果需要用到这些知识却没有掌握,则会让人感到沮丧,也可能导致面试被拒。无论是花几天时间“突击”,还是利用零碎的时间持续学习,在数据结构上下点功夫都是值得的。那么Python 中有哪些数据结构呢?列表、字典、集合,还有……栈?Python 有栈吗?本系列文章将给出详细拼图。


13章: Binary Tree

The binary Tree: 二叉树,每个节点做多只有两个子节点

class _BinTreeNode:
    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None


# 三种depth-first遍历
def preorderTrav(subtree):
    """ 先(根)序遍历"""
    if subtree is not None:
        print(subtree.data)
        preorderTrav(subtree.left)
        preorderTrav(subtree.right)


def inorderTrav(subtree):
    """ 中(根)序遍历"""
    if subtree is not None:
        preorderTrav(subtree.left)
        print(subtree.data)
        preorderTrav(subtree.right)


def postorderTrav(subtree):
    """ 后(根)序遍历"""
    if subtree is not None:
        preorderTrav(subtree.left)
        preorderTrav(subtree.right)
        print(subtree.data)


# 宽度优先遍历(bradth-First Traversal): 一层一层遍历, 使用queue
def breadthFirstTrav(bintree):
    from queue import Queue    # py3
    q = Queue()
    q.put(bintree)
    while not q.empty():
        node = q.get()
        print(node.data)
        if node.left is not None:
            q.put(node.left)
        if node.right is not None:
            q.put(node.right)


class _ExpTreeNode:
    __slots__ = ('element', 'left', 'right')

    def __init__(self, data):
        self.element = data
        self.left = None
        self.right = None

    def __repr__(self):
        return '<_ExpTreeNode: {} {} {}>'.format(
            self.element, self.left, self.right)

from queue import Queue
class ExpressionTree:
    """
    表达式树: 操作符存储在内节点操作数存储在叶子节点的二叉树。(符号树真难打出来)
        *
       / \
      +   -
     / \  / \
     9  3 8   4
    (9+3) * (8-4)

    Expression Tree Abstract Data Type,可以实现二元操作符
    ExpressionTree(expStr): user string as constructor param
    evaluate(varDict): evaluates the expression and returns the numeric result
    toString(): constructs and retutns a string represention of the expression

    Usage:
        vars = {'a': 5, 'b': 12}
        expTree = ExpressionTree("(a/(b-3))")
        print('The result = ', expTree.evaluate(vars))
    """

    def __init__(self, expStr):
        self._expTree = None
        self._buildTree(expStr)

    def evaluate(self, varDict):
        return self._evalTree(self._expTree, varDict)

    def __str__(self):
        return self._buildString(self._expTree)

    def _buildString(self, treeNode):
        """ 在一个子树被遍历之前添加做括号,在子树被遍历之后添加右括号 """
        # print(treeNode)
        if treeNode.left is None and treeNode.right is None:
            return str(treeNode.element)    # 叶子节点是操作数直接返回
        else:
            expStr = '('
            expStr += self._buildString(treeNode.left)
            expStr += str(treeNode.element)
            expStr += self._buildString(treeNode.right)
            expStr += ')'
            return expStr

    def _evalTree(self, subtree, varDict):
        # 是不是叶子节点, 是的话说明是操作数,直接返回
        if subtree.left is None and subtree.right is None:
            # 操作数是合法数字吗
            if subtree.element >= '0' and subtree.element <= '9':
                return int(subtree.element)
            else:    # 操作数是个变量
                assert subtree.element in varDict, 'invalid variable.'
                return varDict[subtree.element]
        else:    # 操作符则计算其子表达式
            lvalue = self._evalTree(subtree.left, varDict)
            rvalue = self._evalTree(subtree.right, varDict)
            print(subtree.element)
            return self._computeOp(lvalue, subtree.element, rvalue)

    def _computeOp(self, left, op, right):
        assert op
        op_func = {
            '+': lambda left, right: left + right,    # or import operator, operator.add
            '-': lambda left, right: left - right,
            '*': lambda left, right: left * right,
            '/': lambda left, right: left / right,
            '%': lambda left, right: left % right,
        }
        return op_func[op](left, right)

    def _buildTree(self, expStr):
        expQ = Queue()
        for token in expStr:    # 遍历表达式字符串的每个字符
            expQ.put(token)
        self._expTree = _ExpTreeNode(None)    # 创建root节点
        self._recBuildTree(self._expTree, expQ)

    def _recBuildTree(self, curNode, expQ):
        token = expQ.get()
        if token == '(':
            curNode.left = _ExpTreeNode(None)
            self._recBuildTree(curNode.left, expQ)

            # next token will be an operator: + = * / %
            curNode.element = expQ.get()
            curNode.right = _ExpTreeNode(None)
            self._recBuildTree(curNode.right, expQ)

            # the next token will be ')', remmove it
            expQ.get()

        else:  # the token is a digit that has to be converted to an int.
            curNode.element = token


vars = {'a': 5, 'b': 12}
expTree = ExpressionTree("((2*7)+8)")
print(expTree)
print('The result = ', expTree.evaluate(vars))

Heap(堆):二叉树最直接的一个应用就是实现堆。堆就是一颗完全二叉树,最大堆的非叶子节点的值都比孩子大,最小堆的非叶子结点的值都比孩子小。 python内置了heapq模块帮助我们实现堆操作,比如用内置的heapq模块实现个堆排序:

# 使用python内置的heapq实现heap sort
def heapsort(iterable):
    from heapq import heappush, heappop
    h = []
    for value in iterable:
        heappush(h, value)
    return [heappop(h) for i in range(len(h))]

但是一般实现堆的时候实际上并不是用数节点来实现的,而是使用数组实现,效率比较高。为什么可以用数组实现呢?因为完全二叉树的性质, 可以用下标之间的关系表示节点之间的关系,MaxHeap的docstring中已经说明了

class MaxHeap:
    """
    Heaps:
    完全二叉树,最大堆的非叶子节点的值都比孩子大,最小堆的非叶子结点的值都比孩子小
    Heap包含两个属性,order property 和 shape property(a complete binary tree),在插入
    一个新节点的时候,始终要保持这两个属性
    插入操作:保持堆属性和完全二叉树属性, sift-up 操作维持堆属性
    extract操作:只获取根节点数据,并把树最底层最右节点copy到根节点后,sift-down操作维持堆属性

    用数组实现heap,从根节点开始,从上往下从左到右给每个节点编号,则根据完全二叉树的
    性质,给定一个节点i, 其父亲和孩子节点的编号分别是:
        parent = (i-1) // 2
        left = 2 * i + 1
        rgiht = 2 * i + 2
    使用数组实现堆一方面效率更高,节省树节点的内存占用,一方面还可以避免复杂的指针操作,减少
    调试难度。

    """

    def __init__(self, maxSize):
        self._elements = Array(maxSize)    # 第二章实现的Array ADT
        self._count = 0

    def __len__(self):
        return self._count

    def capacity(self):
        return len(self._elements)

    def add(self, value):
        assert self._count < self.capacity(), 'can not add to full heap'
        self._elements[self._count] = value
        self._count += 1
        self._siftUp(self._count - 1)
        self.assert_keep_heap()    # 确定每一步add操作都保持堆属性

    def extract(self):
        assert self._count > 0, 'can not extract from an empty heap'
        value = self._elements[0]    # save root value
        self._count -= 1
        self._elements[0] = self._elements[self._count]    # 最右下的节点放到root后siftDown
        self._siftDown(0)
        self.assert_keep_heap()
        return value

    def _siftUp(self, ndx):
        if ndx > 0:
            parent = (ndx - 1) // 2
            # print(ndx, parent)
            if self._elements[ndx] > self._elements[parent]:    # swap
                self._elements[ndx], self._elements[parent] = self._elements[parent], self._elements[ndx]
                self._siftUp(parent)    # 递归

    def _siftDown(self, ndx):
        left = 2 * ndx + 1
        right = 2 * ndx + 2
        # determine which node contains the larger value
        largest = ndx
        if (left < self._count and
            self._elements[left] >= self._elements[largest] and
            self._elements[left] >= self._elements[right]):  # 原书这个地方没写实际上找的未必是largest
            largest = left
        elif right < self._count and self._elements[right] >= self._elements[largest]:
            largest = right
        if largest != ndx:
            self._elements[ndx], self._elements[largest] = self._elements[largest], self._elements[ndx]
            self._siftDown(largest)

    def __repr__(self):
        return ' '.join(map(str, self._elements))

    def assert_keep_heap(self):
        """ 我加了这个函数是用来验证每次add或者extract之后,仍保持最大堆的性质"""
        _len = len(self)
        for i in range(0, int((_len-1)/2)):    # 内部节点(非叶子结点)
            l = 2 * i + 1
            r = 2 * i + 2
            if l < _len and r < _len:
                assert self._elements[i] >= self._elements[l] and self._elements[i] >= self._elements[r]

def test_MaxHeap():
    """ 最大堆实现的单元测试用例 """
    _len = 10
    h = MaxHeap(_len)
    for i in range(_len):
        h.add(i)
        h.assert_keep_heap()
    for i in range(_len):
        # 确定每次出来的都是最大的数字,添加的时候是从小到大添加的
        assert h.extract() == _len-i-1

test_MaxHeap()

def simpleHeapSort(theSeq):
    """ 用自己实现的MaxHeap实现堆排序,直接修改原数组实现inplace排序"""
    if not theSeq:
        return theSeq
    _len = len(theSeq)
    heap = MaxHeap(_len)
    for i in theSeq:
        heap.add(i)
    for i in reversed(range(_len)):
        theSeq[i] = heap.extract()
    return theSeq


def test_simpleHeapSort():
    """ 用一些测试用例证明实现的堆排序是可以工作的 """
    def _is_sorted(seq):
        for i in range(len(seq)-1):
            if seq[i] > seq[i+1]:
                return False
        return True

    from random import randint
    assert simpleHeapSort([]) == []
    for i in range(1000):
        _len = randint(1, 100)
        to_sort = []
        for i in range(_len):
            to_sort.append(randint(0, 100))
        simpleHeapSort(to_sort)    # 注意这里用了原地排序,直接更改了数组
        assert _is_sorted(to_sort)


test_simpleHeapSort()

14章: Search Trees

二叉差找树性质:对每个内部节点V, 1. 所有key小于V.key的存储在V的左子树。 2. 所有key大于V.key的存储在V的右子树 对BST进行中序遍历会得到升序的key序列文章来源地址https://www.toymoban.com/news/detail-706209.html

class _BSTMapNode:
    __slots__ = ('key', 'value', 'left', 'right')

    def __init__(self, key, value):
        self.key = key
        self.value = value
        self.left = None
        self.right = None

    def __repr__(self):
        return '<{}:{}> left:{}, right:{}'.format(
            self.key, self.value, self.left, self.right)

    __str__ = __repr__


class BSTMap:
    """ BST,树节点包含key可payload。用BST来实现之前用hash实现过的Map ADT.
    性质:对每个内部节点V,
    1.对于节点V,所有key小于V.key的存储在V的左子树。
    2.所有key大于V.key的存储在V的右子树
    对BST进行中序遍历会得到升序的key序列
    """
    def __init__(self):
        self._root = None
        self._size = 0
        self._rval = None     # 作为remove的返回值

    def __len__(self):
        return self._size

    def __iter__(self):
        return _BSTMapIterator(self._root, self._size)

    def __contains__(self, key):
        return self._bstSearch(self._root, key) is not None

    def valueOf(self, key):
        node = self._bstSearch(self._root, key)
        assert node is not None, 'Invalid map key.'
        return node.value

    def _bstSearch(self, subtree, target):
        if subtree is None:    # 递归出口,遍历到树底没有找到key或是空树
            return None
        elif target < subtree.key:
            return self._bstSearch(subtree.left, target)
        elif target > subtree.key:
            return self._bstSearch(subtree.right, target)
        return subtree    # 返回引用

    def _bstMinumum(self, subtree):
        """ 顺着树一直往左下角递归找就是最小的,向右下角递归就是最大的 """
        if subtree is None:
            return None
        elif subtree.left is None:
            return subtree
        else:
            return subtree._bstMinumum(self, subtree.left)

    def add(self, key, value):
        """ 添加或者替代一个key的value, O(N) """
        node = self._bstSearch(self._root, key)
        if node is not None:    # if key already exists, update value
            node.value = value
            return False
        else:   # insert a new entry
            self._root = self._bstInsert(self._root, key, value)
            self._size += 1
            return True

    def _bstInsert(self, subtree, key, value):
        """ 新的节点总是插入在树的叶子结点上 """
        if subtree is None:
            subtree = _BSTMapNode(key, value)
        elif key < subtree.key:
            subtree.left = self._bstInsert(subtree.left, key, value)
        elif key > subtree.key:
            subtree.right = self._bstInsert(subtree.right, key, value)
        # 注意这里没有else语句了,应为在被调用处add函数里先判断了是否有重复key
        return subtree

    def remove(self, key):
        """ O(N)
        被删除的节点分为三种:
        1.叶子结点:直接把其父亲指向该节点的指针置None
        2.该节点有一个孩子: 删除该节点后,父亲指向一个合适的该节点的孩子
        3.该节点有俩孩子:
            (1)找到要删除节点N和其后继S(中序遍历后该节点下一个)
            (2)复制S的key到N
            (3)从N的右子树中删除后继S(即在N的右子树中最小的)
        """
        assert key in self, 'invalid map key'
        self._root = self._bstRemove(self._root, key)
        self._size -= 1
        return self._rval

    def _bstRemove(self, subtree, target):
        # search for the item in the tree
        if subtree is None:
            return subtree
        elif target < subtree.key:
            subtree.left = self._bstRemove(subtree.left, target)
            return subtree
        elif target > subtree.key:
            subtree.right = self._bstRemove(subtree.right, target)
            return subtree

        else:    # found the node containing the item
            self._rval = subtree.value
            if subtree.left is None and subtree.right is None:
                # 叶子node
                return None
            elif subtree.left is None or subtree.right is None:
                # 有一个孩子节点
                if subtree.left is not None:
                    return subtree.left
                else:
                    return subtree.right
            else:   # 有俩孩子节点
                successor = self._bstMinumum(subtree.right)
                subtree.key = successor.key
                subtree.value = successor.value
                subtree.right = self._bstRemove(subtree.right, successor.key)
                return subtree

    def __repr__(self):
        return '->'.join([str(i) for i in self])

    def assert_keep_bst_property(self, subtree):
        """ 写这个函数为了验证add和delete操作始终维持了bst的性质 """
        if subtree is None:
            return
        if subtree.left is not None and subtree.right is not None:
            assert subtree.left.value <= subtree.value
            assert subtree.right.value >= subtree.value
            self.assert_keep_bst_property(subtree.left)
            self.assert_keep_bst_property(subtree.right)

        elif subtree.left is None and subtree.right is not None:
            assert subtree.right.value >= subtree.value
            self.assert_keep_bst_property(subtree.right)

        elif subtree.left is not None and subtree.right is None:
            assert subtree.left.value <= subtree.value
            self.assert_keep_bst_property(subtree.left)


class _BSTMapIterator:
    def __init__(self, root, size):
        self._theKeys = Array(size)
        self._curItem = 0
        self._bstTraversal(root)
        self._curItem = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self._curItem < len(self._theKeys):
            key = self._theKeys[self._curItem]
            self._curItem += 1
            return key
        else:
            raise StopIteration

    def _bstTraversal(self, subtree):
        if subtree is not None:
            self._bstTraversal(subtree.left)
            self._theKeys[self._curItem] = subtree.key
            self._curItem += 1
            self._bstTraversal(subtree.right)


def test_BSTMap():
    l = [60, 25, 100, 35, 17, 80]
    bst = BSTMap()
    for i in l:
        bst.add(i)

def test_HashMap():
    """ 之前用来测试用hash实现的map,改为用BST实现的Map测试 """
    # h = HashMap()
    h = BSTMap()
    assert len(h) == 0
    h.add('a', 'a')
    assert h.valueOf('a') == 'a'
    assert len(h) == 1

    a_v = h.remove('a')
    assert a_v == 'a'
    assert len(h) == 0

    h.add('a', 'a')
    h.add('b', 'b')
    assert len(h) == 2
    assert h.valueOf('b') == 'b'
    b_v = h.remove('b')
    assert b_v == 'b'
    assert len(h) == 1
    h.remove('a')

    assert len(h) == 0

    _len = 10
    for i in range(_len):
        h.add(str(i), i)
    assert len(h) == _len
    for i in range(_len):
        assert str(i) in h
    for i in range(_len):
        print(len(h))
        print('bef', h)
        _ = h.remove(str(i))
        assert _ == i
        print('aft', h)
        print(len(h))
    assert len(h) == 0

test_HashMap()

到了这里,关于用python实现基本数据结构【04/4】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 软考30-上午题-数据结构-小结

    真题1: 有向图——AOV 带权有向图——AOE 真题2: 二叉排序树: 左子树 根节点 右子树。 二叉排序树中序遍历,节点有序(递增); 初始序列有序,二叉树是单支树。(无序,也可以是单支树) 真题3: 真题4: 真题5: 真题6:  真题7: prim算法 ,时间复杂度

    2024年02月20日
    浏览(25)
  • 【Python爬虫与数据分析】基本数据结构

    目录 一、概述 二、特性 三、列表 四、字典 Python基本数据结构有四种,分别是列表、元组、集合、字典 ,这是Python解释器默认的数据结构,可以直接使用,无需像C语言那样需要手搓或像C++那样需要声明STL头文件。 Python的数据结构非常灵活,对数据类型没有限制,即一个数

    2024年02月11日
    浏览(38)
  • 数据结构——基本计算器的实现

             目录 一、不带括号的计算器 1.整体思想: 2.具体细节:         (1)字符串中有空格         (2)表达式开头可能是符号         (3)将数字放在第一个栈中         (4)出现“*”和“/”         (5)出现“+”和“-”         (6)完成运算 3.完整

    2024年01月22日
    浏览(28)
  • 【数据结构与算法】1、学习动态数组数据结构(基本模拟实现 Java 的 ArrayList 实现增删改查)

    🍃 数据结构是 计算机 存储 、 组织 数据的方式 🎉 线性 结构 线性表(数组、链表、栈、队列、哈希表) 🎉 树形 结构 二叉树 AVL 树 红黑树 B 树 堆 Trie 哈夫曼树 并查集 🎉 图形 结构 邻接矩阵 邻接表 🎁 线性表是具有 n 个 相同类型元素 的有限 序列 (n = 0) a1 是首节点

    2024年02月10日
    浏览(63)
  • 小肥柴慢慢手写数据结构(C篇)(5-4 中场小结)

    假设前面讨论的所有内容大家都已经自己编码实现了一遍,很容易作出以下推断: (1)数据结构底层的具体存储结构无外乎两种,即:“ 数组 ”和“ 链表 ”,也就是很多资料/博客中描述的“ 顺序存储 ”和“ 链式存储 ”。 兜兜转转,咱们的讨论还是回到了第一次讨论数

    2024年02月22日
    浏览(31)
  • 【数据结构】:实现顺序表各种基本运算的算法

    领会顺序表存储结构和掌握顺序表中各种基本运算算法设计。 编写一个程序sqlist.cpp,实现顺序表的各种基本运算和整体建表算法(假设顺序表的元素类型ElemType为char),并在此基础上设计一个主程序,完成如下功能: 初始化顺序表L 依次插入a,b,c,d,e元素 输出顺序表L 输出顺

    2024年02月07日
    浏览(34)
  • 【Java】实现顺序表基本的操作(数据结构)

    在了解顺序表之前我们要先了解什么是线性表,线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列… 线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构

    2024年02月03日
    浏览(35)
  • 数据结构——单链表基本操作实现 (c++)

    单链表链式存储结构的特点是:用一组任意的存储单元存储线性表的数据元素(这里存储单元可以是连续的,也可以是不连续的),为了表示每个数据元素a与其直接后继数据元素之间的逻辑关系,除了存储信息本身外还要存储一个指示其直接后继的信息(地址). 这两部分信

    2024年02月03日
    浏览(44)
  • 数据结构——单链表上基本操作的实现

    1.按位序插入(带头结点) : ==ListInsert(L, i, e): ==在表L 中的第 i 个位置上插入指定元素 e = 找到第 i-1 个结点 ( 前驱结点 ) ,将新结点 插入其后;其中头结点可以看作第 0 个结点,故 i=1 时也适用。 typedef struct LNode{ ElemType data; struct LNode *next; }LNode, *LinkList; // 在第 i 个位置插入

    2024年01月21日
    浏览(38)
  • 【数据结构】队列基本操作的实现(C语言)

    🚀 作者简介:一名在后端领域学习,并渴望能够学有所成的追梦人。 🐌 个人主页:蜗牛牛啊 🔥 系列专栏:🛹数据结构、🛴C++ 📕 学习格言:博观而约取,厚积而薄发 🌹 欢迎进来的小伙伴,如果小伙伴们在学习的过程中,发现有需要纠正的地方,烦请指正,希望能够与

    2024年02月16日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包