当以有序方式添加项目时,二进制搜索树有效地充当链表

时间:2011-11-23 19:31:50

标签: c++ algorithm

我有大量的物品存放在一个集合中。我需要通过将项目与某个键进行比较来找到该项目,然后判断该项目是否存在。我使用二叉搜索树来执行此操作。

class node
{
    public:
    node(const char *p_name) :
        greater(NULL),
        smaller(NULL),
        name(p_name)
    {}
    node *find(const char *p_name)
    {
        node *l_retval = this;

        for(; l_retval != NULL;)
        {
            int l = strcmp(p_name, l_retval->name.c_str());
            if (l == 0) break; // found it
            else
            if (l > 0) l_retval = greater; // the node searched for is in the 'greater' branch
            else l_retval = smaller; // or in the 'smaller' branch
        }
        return l_retval;
    }
    node *greater, *smaller;
    std::string name; // or any other type of data you would like to store
};

如果以随机方式添加项目,一切都很好。如果有时以有序的方式添加项目,我的BST将充当链接列表(慢速)。

问题是:给定BST(平衡或链接列表样式),我如何“平衡”BST,因此它被重新排列并且不能有效地充当链表?

该示例使用的是C ++,但该问题适用于比C ++更多的语言。

如果您向我提供可以帮助我的链接,谢谢!

5 个答案:

答案 0 :(得分:4)

如果您不想使用自平衡树(红黑色,AVL等),那么“简单”解决方案就是在将集合插入树之前对其进行随机播放。

如果你想要一个完美平衡的树,你可以从一个有序的集合开始,然后将该列表的中间元素插入到树中,并递归地对剩下的两个有序子集进行相同的操作。

我知道您正在调查算法,但在现实生活中,您可能只想使用std::map,而不必担心其工作原理的详细信息。

答案 1 :(得分:3)

这是BST的概念。

现在你要找的是衍生物,而不是纯粹的:

Self-balancing binary search tree

  

自平衡二叉树通过在关键时刻对树进行变换(例如树旋转)来解决此问题,以保持高度与log2(n)成比例。虽然涉及一定的开销,但从长远来看,通过确保快速执行后续操作可能是合理的。

     

始终将高度保持在最小值并不总是可行的;可以证明任何这样做的插入算法都会产生过多的开销。[引证需要]因此,大多数自平衡BST算法将高度保持在这个下界的常数因子中

自平衡树最受欢迎的变种毫无疑问是红黑树

链接

维基百科对树木算法非常权威:

  • Binary Search Tree

      

    注意 “在任一版本中,此操作都需要与最坏情况下树的高度成比例的时间,即所有树的平均情况下的O(log n)时间,但在最坏的情况下是O(n)时间。“< - 这是你的观点

    并在排序

      

    build_binary_tree的最坏情况时间是O(n2) - 如果你给它一个排序的值列表,它将它们链接到一个没有左子树的链表。例如,{ {1}}([1,2,3,4,5])产生树(1(2(3(4(5)))))“

  • Red-Black Tree

答案 2 :(得分:1)

首先想到的问题是你为什么要实现这个?使用std::setstd::map。两者都实现平衡树。您需要提供一个订购功能,但除此之外(以及您无法修改设置订单的字段的明显限制)应该没问题。

如果已经将元素存储在另一个容器中,则可以使用控制指令的键的映射到指向原始容器的指针/迭代器(注意容器中可能使指针/迭代器无效的任何操作)

答案 3 :(得分:1)

你为此目的AVL trees

答案 4 :(得分:0)

一个技巧是使用替代比较函数(例如某种哈希,或者只是随机)将链表移动到新的链表,然后使用临时列表并重新插入到原始树中。 / p>

int node_cmp_random(struct node *one, struct node *two)
{       
return (rand() %2) ? -1 : 1;
}

更新:随机随机(C代码)

void node_randomise(struct node **tpp)
{
struct node *new, *this, **hnd;

for (new=NULL; this = node_consume( tpp); ) {
    for (hnd= &new; *hnd; hnd = (rand()&1) ? &(*hnd)->prev : &(*hnd)->next) {;}
    *hnd = this;
    }
*tpp = new ;
}

struct node * node_consume(struct node **tpp)
{
struct node * ret;
if (!*tpp) return NULL;

while ((*tpp)->prev) tpp = &(*tpp)->prev;
ret = *tpp;
*tpp = ret->next;
ret->next = NULL;
return ret;
}

更改此代码以使用函数参数是显而易见的,并留给读者练习。