了解 GDB 中 STL 多集的红黑树遍历

Understanding Red-Black Tree Traversal of STL multiset in GDB

本文关键字:遍历 GDB STL 了解      更新时间:2023-10-16

我正在尝试理解以下 GDB 脚本中的 RB 树遍历

define pset
    if $argc == 0
        help pset
    else
        set $tree = $arg0
        set $i = 0
        set $node = $tree._M_t._M_impl._M_header._M_left
        set $end = $tree._M_t._M_impl._M_header
        set $tree_size = $tree._M_t._M_impl._M_node_count
        if $argc == 1
            printf "Set "
            whatis $tree
            printf "Use pset <variable_name> <element_type> to see the elements in the set.n"
        end
        if $argc == 2
            while $i < $tree_size
                set $value = (void *)($node + 1)
                printf "elem[%u]: ", $i
                p *($arg1*)$value
                if $node._M_right != 0
                    set $node = $node._M_right
                    while $node._M_left != 0
                        set $node = $node._M_left
                    end
                else
                    set $tmp_node = $node._M_parent
                    while $node == $tmp_node._M_right
                        set $node = $tmp_node
                        set $tmp_node = $tmp_node._M_parent
                    end
                    if $node._M_right != $tmp_node
                        set $node = $tmp_node
                    end
                end
                set $i++
            end
        end

我通过stl_tree.h了解RB树的内部结构,但无法理解。这些是我以下的疑问——

  1. 这里发生的是迭代顺序遍历吗?

  2. 怎么能$end = $tree._M_t._M_impl._M_header.我以为_M_header是根节点。如果不是,哪个是根节点?

  3. 如果它的无序遍历,那么我们为什么要首先通过执行if $node._M_right != 0 set $node = $node._M_right来向下移动树的右侧子树。此外,我们在 while 循环的开头打印出元素,就像我们在预购中所做的那样。但是当我在 GDB 中运行此脚本时,它确实按排序顺序打印元素,这让我推测它是按顺序打印的。

  4. 说到遍历,RB 树不是和普通 BST 一样吗?RB树不能只用左右指针遍历吗?为什么在这里使用父指针?

我想

我想通了。

我上面的所有问题都可以用一个观察来回答——"tree._M_t._M_impl._M_header不是根节点。它是一个节点,充当 RB 树的缓存。也就是说,tree._M_t._M_impl._M_header._M_Left指向树的最左侧节点,tree._M_t._M_impl._M_header._M_Right指向最右侧的节点,tree._M_t._M_impl._M_header._M_parent指向树的根节点。

现在,我们可以看到给定的代码确实是如何进行顺序遍历的。这里不使用正常BST遍历的原因是为了提高速度。通过缓存树的最左侧节点,可以在O(1)中实现iterator.begin()之类的东西,而不是O(h)其中h是树的高度。