Set 是无序容器,它的插入顺序与迭代(或者 print)输出的顺序不保证与插入顺序一致,与 Dict 类似的问题,Set 的输出顺序是如何决定的呢?

首先我们从 Set 的输出开始寻找蛛丝马迹,在 Dict 的研究中,我们知道对象的输出由 *_repr 函数实现,对于 SetObject 这个函数是 set_repr:

// Objects/setobject.c:579set_repr(PySetObject *so)
{// ··· ···// Set 转换为 Listkeys = PySequence_List((PyObject *)so);// ··· ···// 获得 List 的字符串listrepr = PyObject_Repr(keys);// 截掉 List 输出左右两端的 "[" 和 "]"tmp = PyUnicode_Substring(listrepr, 1, PyUnicode_GET_LENGTH(listrepr)-1);// ··· ···// 拼接输出// ··· ···return result;
}

Set 的输出经历了以下步骤:

  • Set 转换为 List;
  • List 转换为 String;
  • 舍弃 List 输出左右两端的方括号,替换为小括号 ();

我们又一次见到了熟悉的面孔 PySequence_List,该函数的作用是将可迭代对象转换为 List 对象。Set 也是可迭代对象,具体的迭代操作由:

// Objects/setobject.c:867static PyObject *setiter_iternext(setiterobject *si){// ··· ···
}

该函数的代码表明 Set 对象的遍历,实际上是对 Set->table 的遍历,但是会忽略 NULL 和 dummy 元素。Set 的遍历顺序由其 table 中 entry 的实际排布决定。Set 元素的插入方式,可以参看上一节的介绍,其中包含了 Set 的 lookup 逻辑。

验证

下面以一个简单的例子验证我们的理解。首先创建一个空的 Set,命名为 a。为了方便,依然使用整数作为 Key,原因是整数的 hash 就是它们自身。

a = set()

此时 a 的 table 长度为初始值 8。依次插入 1~4,会分别插入 table[1~4]。当插入 5 的时候,5/8 > 3/5,table 会伸缩到 4 倍有效元素数量 5 * 4 = 20,在实际分配的时候,内存尺寸会对 2^n 向上取整, 实际伸缩 table 长度到 32。

a.update([1,2,3,4])

此时 table 的样子:

下面插入 5:

# resize the table firstly, then insert.
a.add(5)

经过 resize 的 table 看起来如此:

如果将 a 打印出来,输出的顺序应该就是 1~5,#0 处的 NULL 会被忽略掉。

print(a)

输出:

{1, 2, 3, 4, 5}

注意,此时 table 的长度已经是 32 了,下面插入一个较大的 Key = 32,对 32 求模刚好为 0,那么它会被插入到 #0:

a.add(32)
print(a)

上面的输出也可以印证这一点,输出:

{32, 1, 2, 3, 4, 5}

新插入的 32 打破了插入顺序和 Key 的大小顺序。 同理:

  • 如果继续插入 Key = 39,它会出现在 #7 的位置;
  • 再插入 Key = 70,则会出现在 #6 的位置;

我们通过代码来验证

a.add(39)
a.add(70)
print(a)

输出:

{32, 1, 2, 3, 4, 5, 70, 39}

哈! 与预期完全一致!

关注我,了解程序员的烧脑日常,还有开源 Python 教程。

c++ set 遍历_47. Set 是如何工作的(3) 遍历顺序是如何确定的?相关推荐

  1. 后序遍历的非递归算法python_二叉树后序遍历(递归与非递归)算法C语言实现...

    二叉树后序遍历的实现思想是:从根节点出发,依次遍历各节点的左右子树,直到当前节点左右子树遍历完成后,才访问该节点元素. 图 1 二叉树 如图 1 中,对此二叉树进行后序遍历的操作过程为: 从根节点 1 ...

  2. 窗体内元素遍历-通用方法(DevExpress 中BarManager的遍历)

    窗体内元素遍历-通用方法(DevExpress 中BarManager的遍历) 需求:WinForm窗体中对于各个元素进行遍历设置. 本身因为程序绝大部分功能已经开发完毕,但是权限控制没有追加,在后续 ...

  3. 遍历二叉树的各种操作(非递归遍历)

    先使用先序的方法建立一棵二叉树,然后分别使用递归与非递归的方法实现前序.中序.后序遍历二叉树,并使用了两种方法来进行层次遍历二叉树,一种方法就是使用STL中的queue,另外一种方法就是定义了一个数组 ...

  4. 树形结构:迭代方式遍历树,宽度优先,先序遍历,中序遍历,后序遍历

    迭代的方式处理树,就必须清楚你将要访问的顺序,对应的就是指针怎么走,你必须很清楚 树的宽度优先搜索,他是一层一层的访问,就搞不清楚怎么划分子问题了,但是你访问的顺序 你很清楚,那么就使用迭代的方式实现 ...

  5. 二叉树的层序遍历,前序遍历(递归,非递归),中序遍历(递归,非递归),后续遍历(递归,非递归)

    文章目录 二叉树的层序遍历 前序遍历 递归版本 非递归版本 中序遍历 递归版本 非递归版本 后序遍历 递归版本 非递归版本 二叉树的层序遍历 void printTree(BinaryTree* ar ...

  6. 无向图的深度优先遍历非递归_LeetCode0429: N叉树的层序遍历

    题目介绍 描述: 给定一个 N 叉树,返回其节点值的层序遍历. (即从左到右,逐层遍历). 例如,给定一个 3叉树 : 返回其层序遍历:[[1],[3,2,4],[5,6] ]说明:树的深度不会超过 ...

  7. python遍历树结构_python 数据结构与算法——树的遍历

    1.广度优先遍历 2.深度优先遍历 先序遍历:把根放在最前面 中序遍历:把根放在中间 后序遍历:把根放在后面 # -*- coding: utf-8 -*- """ Cr ...

  8. 二叉树的遍历实验报告C语言,数据结构-二叉树的遍历(类C语言描写叙述)

    遍历概念 所谓遍历(Traversal)是指沿着某条搜索路线.依次对树中每一个结点均做一次且仅做一次訪问.訪问结点所做的操作依赖于详细的应用问题. 遍历是二叉树上最重要的运算之中的一个,是二叉树上进行 ...

  9. 按照前序遍历创建二叉树及树的四种遍历方式

    一.二叉树的介绍 二叉树的特点是二叉树的每个结点的度都不大于2,可以视为每个结点都有左孩子和右孩子.故二叉树结点的数据结构为 二.二叉树的特点 1.设根结点所在的层数为第1层,则第i层最多有个结点. ...

最新文章

  1. ajax基础学习笔记
  2. LeetCode 98验证二叉搜素树(中序遍历)99恢复二叉搜索树
  3. J2SE:Java环境搭建探究环境变量
  4. ORA-02290:违反检查约束条件(sys_c0011321)什么原因
  5. POJ 1915 经典马步 双向bfs
  6. 浅谈Borg/YARN/Mesos/Torca/Corona一类系统
  7. 数据结构:实验四 图的遍历
  8. linux mysql 开发_Linux64下mysql安装和开发
  9. 衣带渐宽终不悔,为伊消得人憔悴。
  10. Groupon新变种 LevelUp:折价券
  11. 工业革命4.0是光和算的革命,时代呼唤硬科技!
  12. C语言求1到100的和
  13. Spring 注解@Value详解
  14. 对Java的展望_优秀技能经验及对java学习展望
  15. 【电脑配置知识】显卡 GPU
  16. 智能车图像处理-阳光算法
  17. DS18B20测量温度液晶1602显示
  18. Apache Kafka - ConsumerInterceptor 实战 (1)
  19. 毕业四年后一次同学聚会-性格决定命运
  20. 偏微分方程简明教程第六章部分答案

热门文章

  1. TCP握手/挥手的过程分析
  2. Android之Fragment 真正的完全解析(上)
  3. java中hasnext的作用_java中Scanner的hasNext()的疑问
  4. php json error,PHP 7.3 中的 JSON 错误处理
  5. 我要3万取款机怎么取_7万的新宝骏RS-3怎么样?用车三个月后,车主说出了实话...
  6. 理科生浪漫起来,谁都顶不住!
  7. 快速入门深度学习,其实并不难!
  8. 5道谷歌面试题:即使是天才也要怀疑自己能力了(附答案)
  9. mysql 堆叠查询_SQL 注入方法 - 盲注、报错注入、UNION查询注入与堆叠注入
  10. java输入数据插入if_java编程,从键盘录入10个整数数据,将每次录入的数据按从小到大的顺序插入到数组中。...