C++ - 将项目添加到排序数组的最快方法

Posted

技术标签:

【中文标题】C++ - 将项目添加到排序数组的最快方法【英文标题】:C++ - Fastest way to add an item to a sorted array 【发布时间】:2015-02-09 11:03:38 【问题描述】:

我有一个包含大约 200 000 个项目的数据库,按用户名排序。现在,当我将一个项目添加到数组末尾并调用我的快速排序函数来对该数组进行排序时,排序几乎需要一秒钟,这是不可接受的。肯定有相当多的优化可以完成。例如,如果我顺序比较每个字符串从 n-1 到 0,然后相应地移动项目,性能会好得多。

另一个想法是我可以执行从 0 到 n-1 的二进制搜索,而不是事实上的搜索,而是类似于利用我已经排序的数组的东西。但是,我未能编写一个适当的函数来返回一个应该放置我的新元素的索引。

void quick_sort(int left, int right)

    int i = left, j = right;
    if (left >= right) return;
    char  pivotC[128];
    DataEntry *tmp;

    strcpy_a(pivotC, sizeof pivotC, User[(left + right) / 2]->username);

    while (i <= j)
    
        while (StringCompare(User[i]->username, pivotC))
            i++;
        while (StringCompare(pivotC, User[j]->username))
            j--;
        if (i <= j) 
        
            tmp = User[i];
            User[i] = User[j];
            User[j] = tmp;
            i++;
            j--;
        
    
    if (left < j)
        quick_sort(left, j);
    if (i < right)
        quick_sort(i, right);

非常感谢任何帮助。

【问题讨论】:

是的,你可以使用二进制搜索 使用 STL containers,如 std::map。如果您不能使用它们,请阅读balanced search trees 并使用binary search 你为什么不用std::sort() 【参考方案1】:

解决方案是重写你的代码以使用 stl,我不明白为什么人们用 C++ 编写 C 代码。

你需要一个用户向量

std::vector<User> users;
//then you can keep it ordered at each insertion
auto it = upper_bound(users.begin(), users.end(), user_to_insert, 
    [](auto& lhs, auto& rhs )  /* implementation left to the reader */);
users.insert(it, user_to_insert);

您现在以更好、更简洁的方式拥有相同的功能

【讨论】:

谓词需要带两个参数。 另外,我相信你需要使用upper_boundinsert 在迭代器之前插入,因此需要理论插入位置之后的下一个元素。 是的,或者反转 lambda 中的比较,但我猜 upper_bound 是最干净的 @SebastianRedl 在列表 1, 4, 5 中尝试插入4(再次)使用lower_bound 将导致 1, (new) 4, 4, 5 ;如果我们使用uppoer_bound,我们将拥有 1, 4, (new) 4, 5 。它有什么不同?事实上使用lower_bound 可能会跳过插入知道元素已经存在。使用upper_bound,我们必须在迭代器上创建一个--,以验证元素是否已经存在。 @legends2k 你说得对,我不知何故认为lower_bound( 1, 3, 4 , 2) 会返回指向1 的迭代器。这当然是错误的,所以lower_bound 有效,正如您所指出的,如果您想消除重复项会更好。 (另一方面,如果您有大量重复项,upper_bound 会稍微快一点,因为它必须移动更少的元素。)【参考方案2】:

如果你想学习如何编写二进制搜索代码,重新发明***很好,否则重用更好。

std::lower_bound 对已排序的范围[first, last) 执行二分查找,如果已存在,则返回一个迭代器到被搜索的元素x;否则迭代器将指向大于x 的第一个元素。由于标准容器暴露 insert 会在迭代器之前插入,因此该迭代器可以按原样使用。这是一个简单的例子。

#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

int main()

    std::list<int> data =  1, 5, 7, 8, 12, 34, 52 ;

    auto loc = std::lower_bound(data.begin(), data.end(), 10);
    // you may insert 10 here using loc
    std::cout << *loc << '\n';

    loc = std::lower_bound(data.begin(), data.end(), 12);
    // you may skip inserting 12 since it is in the list (OR)
    // insert it if you need to; it'd go before the current 12
    std::cout << *loc << '\n';

12

12

【讨论】:

【参考方案3】:

简单直接的方法导致二分查找太主流了。只需要几行:

int where_to_add(int array[], int element)

    int i;
    for (i = length; i >= 0 && array[i-1] > element; i--);
    return i;

如果这就是你要找的答案,请告诉我

【讨论】:

【参考方案4】:

二进制搜索的兴趣有限,因为无论如何您都需要插入,这仍然是一个耗时的操作 (O(N))。因此,您对线性搜索然后插入的第一个想法就足够了;您可以在一个反向循环中组合。 (这是StraightInsertionSort的一个步骤。)

处理动态排序列表真正有效的方法是维护平衡树或使用哈希表。

【讨论】:

【参考方案5】:

如果您正在对仅包含一些新的不合适的尾随项的排序列表进行排序,那么您应该利用插入排序实际上有效工作的罕见情况。在排序后的列表上实现插入排序,只有少数尾随的值可以在 O(n) 时间内排序。您只是将几个不合适的值插入到位,而快速排序正在选择一个枢轴并完成整个快速排序过程。此外,如果您没有将某种类型的有效枢轴选择过程合并到您的快速排序中,并且在已经排序的列表上采用一些“前 3 个项目的平均值”方法,那么您将在 O(n^2 ) 时间。

【讨论】:

【参考方案6】:

你可以像这样进行二分搜索。在这里你可以假设如果 val 是字符串类型,则使用字符串比较函数进行比较,并且 int AR[] 是字符串集,或者你可以将它们映射为整数。由于数组是排序的,我认为二进制搜索会给你最好的性能。

int bsearch(int AR[], int N, int VAL)

    int Mid,Lbound=0,Ubound=N-1;

    while(Lbound<=Ubound)
    
        Mid=(Lbound+Ubound)/2;
        if(VAL>AR[Mid])
            Lbound=Mid+1;
        else if(VAL<AR[Mid])
            Ubound=Mid-1;
        else
            return Mid;
    

    return 0;

【讨论】:

【参考方案7】:

据我所知,您正在使用 C 数组来存储您的条目,这意味着每当您尝试插入新条目时,大量条目的性能都会受到很大影响,因为您可能需要移动很多数组中的条目。

如果您打算保留一个 C 数组并且不使用一些 stl 有序容器(尽管主要考虑 std::map),您可以尝试将您的 C 数组拆分为两个数组。一个将是第一个数组,其中包含您的键和第二个数组元素的索引。您仍然需要对第一个数组进行排序,但它的元素只有两个单词(一个用于键,一个用于索引)而不是一个包含键和一些值的大块)并且应该更快。插入项目时,您在第二个数组的末尾分配并获取索引以将其作为一对插入第一个数组中的键。如果您打算动态删除一个元素,您可以更聪明一点,但您的问题似乎没有涵盖它。

但即便如此,它可能仍然太慢,所以你确实应该考虑 std::map 或一些算法,如使用 AVL 的二叉树、红黑树、Splay 树等,你不需要物理移动元素.

【讨论】:

【参考方案8】:
int add(Container c, int r, int l, Unit t)

    if(c[r]>t)
        return r;
    if(c[l]<t)
        return l+1;
    if(c[r]==c[l])
    
         if(c[r]==t)
            return -1;
         return -1;
    
    int m=(r+l)/2;
    if(c[m]==t)
          return -1;
    if(c[m]>t)
          return add(c,m,l,t);
    if(c[m]<t)
          return add(c,r,m,t);

它可能会给你你需要添加的索引...我希望它可以帮助。它假设你不需要添加它已经在。

【讨论】:

右(r) 左(l) 中(m) 容器(c) t(物体找到它的位置) 它返回你推那个物体的正确位置

以上是关于C++ - 将项目添加到排序数组的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

一个无序数组用啥排序方法排序是最快的

寻找对包含 C++ 中三个不同值的 2000 个项目的列表进行排序的最快方法

求c++程序 算出a[100]数组里面的最大的10个数,速度最快

从 C++ 中的字节数组中提取非零索引的最快方法是啥

根据嵌套键值对对象数组进行排序的最快方法

在未排序数组中搜索元素的最快方法