哪个更快,对向量进行排序,然后将其放入AVL树中,或直接输入

Which is faster, sorting a vector, then putting it into an AVL tree, or inputting it directly?

本文关键字:AVL 树中 输入 然后 向量 排序      更新时间:2023-10-16

所以情况是这样的:

我有

数百万个,可能数十亿个字符串,我正在尝试解析并放入排序结构中,假设我有 5,000,000 个字符串。我正在尝试编写一个快速程序,可以将所有这些来自未排序向量的字符串放入有序数据结构中,该结构也可以快速搜索结构,因此 AVL 树的推理(最终我计划使用 a-z 的哈希表进行更快的查找,但稍后再说)。我首先将所有字符串放入一个向量中,但它们都是混乱的,未排序的和不同的长度。我不希望我的树中有任何重复的字符串,所以如果程序找到字符串"hello"和"hello",它将只有一个 AVL 条目,并且会根据该字符串出现的频率增加一个整数持有者。

所以我的问题是:在所有单词与其他相同单词排序在一起之后,先对向量进行排序(使用多线程快速排序或其他快速的东西)然后将其输入到 AVL 树中会更快,还是将所有数据从未排序的向量放入 AVL 树中更快, 并不断检查 AVL 树是否已经存在一个单词,然后递增它。

因此,为了按操作顺序来描述它,这里有两种情况:

CASE A:
> Get input/parse strings
> Put strings into vector (unsorted)
> Put vector into array or linked-list
> Quicksort that array/llist
> Input that sorted array into the AVL Tree

CASE B:
> Get input/parse strings
> Put strings into vector (unsorted)
> Insert vector data into AVL tree
> During insertion, check if there are duplicate words, if so, increment the counter

哪种情况更快??

-

-编辑-- 因此,在听到一些评论后,从一开始就将排序数组插入 AVL 树将是一个坏主意,这是有道理的,因为需要进行多少次旋转。似乎直接插入到 AVL 树中可能是一个好主意,但是当单词已经在树中的某个地方时,有效插入的最佳方法是什么?我怎样才能确保我找到它?这就是我的排序可以进来的地方吗?

想想平衡对 AVL 树的工作方式。如果"中间值"排在第一位,效果最好。对于排序的输入,您将需要大量的重新平衡,因此预排序可能弊大于利。

例如,考虑以下保存值 1-6 的 AVL 树:

    4
   / 
  2   5
 /    
1   3   6

如果输入顺序是 4, 2, 5, 1, 3, 6 ,则永远不需要平衡树。 相反,对于排序的输入1, 2, 3, 4, 5, 6,您将需要许多重新平衡操作:

  1     +3     2     +4     2       +5     2       +6       3
      --->   /    --->   /      --->   /      --->     / 
    2        1   3        1   3          1   4            2   5
                                           /           /   / 
                                4          3   5        1   4   6

更新 最初的问题是,在插入到 AVL 树之前对数据进行排序是否会提高性能。 现在OP编辑了这个问题,转向了他的具体问题。

但是,当单词已经在树中的某个地方时,有效插入的最佳方法是什么?我怎样才能确保我找到它? 这就是我的排序可以进来的地方吗?

AVL树的全部意义在于有效地查找数据,所以我不明白这个问题。 如何遍历二叉搜索树以查找值应该是显而易见的。 为什么要为此对数据进行排序?

请注意,二叉搜索树是存储键的良好数据结构,但它也可以管理与这些关联的任意数据。 在您的情况下,您希望将计数与密钥一起存储。 因此,您不需要单词/字符串树,而是表示单词及其计数的对树(字符串、整数)。 对于树顺序,只需考虑字符串键,即单词。

对于要插入的每个单词,请在树中查找它。 如果已存在,请更新字数统计。 否则,插入字数为 1 的新对。

最后一点:C++标准库带有一个map类型,通常(总是?)使用平衡树(AVL或红黑)实现。 仅使用此实现,您将节省大量工作和错误修复。 自C++11以来,还有un unordered_map,通常(总是?)使用哈希表实现。

我会将我的评论转换为答案。

如果字符串集是预定义的,也就是说,在初始加载后您不会向其添加更多字符串,那么最快的可能是根本不使用 AVL 树(或任何其他树)。

只需将字符串加载到std::vector中,对其进行排序(O(N*logN),删除唯一(std::uniq,O(N)),然后用于查找使用std::lower_bound(O(logN))。

具有与 AVL 树相同的复杂性,实际上它很可能会更快,因为缓存友好性增加。

在现实世界中,以下内容可能不会更快。

将排序后的向量插入 AVL 树时,请像插入树本身一样插入它。首先插入中间,然后递归插入左半部分的中间和右半部分的中间,依此类推。如果向量中的所有值都均匀分布,则不必重新平衡树。(理论上。

更好的是,你可以用排序的向量构建你自己的树(如果你控制内部存储器),或者首先使用二叉搜索。

获得客观答案的唯一方法是测试和测量。

AVL 树中的 1-插入是 O(Log n)。排序是 O(nLogN),因此在插入之前排序会降低性能。2-出于计数目的,您可以使用哈希表来查找每个单词的出现次数。遍历所有单词,更新哈希表中每个单词的计数,然后使用哈希表在 AVL 树中插入单词,以检查单词是否已插入,如果没有插入其关联的计数。

"但是当一个单词已经在树中的某个地方时,有效插入的最佳方法是什么?我怎样才能确保我找到它?这就是我的分拣可以进来的地方吗?

在以下情况下,为什么不使用地图:键=单词,值=单词索引

这样,只要单词存在,您就可以获得访问权限,并且您将拥有操作它的索引