判断整数数组元素是否已经按升序排序。_通俗易懂讲解 位排序

作者:上善若水 来源:公众号码农有道

这几天没事的时候,翻了翻<>这本书,中间有一节通过一个问题讲述了一种比较巧妙的排序方法—位图排序。我们还是先看问题吧。

问题描述

编程珠玑这本书给出的问题如下:

  • 输入:一个至多包含n个非负整数的文件,每个数都小于n; 且这些整数都不重复;数据之间也不存在关联关系。 此处n取值10000000。
  • 约束:①最多1MB的内存空间可用;②磁盘空间充足;③运行时间最多几分钟, 最好是线性时间。
  • 输出:按升序排列的整数序列。

位图排序思想

针对该题的特点,由于待排序的数据记录较多,如果使用常见的排序方法效率较低,而且内存空间有限(限制为1MB左右),不能一次性将数据如内存,需从文件中多次读入,该书引入了一种新的排序方法 ---位图法。

所谓位图法,就是使用一串二进制串来表示待排序列元素集合的方法。 比如,我们知道一个byte = 8bit,表示成二进制串就是:00000000.我们可以用bit的位置[对于一个字节就是0-7,)来表示待序序列中是否出现一个数,1表示出现,0表示不出现。比如说00000010则表示待排序列包含1,因为bit 1为1。如果还不明白,继续看下面的两个例子和示图。

例如:待排序集合{3,6,0,2},由于这个序列的最大数是6,因此用一个byte就够了,因此可以分配一个只含一个元素的char数组,如char arr[1],然后看待排序列第一个元素是3,因此将数组元素的第3bit设为1,依次扫描直到待排序列的最后一个元素2,其位图可以用下图的左边表示。

那么如果待排序列的最大数不能用一个byte的最大位置表示了,比如说{3,6,0,2,8,9,10,12}了。很简单,那么就用2个字节的char数组,char arr[2],比如说待排序列中出现8,那么就将arr数组第二个元素的第0位设为1就可以了,依次类推,如下图右边所示。

12c611b5a2b8b151748c1a7a53e11053.png

好了,现在已经根据待排序列分配好char arr[ ]数组了,也根据待排序列的数据将数组arr的相应位设置好了。最后我们要做的很简单,就是从低到高位依次扫描这个数组的每一位,如果该位为1,表示这个数在待排序列中出现过,那么打印出或者写入到输出文件中,扫描完也就得到了排序后的序列了。

根据上面所描述,不难得出位图排序的伪码如下:

e8e7283e54b5f4f3705099b03b10d7bb.png

位图排序实现

下面给出位图排序的实现,为了简化,这里使用了C++标准库中的bitset容器,bitset容器是C++中一种提供位集合的数据结构,我们可以像使用数组一样使用位。使用bitset很简单,不太了解的同学去网上查查相关的资料很快就会用的,下面就是位图排序的C++实现。

241b6705eb211fccfc6d410d35eb430b.png
0e6ca5ebe818b367dbd2d6a60f2c848a.png

位图排序适用场景

根据位图排序的特点,不难看出位图排序的适用是有一定局限性的,也就是说位图排序只有在一定的场景下使用效果才最好,如下所示。

  • 需要知道待排序序列中的最大数,因为位图排序时需根据该值来分配数组空间。
  • 位图排序比较适合元素密集的序列,因为在位图排序中,有些数没有出现过,仍要为其保留一个位。如果元素稀疏,则空间浪费很多,比如说一个待排序列{1,10000000,2},总共三个元素,却要保留10000000/8个字节,是不是有点太得不偿失了。
  • 待排序的元素最好不能重复。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我来解答你的问题。 首先,让我们来看看二叉树的遍历方式。二叉树的遍历方式有三种:前序遍历、中序遍历和后序遍历。 - 前序遍历:先访问根节点,再访问左子树,最后访问右子树。 - 中序遍历:先访问左子树,再访问根节点,最后访问右子树。 - 后序遍历:先访问左子树,再访问右子树,最后访问根节点。 而在XML解析中,我们可以将XML文件看作一棵树,它的每个节点都有自己的标签、属性和内容。因此,在解析XML文件时,我们也可以采用类似于二叉树的遍历方式来遍历XML树。 具体来说,我们可以使用tinyxml2库中的函数来遍历XML树。以下是一个简单的示例代码,演示如何遍历XML树中的所有节点: ``` #include "tinyxml2.h" #include <iostream> using namespace tinyxml2; void TraverseXML(XMLElement* element) { if (element == nullptr) { return; } // 访问当前节点 std::cout << "Node name: " << element->Name() << std::endl; // 访问当前节点的属性 const XMLAttribute* attribute = element->FirstAttribute(); while (attribute != nullptr) { std::cout << "Attribute name: " << attribute->Name() << ", value: " << attribute->Value() << std::endl; attribute = attribute->Next(); } // 访问当前节点的内容 std::cout << "Node value: " << element->GetText() << std::endl; // 遍历当前节点的子节点 XMLElement* child = element->FirstChildElement(); while (child != nullptr) { TraverseXML(child); child = child->NextSiblingElement(); } } int main() { XMLDocument doc; doc.LoadFile("example.xml"); XMLElement* root = doc.FirstChildElement(); TraverseXML(root); return 0; } ``` 这段代码中,我们首先定义了一个TraverseXML函数,用于遍历XML树中的所有节点。该函数的参数是一个XMLElement指针,表示当前节点。在函数内部,我们首先访问了当前节点的标签、属性和内容,然后遍历了当前节点的所有子节点。 在主函数中,我们首先使用XMLDocument类的LoadFile函数来加载XML文件,然后获取XML树的根节点,并将其作为参数传递给TraverseXML函数,从而遍历整个XML树。 希望这个通俗易懂讲解能够帮助你理解如何使用tinyxml2遍历所有节点。如果你还有其他问题,可以随时问我哦!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值