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

Posted

技术标签:

【中文标题】在未排序数组中搜索元素的最快方法【英文标题】:Fastest way to search for an element in unsorted array 【发布时间】:2011-10-05 04:45:36 【问题描述】:

我今天刚碰到这个问题,正在尝试一个比 O(N) 更好的解决方案,但无法想出一个。

通过 SO 搜索但找不到此问题。

有没有比 O(n) 更好的解决方案,或者是一个无法解决的问题?

我最初的想法是二分搜索,但同样你需要对它进行排序,这又是 >n。我还想过只对搜索元素可能所属的数组的一半应用快速排序,但我们再次进行 n 比较,然后才丢弃另一半。我是在做对还是在寻找错误的解决方案?

我正在尝试 C++ 中的解决方案,但没有 javascript 的 IndexOf() 或 C# Array.find() 或 LINQ。

【问题讨论】:

如果没有排序,我认为你不能比O(n) 做得更好。 如何从数组的两端搜索,如果元素不存在则在中间相遇。它适用于固定大小的数组或循环链表。 @user6001430 两次比较 【参考方案1】:

使其平行。将数组分成块并并行搜索。 复杂度为 O(n),但运行时间会少得多。实际上它将与否成正比。您拥有的处理器数量。

你可以在 C++ 中使用Parallel Patterns Library

【讨论】:

我敢打赌,在您获得任何显着加速之前,它会受到内存限制。 是的,我也考虑过破坏数组并尝试使用它们的线程,但这不是这个问题的算法角度。这又是特定的实现,例如 IndexOf() 或 find() @MuhammadHasanKhan 它与所涉及的 CPU 内核数量不成比例。参见 Amdal 定律 - en.wikipedia.org/wiki/Amdahl%27s_law【参考方案2】:

没错,最快的方法是简单地遍历数组并查找它。如果没有更多信息,您将无能为力。

除非你有quantum computer,否则就是这样。

【讨论】:

我希望你不会用 C++ 对那台量子计算机进行编程 如果您要计算并行性,那么是的,您可以做得比O(n) time 更好。 :) @Mysticial 除非您拥有与n 相当的处理器数量(也就是说,处理器的数量是无限的),否则它不会改变渐近时间。 有时我想知道是否有人真正关注O 的实际含义。 -_-【参考方案3】:

如果您只搜索一个元素,只需遍历它即可。没有办法让它更快。

如果您要搜索多次,那么值得将其编入索引(或排序,如果您愿意的话)并快速进行以下搜索(log(n))。

【讨论】:

是的...你是对的...但是找到一种只做一次的方法可能会很神奇,甚至可以在不使用索引数字的二进制搜索的情况下使用 n 次。 不可能。并非不可能,如“你不可能在空中跳 3 米”,因为这可能发生在大量类固醇和有弹性的地板上,但不可能,因为 1 + 1 不能等于 3。 哈哈...!同意。我刚刚发布了这个问题,看看我是唯一一个对此感到震惊的人还是还有更多人? :P 这应该是公认的答案。 “如果您多次搜索”是决定性因素。【参考方案4】:

如果未排序,则必须检查每个元素。

【讨论】:

【参考方案5】:

您可以使用这种方法以 O(1) 搜索元素。

只需创建一个 MAP 。当您为该键插入一个值时,将值分配给“1”,然后再次搜索它,只需检查该数组是否存在。

下面是代码:-

#include<bits/stdc++.h>

using namespace std;

int main()
    int n;
    cin>>n;
    map<int,int> map;
    for(int i=0;i<n;i++)
        int k;
        cin>>k;
        map[k]=1;
    
    int num;
    cin>>num;

    if(map[num])
        cout<<"FOUND"<<endl;
    else
        cout<<"NOT FOUND"<<endl;
    

    return 0;




Input: 
5    // *no. of elements*
6 4 7 3 2  //*elements* 
3    // *number to find*

输出:找到

【讨论】:

这假设您有额外的内存。通常不适合大型阵列。除非搜索次数足够多,否则这通常不是一个好的交易。 如果您正在搜索数组中的数字,那么您可以将其应用于 .【参考方案6】:

如果您不进行并行搜索,那么您可以将键作为标记值插入数组末尾,并仅使用 'n' 次比较而不是 2n 次比较进行搜索。

更多详情,请参考以下问题: What's the point of using linear search with sentinel?

【讨论】:

【参考方案7】:

通常,我们在一次迭代中检查数组的一个元素...这需要 n 次迭代才能完全循环遍历数组... 因此,最坏情况的时间复杂度变为 O(n)。

for(int i=0;i<s;i++)   // s = array size
    if(arr[i] == n)     // n = element to be searched
        return i;

但我尝试的是在一次迭代中检查多个元素。 假设每次迭代有 5 个元素。所以,在这种情况下,for 循环看起来像,

// s = array size
// n = element to be searched
for(int i=0;i<s;i+=5)  // notice the increment in i here...
    if(arr[i] == n)   
        return i;
    
/* check the next four indexes as well as if arr[i] is the last element of the array */ 
    else if( arr[i+1] == n && i+1 < s)
        return i+1;
    else if(arr[i+2] == n && i+2 < s)
        return i+2;
    else if(arr[i+3] == n && i+3 < s)
        return i+3;
    else if(arr[i+4] == n && i+4 < s)
        return i+4;

这里,理论上时间复杂度应该是 O(n/5)...

但是,当我通过获取大小为 1000000 的数组以及随机排列的元素 1 到 1000000 并计算两个循环对相同数组大小的不同测试用例所花费的时间来执行程序时......这些就是结果!

每次迭代一个元素

    时间复杂度(以微秒为单位):4105 4180 4108 4115 4087 4137 4094 4089 4141 4167 4082 4084 4114 4118 4099

每次迭代 5 个元素

    时间复杂度(以微秒为单位):1318 1382 1384 1297 1364 1289 1351 1617 1300 1289 1395 1385 1349 1329 1369

因此,正如我所见,它在时间复杂度上产生了重大变化!

【讨论】:

【参考方案8】:

这可以通过使用一些技巧来解决。在一个未排序的数组中,如果我们遍历它,最坏情况下的复杂度(当元素出现在最后一个索引处)将是 O(N),其中 N 是数组的大小。所以,这就是诀窍。首先检查最后一个索引,以便如果元素存在于最后一个索引(最坏的情况),我们的代码将在 O(1) 中执行。然后代码遍历并找到元素。所以,现在最坏情况的复杂度是 O(N-1)。

int findElement(int N, int arr[], int element)
  if(arr[N]==element)
    return i;
  
  for(int i=0; i<N-1; i++)
    if(arr[i]==element)
      return i;
  
  return -1;

【讨论】:

【参考方案9】:

然而,还有另一个逻辑……

(偶数存储在偶数地址中)

先检查搜索元素是奇数还是偶数

如果搜索元素是“偶数”,则只搜索偶数 地址(创建循环增量以跳过奇数地址)

该逻辑可以跳过一半的元素搜索

例如:

如果有 100 个元素以无序方式存储并搜索 元素是 98.... 因为搜索数是偶数...你可以跳过 所有奇数地址(因此跳过 50 个元素)现在搜索完成 仅用于其余 50 个偶数地址....

您可以划分元素并并行搜索或使用“枢轴键”排序到剩余50个元素或任何其他搜索方法

【讨论】:

未排序数组:8 32 4 3 14【参考方案10】:

如下在快速排序期间应用分区方法的算法效率如何?

    随机选择列表中的某个值(我们称之为 v)。

    将整个列表分成两部分。左边部分包含所有小于 v 的元素。右边部分包含所有大于 v 的元素。

    重复步骤 2、3,直到确定元素是否存在。

我不确定上述算法的复杂度,但看起来它肯定会小于快速排序算法的复杂度:(n log n)。

【讨论】:

这仅适用于排序数组。未排序的数组如下:8 32 4 3 14【参考方案11】:

给定以下数组,您可以进行并行搜索。

const array = [1, 2, 3, 4, 5, 6, 7, 3];
const search = 3;

for (let i = 0; i < array.length; i++) 
  if (array[i] === search) 
    console.log(i);
    break;
  
  if (typeof array[i + 1] !== "undefined") 
    if (array[i + 1] === search) 
      console.log(i + 1);
      break;
    
    if (typeof array[i + 2] !== "undefined") 
      if (array[i + 2] === search) 
        console.log(i + 2);
        break;
      
      if (typeof array[i + 3] !== "undefined") 
        if (array[i + 3] === search) 
          console.log(i + 3);
          break;
        
        if (typeof array[i + 4] !== "undefined") 
          if (array[i + 4] === search) 
            console.log(i + 4);
            break;
          
          if (typeof array[i + 5] !== "undefined") 
            if (array[i + 5] === search) 
              console.log(i + 5);
              break;
            
            if (typeof array[i + 6] !== "undefined") 
              if (array[i + 6] === search) 
                console.log(i + 6);
                break;
              
              if (typeof array[i + 7] !== "undefined") 
                if (array[i + 7] === search) 
                  console.log(i + 7);
                  break;
                
              
            
          
        
      
    
  

【讨论】:

如果数组有 10000 个元素,你会放 20000 个 ifs 吗?【参考方案12】:

可以让你的程序运行得比O(n)快。

首先使用归并排序算法对数组进行排序,然后使用二进制搜索来查找元素。两个算法的运行时间都是O(log_2(n))。将这两个复杂性加在一起,得到2*log_2(n),即O(log_2(n)) 和见证C = 2

【讨论】:

合并排序复杂度为 O(n * log(n))。在说all 错误之前先弄清事实。如果您需要原始顺序,也不能对数组进行排序,这取决于手头的任务。排序需要比搜索更多的工作,这对您来说不是很自然吗?排序只有在没有时才有意义。的搜索量很高(超过某个阈值)。 是的,我忘记了合并功能。但是二分查找仍然有 O(log_2(n)) 的运行时间。将此添加到归并排序的运行时间,得到 nlog_2(n)+log_2(n),它等于 O(nlog_2(n)),见证人 C=2 和 n_0=0 , 挺好的。 嗯,O(n * log(n)) 比线性搜索的 O(n) 差。

以上是关于在未排序数组中搜索元素的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

搜索排序数组的最快搜索算法

查找数组的所有元素是不是不同的最快方法?

R中的性能:对矩阵中的行元素进行排序的最快方法是啥?

最快效率求出乱序数组中第k小的数

检查数组中所有元素是不是相等的最快方法

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