Python利用treap实现双索引的方法

前言:

在很多应用场景下,我们不但需要堆的特性,例如快速知道数据最大值或最小值,同时还需要知道元素的排序信息,因此本节我们看看如何实现鱼和熊掌如何兼得。假设我们有一系列数据,它的元素由两部分组成,一部分对应商品的名称,其类型为字符串,一部分对应商品的货存数量,类型为整形,我们既需要将商品根据其名称排序,同时我们又需要快速查询当前货存最小的商品,我们如何设计相应的算法和数据结构来满足这样特性呢。

举个例子,如下图:

从上图看,它对应元素字符串是排序二叉树,因此根节点左子树对应元素的字符串都小于根字符串,同时右子树对应的字符串都大于根节点字符串,同时每个元素还对应着相应商品的货存数量,我们需要及时掌握当前货存最少的商品,这样才能在其耗尽之前迅速补货。但是从上图可以看到,要保证字符串的排序性就得牺牲对于商品数量的小堆性质,例如上图中water对应的货存与wine对应的货存违背了小堆的性质,现在问题是如何在保证字符串排序的情况下,确保数量同时能满足小堆性质。

首先我们先定义一下数据结构:

class Node:
    def __init__(self, key: str, priority: float):
        self._key = key
        self._priority = priority
        self._left: Node = None
        self._right: Node = None
        self._parent: Node = None 

    @property
    def left(self):
        return self._left 

    @property
    def right(self):
        return self._right 

    @property
    def parent(self):
        return self._parent 

    @left.setter
    def left(self, node):
        self._left = node
        if node is not None:
            node.parent = self 

    @right.setter
    def right(self, node):
        self._right = node
        if node is not None:
            node.parent = self 

    @parent.setter
    def parent(self, node):
        self._parent = node 

    def is_root(self) -> bool:
        if self.parent is None:
            return True
        return False 

    def __repr__(self):
        return "({}, {})".format(self._key, self._priority) 

    def __str__(self):
        repr_str: str = ""
        repr_str += repr(self)
        if self.parent is not None:
            repr_str += " parent: " + repr(self.parent)
        else:
            repr_str += " parent: None" 

        if self.left is not None:
            repr_str += " left: " + repr(self.left)
        else:
            repr_str += " left: None" 

        if self.right is not None:
            repr_str += " right: " + repr(self.right)
        else:
            repr_str += " right: None" 

        return repr_str 

class Treap:
    def  __init__(self):
        self.root : Node = None

当前问题是,当上图所示的矛盾出现时,我们如何调整,使得字符串依然保持排序性质,同时货存数值能满足小堆性质。我们需要根据几种情况采取不同操作,首先看第一种,如下图:

从上图看到,一种情况是父节点与左孩子在数值上违背了堆的性质,此时我们执行一种叫右旋转操作,

其步骤是:

  1. Beer节点逆时针旋转,替换其父节点;
  2. 父节点Cabbage顺时针旋转,成为Beer的右孩子节点;
  3. 原来Beer的右孩子节点转变为Cabbage的左孩子节点;

完成后结果如下图所示:

可以看到,此时字符串依然保持排序二叉树性质,同时数值对应的小堆性质也得到了满足。

我们看看代码实现:

class Treap:
    def __init__(self):
        self._root: Node = None 

    def right_rotate(self, x: Node):
        if x is None or x.is_root() is True:
            return 

        y = x.parent
        if y.left != x:  # 必须是左孩子才能右旋转
            return 

        p = y.parent
        if p is not None:  # 执行右旋转
            if p.left == y:
                p.left = x
            else:
                p.right = x
        else:
            self._root = x 

        y.left = x.right
        x.right = y

接下来我们构造一些数据测试一下上面的实现是否正确:

def setup_right_rotate():
    flour: Node = Node("Flour", 10)
    cabbage: Node = Node("Cabbage", 77)
    beer: Node = Node("Beer", 76)
    bacon: Node = Node("Bacon", 95)
    butter: Node = Node("Butter", 86) 

    flour.parent = None
    flour.left = cabbage
    flour.right = None
    cabbage.left = beer 

    beer.left = bacon
    beer.right = butter 

    return flour, beer 

def print_treap(n: Node):
    if n is None:
        return 

    print(n)
    print_treap(n.left)
    print_treap(n.right) 

treap = Treap()
root, x , cabbage = setup_right_rotate()
print("---------before right rotate---------:")
print_treap(root)
treap.right_rotate(x)
print("-------after right rotate-------")
print_treap(root)

上面代码执行后输出内容如下:

---------before right rotate---------:
(Flour, 10) parent: None left: (Cabbage, 77) right: None
(Cabbage, 77) parent: (Flour, 10) left: (Beer, 76) right: (Eggs, 129)
(Beer, 76) parent: (Cabbage, 77) left: (Bacon, 95) right: (Butter, 86)
(Bacon, 95) parent: (Beer, 76) left: None right: None
(Butter, 86) parent: (Beer, 76) left: None right: None
(Eggs, 129) parent: (Cabbage, 77) left: None right: None
-------after right rotate-------
(Flour, 10) parent: None left: (Beer, 76) right: None
(Beer, 76) parent: (Flour, 10) left: (Bacon, 95) right: (Cabbage, 77)
(Bacon, 95) parent: (Beer, 76) left: None right: None
(Cabbage, 77) parent: (Beer, 76) left: (Butter, 86) right: (Eggs, 129)
(Butter, 86) parent: (Cabbage, 77) left: None right: None
(Eggs, 129) parent: (Cabbage, 77) left: None right: None

对比右旋转前后输出的二叉树看,旋转后的二叉树打印信息的确跟上面我们旋转后对应的图像是一致的。接下来我们实现左旋转,先把上图中cabbage节点对应的值改成75,这样它与父节点就违背了小堆性质:

我们要做的是:

  1. cabbage节点向“左”旋转到beer的位置;
  2. beer的父节点设置为cabbage;
  3. beer的右孩子设置为cabbage的左孩子;
  4. cabbage的左孩子变成beer;左旋转后二叉树

成形如下:

从上图看,左旋转后,字符串依然保持二叉树排序性,同时数值的排放也遵守小堆原则,我们看相应的代码实现:

class Treap:
   ... 

    def left_rotate(self, x : Node):
        if x is None or x.is_root() is True:
            return 

        y = x.parent
        if y.right is not x: # 只有右孩子才能左旋转
            return 

        p = y.parent
        if p is not None:
            if p.left is y:
                p.left = x
            else:
                p.right = x
        else:
            self._root = x 

        y.right = x.left
        x.left = y

为了测试上面代码实现,我们首先把cabbage的值修改,然后调用上面代码:

cabbage._priority = 75
print("-------before left rotate--------")
print_treap(root)
treap.left_rotate(cabbage)
print("-------after left rotate---------")
print_treap(root)

代码运行后输出结果为:

-------before left rotate--------
(Flour, 10) parent: None left: (Beer, 76) right: None
(Beer, 76) parent: (Flour, 10) left: (Bacon, 95) right: (Cabbage, 75)
(Bacon, 95) parent: (Beer, 76) left: None right: None
(Cabbage, 75) parent: (Beer, 76) left: (Butter, 86) right: (Eggs, 129)
(Butter, 86) parent: (Cabbage, 75) left: None right: None
(Eggs, 129) parent: (Cabbage, 75) left: None right: None
-------after left rotate---------
(Flour, 10) parent: None left: (Cabbage, 75) right: None
(Cabbage, 75) parent: (Flour, 10) left: (Beer, 76) right: (Eggs, 129)
(Beer, 76) parent: (Cabbage, 75) left: (Bacon, 95) right: (Butter, 86)
(Bacon, 95) parent: (Beer, 76) left: None right: None
(Butter, 86) parent: (Beer, 76) left: None right: None
(Eggs, 129) parent: (Cabbage, 75) left: None right: None

输出结果的描述与上图左旋转后的结果是一致的。由于Treap相对于元素的key是排序二叉树,因此在给定一个字符串后,我们很容易查询字符串是否在Treap中,其本质就是排序二叉树的搜索,其实现我们暂时忽略。

虽然查询很简单,但是插入节点则稍微麻烦,因为插入后,新节点与其父节点可能会违背小堆性质,因此在完成插入后,我们还需使用上面实现的左旋转或右旋转来进行调整。

到此这篇关于Python使用treap实现双索引的方法的文章就介绍到这了,更多相关Python使用treap实现双索引内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Python实现列表索引批量删除的5种方法

    最近用Java做项目,使用到List broadcastInfos的 broadcastInfos.remove()方法,出现项目的重大失误,因为第一次使用Java语言,过于相信remove()方法,所以,不加思索的就使用了来删除list对象中的指定元素. 背景: 目标对象 listObj:[3, 4, 5, 6] 删除指定索引列表 indexList: [1, 2] 返回结果: [3, 6] 常见错误: for listElement in listObj: for index in inde

  • Python中remove漏删和索引越界问题的解决

    list.remove方法在删除元素的时候往往会出现漏删或者索引越界的情况示例如下: 漏删: lst=[9,25,12,36] for i in lst: if i>10: lst.remove(i) print(lst) >>>[9, 12] 那么为什么12被漏删了呢?其实原理很简单,如图: 列表从下标为0开始遍历,遍历到25时,将25删除,返回一个新的列表: 注意,原来的25对应的下标是1,所以系统会从下标为2的地方开始遍历,但是在新列表中,下标为2的地方变成了36,所以12就

  • Python Dataframe常见索引方式详解

    创建一个示例数据框: import pandas as pd df = pd.DataFrame([['乔峰', '男', 95, '降龙十八掌', '主角'], ['虚竹', '男', 93, '天上六阳掌', '主角'], ['段誉', '男', 92, '六脉神剑', '主角'], ['王语嫣', '女', 95,'熟知武诀', '主角'], ['包不同', '男', 65, '胡搅蛮缠', '配角'], ['康敏', '女', 40, '惑夫妒人', '配角']], index=list

  • Python利用treap实现双索引的方法

    前言: 在很多应用场景下,我们不但需要堆的特性,例如快速知道数据最大值或最小值,同时还需要知道元素的排序信息,因此本节我们看看如何实现鱼和熊掌如何兼得.假设我们有一系列数据,它的元素由两部分组成,一部分对应商品的名称,其类型为字符串,一部分对应商品的货存数量,类型为整形,我们既需要将商品根据其名称排序,同时我们又需要快速查询当前货存最小的商品,我们如何设计相应的算法和数据结构来满足这样特性呢. 举个例子,如下图: 从上图看,它对应元素字符串是排序二叉树,因此根节点左子树对应元素的字符串都小于根字

  • Python实现简单的索引排序与搜索功能

    今天,我上的课,学了索引排序与搜索.让我们用Python实现,觉得有点意思就跟大家分享一波. 代码如下图: import requests import re def News_Spider():#定义一个爬虫 url = 'https://news.sina.com.cn/'#url地址,新浪新闻 headers = {#请求头 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like

  • Python enumerate() 函数如何实现索引功能

    1.描述: enumerate()函数用于将一个可遍历的数据对象(如列表,元组,字符串)组合为一个索引序列,同时列出数据和数据索引(下标),一般用于for循环当中 2.语法 enumerate(sequence, [start=0]) 3.参数: sequence:一个序列,迭代器或其他支持迭代对象 start:可选参数,下标起始位置,默认从索引0开始 4.返回值 返回enumerate(枚举)对象 5.实例 list1 = [10,20,30,40,"maple","yf&

  • python 实现二维数组的索引、删除、拼接操作

    1.数组的索引 我用的是iloc函数.导入数据是data,索引data.iloc[i,j],i代表行,j代表列.如果要索引i行之后的所有行元素,使用data.iloc[i:,j], i行之前的所有行,使用data.iloc[:i,j]. 2.数组的拼接 可以使用append函数.np.apend(a,b),a和b为待拼接的数组. 由于我需要把一维数组按行拼接成二维数组,选择vstack函数,可以实现垂直方向的拼接.np.vstack((a,b)) 3.数组删除一行或多行元素 我用的是drop函数

  • Python利用QQ邮箱发送邮件的实现方法(分享)

    废话不多说,直接上代码 Python2.7 #!/usr/bin/env python2.7 # -*- coding=utf-8 -*- import smtplib from email.mime.text import MIMEText _user = "648613081@qq.com" _pwd = "这里改成你的授权码" _to = "648613081@qq.com" msg = MIMEText("this is a e

  • Python利用scapy实现ARP欺骗的方法

    一.实验原理. 本次用代码实现的是ARP网关欺骗,通过发送错误的网关映射关系导致局域网内其他主机无法正常路由.使用scapy中scapy.all模块的ARP.sendp.Ether等函数完成包的封装与发送.一个简单的ARP响应报文发送: eth = Ether(src=src_mac, dst=dst_mac)#赋值src_mac时需要注意,参数为字符串类型 arp = ARP(hwsrc=src_mac, psrc=src_ip, hwdst=dst_mac, pdst=dst_ip, op=

  • Python Series从0开始索引的方法

    如下所示: b.reset_index(drop=True) reset_index代表重新设置索引,drop=True为删除原索引. 以上这篇Python Series从0开始索引的方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

  • python for 循环获取index索引的方法

    使用 enumerate 函数 可以返回下标. 例如 for inx, val in enumerate(['uyy', 'dfdf']): print(inx) print(val) 结果是 0 uyy 1 dfdf 以上这篇python for 循环获取index索引的方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

  • python利用ffmpeg进行录制屏幕的方法

    前几天下载了几个视频,但是有两集是一个视频的,偶尔找到了ffmpeg处理视频的方法,它的功能非常强大.因此,分享一下,一起学习. import subprocess,sys,os import re class CutSplicingVdeio(object): def __init__(self): pass #dercription CutSplicingVdeio this class function def instructions(self): dercription="vdeio

  • Python实现迭代时使用索引的方法示例

    本文实例讲述了Python实现迭代时使用索引的方法.分享给大家供大家参考,具体如下: 索引迭代 Python中,迭代永远是取出元素本身,而非元素的索引. 对于有序集合,元素确实是有索引的.有的时候,我们确实想在 for 循环中拿到索引,怎么办? 方法是使用 enumerate()函数: >>> L = ['Adam', 'Lisa', 'Bart', 'Paul'] >>> for index, name in enumerate(L): ... print index

  • Python利用Beautiful Soup模块修改内容方法示例

    前言 其实Beautiful Soup 模块除了能够搜索和导航之外,还能够修改 HTML/XML 文档的内容.这就意味着能够添加或删除标签.修改标签名称.改变标签属性值和修改文本内容等等.这篇文章非常详细的给大家介绍了Python利用Beautiful Soup模块修改内容的方法,下面话不多说,来看看详细的介绍吧. 修改标签 使用的示例 HTML 文档还是如下: html_markup=""" <div class="ecopyramid">

  • python利用matplotlib库绘制饼图的方法示例

    介绍 matplotlib 是python最著名的绘图库,它提供了一整套和matlab相似的命令API,十分适合交互式地进行制图.而且也可以方便地将它作为绘图控件,嵌入GUI应用程序中. 它的文档相当完备,并且 Gallery页面 中有上百幅缩略图,打开之后都有源程序.因此如果你需要绘制某种类型的图,只需要在这个页面中浏览/复制/粘贴一下,基本上都能搞定. matplotlib的安装方法可以点击这里 这篇文章给大家主要介绍了python用matplotlib绘制饼图的方法,话不多说,下面来看代码

  • Python利用ElementTree模块处理XML的方法详解

    前言 最近因为工作的需要,在使用 Python 来发送 SOAP 请求以测试 Web Service 的性能,由于 SOAP 是基于 XML 的,故免不了需要使用 python 来处理 XML 数据.在对比了几种方案后,最后选定使用 xml.etree.ElementTree 模块来实现. 这篇文章记录了使用 xml.etree.ElementTree 模块常用的几个操作,也算是总结一下,免得以后忘记了.分享出来也方法需要的朋友们参考学习,下面话不多说了,来一起看看详细的介绍吧. 概述 对比其他

随机推荐