在数组中搜索特定范围内的整数

Posted

技术标签:

【中文标题】在数组中搜索特定范围内的整数【英文标题】:Searching an array for integers in a specific range 【发布时间】:2011-12-14 12:12:25 【问题描述】:

有人可以给我以下问题的想法吗?

给定一个长度为n的数组ar[],以及一些查询,每个查询的形式为a, b, c,找到索引最小的数字i 使得索引位于[c, n] 范围内并且使得a < ar[i] < b。总共有(n - 1)c1n - 1。每个查询的预期复杂度应约为 O(log n),并且最多为复杂度的预计算 O(n log n) > 合适。直觉上,我想到了段树,但我想不出一种构建它的方法,也想不出每个节点要保留什么。

【问题讨论】:

不,数组没有排序:) 我已经删除了我的答案,直到我想到了 c 约束... @OliCharlesworth 好的,慢慢来:D 是否打算永远返回索引i = 00 < c < n,而不是0 <= c < n)?时间或空间的最坏情况是复杂性还是两者兼而有之? 在分段树的每个节点中,您保留分段中数字的有序列表。然后你对c进行二分搜索。 【参考方案1】:

好的,我想我应该实现我与 user12861 讨论过的O(nlogn)/O(logn) 解决方案。

代码通过构建n 树来工作,每个c 一棵树。每棵树与较小的树共享其大部分内容,最多有logn 个新节点。这样预处理的总内存和时间成本限制在O(nlogn)

实际的树与区间树非常相似。节点存储它们跨越的最小值和最大值,以及它们包含的最小索引。然而,我的实现不是自平衡的,但可以使用您最喜欢的启发式方法添加。

import sys

class Node:
    pass
class Interval(Node):
    def __init__(self,left,val,i,right):
        self.i = i; self.val = val
        self.left = left; self.right = right
        self.mini = min(left.mini, i, right.mini)
        self.min = min(left.min, val, right.min)
        self.max = max(left.max, val, right.max)
    def add(self,val,i):
        # We do naive inserting here. In a real worst case logn solution,
        # self-balancing would take place here.
        if (val,i) < (self.val,self.i): return Interval(self.left.add(val,i), self.val, self.i, self.right)
        if (self.val,self.i) < (val,i): return Interval(self.left, self.val, self.i, self.right.add(val,i))
        return self
    def query(self,a,b):
        # If the entire interval is covered, return mini, this is what keeps us
        # at max 2 opened nodes per level in the tree.
        if a <= self.min and self.max <= b: return self.mini
        # Otherwise compose an answer from the subtrees.
        res = sys.maxint
        if a <= self.val <= b: res = min(res, self.i)
        if self.left.min <= b and self.left.max >= a: res = min(res, self.left.query(a,b))
        if self.right.min <= b and self.right.max >= a: res = min(res, self.right.query(a,b))
        return res
class Tip(Node):
    def __init__(self):
        # This is a typical null-pattern idiom. The self.values are set to
        # the min/max identities to ensure to interference with the upwards
        # value propagation.
        self.mini = sys.maxint
        self.min = sys.maxint
        self.max = -sys.maxint
    def add(self,val,i):
        return Interval(Tip(), val, i, Tip())
    def query(self,a,b):
        return sys.maxint

# The input data array
data = [0, 3, 1, 2, 0, 4, 9, 6, 1, 7]
N = len(data)

# Build the array of trees. O(nlogn)
ar = [None]*N + [Tip()]
for i in range(N-1, -1, -1):
    ar[i] = ar[i+1].add(data[i],i)

# The query function. O(logn)
def query(a,b,c):
    return ar[c].query(a,b)

# Test
print query(2, 7, 4) # = 5

【讨论】:

这对我来说确实是解决方案。很不错!尽管您在 cmets 中有提示,但我无法弄清楚如何自己构建或查询树,因此感谢您提供代码。请注意,您提供的代码适用于 a 当然,没有自平衡的最坏情况下的性能是相当差的。 O(n^2) 设置和 O(n) 查询。我对树木没有太多经验,所以我不知道如何进行自平衡,但我相信如果你这么说,我相信它可以做到。 自平衡需要和上面一样多的代码,所以我认为它会将注意力从有趣的东西上转移开。如果你想做一个平衡的版本,我建议你研究一下陷阱。所有平衡的关键点是旋转,在这种情况下,对于大多数基于树的数据结构来说,旋转不会改变语义。【参考方案2】:

我已将梅森·布莱恩特的技术修改为可行的方法。问题在于 FindLowestIndex 中的更多错误和搜索树的更大错误(它可以返回多个结果)。

尽管做了这项工作,但它仍然不能真正解决问题。 O(n log n) 时间设置很容易,但使用这种技术我只能获得 O((log n)^2) 查询时间。我想知道您是否有原始问题的链接,以防有更多说明?或者我想知道这个问题是否真的可以解决。或者O((log n)^2) 可能是“关于”O(log n),因为问题要求,它无论如何都小于O(n)

该技术是将我们的数组存储在典型的段树中,但除了通常的段信息,我们还按顺序存储每个节点下元素的所有索引。这个额外的存储只需要额外的O(n log n) 时间/空间,如果你正确地添加它(在每个 log n 级别存储 n 个项目),所以它不会以任何重要的方式影响我们的设置时间。然后我们查询树以找到包含在我们的 (a, b) 范围内的最小节点集。此查询与典型的段树查询 (O(log n)) 花费的时间大致相同,并且最多可以找到大约 2*log n 个匹配段。当我们查询时,我们在每个匹配段中找到与我们的约束 c 匹配的最低索引。我们可以使用二分搜索来找到这个索引,因为我们保持索引的顺序,所以每个匹配节点都需要O(log n)时间最坏情况。当我们适当地加起来,总时间是O((log n)^2)

让我知道您想要澄清的任何步骤。

C#代码:

void Test() 
  DateTime start;
  TimeSpan at = new TimeSpan(), bt = new TimeSpan();

  Random rg = new Random();
  for(int i = 0; i < 20; i++) 
    // build arrays from 5 to 10000 elements long, random values between 0 and 100
    // Break even time for queries is around 10000 elements in array for the values and queries used
    int[] array = (from n in Enumerable.Range(1, rg.Next(5, 10000)) select rg.Next(0, 100)).ToArray<int>();

    // Setup should be O(n log n) time/space
    ArraySearcher Searcher = new ArraySearcher(array);

    // Test each array a number of times equal to its length, with random values for a, b, c
    for(int j = 0; j < array.Length; j++) 
      int a = rg.Next(-1, 101), b = rg.Next(a, 102), c = rg.Next(0, array.Length);
      start = DateTime.Now;
      int expected = BruteSearch(array, a, b, c);
      at += DateTime.Now - start;
      // Search should be O(log n) time, but in reality is O((log n)^2) :(
      start = DateTime.Now;
      int got = Searcher.QuickSearch(a, b, c);
      bt += DateTime.Now - start;
      System.Diagnostics.Debug.Assert(got == expected);
    
  
  MessageBox.Show(at.ToString() + ", " + bt.ToString());


int BruteSearch(int[] array, int a, int b, int c) 
  for(int i = c; i < array.Length; i++)
    if(a < array[i] && array[i] < b)
      return i;
  return -1;


class ArraySearcher 

  SegmentNode Root;
  List<SegmentNode> Nodes;

  public ArraySearcher(int[] array) 
    Nodes = array.Select((value, index) => new SegmentNode(value, value, new List<int>  index , null, null)).ToList<SegmentNode>();
    // Sorting will take us O(n log n)
    Nodes.Sort();
    // Creating a normal segment tree takes O(n log n)
    // In addition, in this tree each node stores all the indices below it in order
    // There are a total of n of these indices at each tree level, kept in order as we go at no extra time cost
    // There are log n levels to the tree
    // So O(n log n) extra time and space is spent making our lists, which doesn't hurt our big O notation compared to just sorting
    this.Root = SegmentNode.Merge(Nodes, 0, Nodes.Count - 1);
  

  public int QuickSearch(int a, int b, int c) 
    return this.Root.FindLowestIndex(a, b, c);
  

  class SegmentNode : IComparable 

    public int Min, Max;
    public List<int> Indices;
    public SegmentNode Left, Right;

    public SegmentNode(int Min, int Max, List<int> Indices, SegmentNode Left, SegmentNode Right) 
      this.Min = Min;
      this.Max = Max;
      this.Indices = Indices;
      this.Left = Left;
      this.Right = Right;
    

    int IComparable.CompareTo(object other) 
      return this.Min - ((SegmentNode)other).Min;
    

    public static SegmentNode Merge(List<SegmentNode> Nodes, int Start, int End) 
      int Count = End - Start;
      if(Start == End)
        return Nodes[Start];
      if(End - Start == 1)
        return SegmentNode.Merge(Nodes[Start], Nodes[End]);
      return SegmentNode.Merge(SegmentNode.Merge(Nodes, Start, Start + Count/2), SegmentNode.Merge(Nodes, Start + Count/2 + 1, End));
    

    public static SegmentNode Merge(SegmentNode Left, SegmentNode Right) 
      int LeftCounter = 0, RightCounter = 0;
      List<int> NewIndices = new List<int>();
      while(LeftCounter < Left.Indices.Count || RightCounter < Right.Indices.Count) 
        if(LeftCounter < Left.Indices.Count && (RightCounter == Right.Indices.Count || Left.Indices[LeftCounter] < Right.Indices[RightCounter]))
          NewIndices.Add(Left.Indices[LeftCounter++]);
        else
          NewIndices.Add(Right.Indices[RightCounter++]);
      
      return new SegmentNode(Left.Min, Right.Max, NewIndices, Left, Right);
    

    public int FindLowestIndex(int SeekMin, int SeekMax, int c) 
      // This will find at most O(log n) matching segments, always less than 2 from each level of the tree
      // Each matching segment is binary searched in at worst O(log n) time
      // Total does indeed add up to O((log n)^2) if you do it right
      if(SeekMin < this.Min && SeekMax > this.Max)
        return FindLowestIndex(this.Indices, c);
      if(SeekMax <= this.Min || SeekMin >= this.Max)
        return -1;
      int LeftMatch = this.Left.FindLowestIndex(SeekMin, SeekMax, c);
      int RightMatch = this.Right.FindLowestIndex(SeekMin, SeekMax, c);
      if(LeftMatch == -1)
        return RightMatch;
      if(RightMatch == -1)
        return LeftMatch;
      return LeftMatch < RightMatch ? LeftMatch : RightMatch;
    

    int FindLowestIndex(List<int> Indices, int c) 
      int left = 0, right = Indices.Count - 1, mid = left + (right - left) / 2;
      while(left <= right) 
        if(Indices[mid] == c)
          return c;
        if(Indices[mid] > c)
          right = mid - 1;
        else
          left = mid + 1;
        mid = left + (right - left) / 2;
      
      if(mid >= Indices.Count)
        return -1;
      // no exact match, so return the next highest.
      return Indices[mid];
    
  

【讨论】:

我想我明白了。而且我认为O(log ^ 2 N) 复杂性很好(希望如此)。但是有一些我无法得到的东西,你为什么要存储所有索引的列表?存储每个段的开头和结尾还不够吗? (对不起,如果我听起来很愚蠢:D)。 每个段的开头和结尾让我们知道何时我们的值符合我们的约束a &lt; ar[i] &lt; b。那么我们仍然需要满足i &gt;= c 的约束,并且我们需要在O(log n) 时间内完成,以获得O(log^2 n) 时间总数。如果我们在整个树中搜索满足需要 O(n) 时间的元素,太慢了。因此,我们让每个节点保留一个它包含的有序索引列表,因此我们可以在O(log n) 时间对该列表进行二分搜索。我希望这会有所帮助。我希望它是正确的!【参考方案3】:

你有没有经过this?我想它会帮助你理解结构并思考构建它的方法。其余可以设置比较。

【讨论】:

我知道什么是段树。而且我已经使用它实现了一些简单的东西。你能更具体一点吗?你能告诉我应该在分段树的每个节点中存储什么吗?【参考方案4】:

我第一次尝试从范围构建一棵树。

您首先要找到包含查询的所有解决方案的子树的根。这很容易。但是:该子树包含解决方案集之外的一些值。

因此,要找到最小的数字,您需要遍历树两次(从树的左侧的根向下,然后从右侧的根向下)。在这些遍历中的每一点,您都会检查是否找到了比之前的解决方案更低的解决方案。

完成后,该集合中最低的解决方案将是最低的。

由于检查每个节点的解决方案需要对该子树中的索引列表进行二进制搜索,因此您最终会以 O(ln(n)^2) 的速度运行,这太慢了。

如果我们一直在寻找数组中的第一个元素,这将很容易实现。那么你就不需要做二分查找了,你只需要得到第一个元素。

要到达那个地方,你最终要建造 n 棵树。

tree[0] has all values in the initializaing array
tree[1] has all but the first.
tree[2] has all but the first and second.

所以这些树中的每一个都只包含一个最佳解决方案。

所以搜索在 O(ln(n)) 中运行。

在示例代码中,通过不实现自定义数据结构来构建树。

您构建一个节点数组,制作该数组的副本并对其中一个副本进行排序。这在 O(n ln (n)) 中运行。另一个副本用于查找已排序数组中的位置,因此您可以在每次构建另一棵树时删除一个元素。

当然可以在恒定时间内删除链表中的节点,但是由于您只有对该对象的引用,我怀疑java实现必须在链表中搜索要删除的项目,所以在这个例子中构建树可能需要 O(n^2) 。不过很容易修复。

    public class ArraySearcher 

        public class SegmentNode implements Comparable<SegmentNode> 

            int min;
            int max;

            SegmentNode root;
            SegmentNode left;
            SegmentNode right;

            int lowIndex;

            public int compareTo(final SegmentNode obj)
            
                return this.min - obj.min;

            

            public boolean contains(final int n)
            
                if ((n > this.min) && (n < this.max)) 
                    return true;
                 else 
                    return false;
                
            

            /**
             * Given the root of a tree, this method will find the center node. The
             * center node is defined as the first node in the tree who's left and
             * right nodes either contain solutions or are null.
             * 
             * If no node can be found which contains solution, this will return
             * null.
             * 
             * @param a
             * @param b
             * @return
             */
            public SegmentNode findCenter(final int a, final int b)
            
                SegmentNode currentNode = this;

                while (true) 

                    /*
                     * first check to see if no solution can be found in this node.
                     * There is no solution if both nodes are
                     */
                    if ((a < currentNode.min && b < currentNode.min)
                            || (a > currentNode.max && b > currentNode.max)) 
                        return null;
                    

                    /*
                     * Now check to see if this is the center.
                     */
                    if (((currentNode.left == null) || (a < currentNode.left.max))
                            && ((currentNode.right == null) || (b > currentNode.right.min))) 

                        // this is the center, return it.
                        return currentNode;

                     else if ((currentNode.left == null)
                            || (a < currentNode.left.max)) 
                        // no solution on one side, is it in the left?
                        currentNode = currentNode.left;
                     else 
                        currentNode = currentNode.right;
                    
                

            

            public SegmentNode findLeft(final int seekMin)
            
                /*
                 * keep going left until
                 */
                if ((this.min > seekMin)) 
                    return this;
                 else 
                    if (this.right == null) 
                        return null;
                    
                    return this.right.findLeft(seekMin);
                
            

            /**
             * This method can be called on the center node. it traverses left
             * 
             * @param a
             * @param b
             * @return
             */
            public Integer findLeftmostLowestIndex(final int n)
            
                if (null == this.left) 
                    return null;
                

                int lowest = Integer.MAX_VALUE;
                SegmentNode current = this.left;

                // update the lowest with the right node if it is greater
                // than the current node.
                while (true) 

                    // collect the best value from the right and/or left
                    // if they are greater than N
                    if ((null != current.left) && (current.left.min > n)) 
                        lowest = current.left.lowIndex(lowest);
                    
                    if ((null != current.right) && (current.right.min > n)) 
                        lowest = current.right.lowIndex(lowest);
                    
                    if ((null == current.left) && (null == current.right)
                            && (current.max > n)) 
                        lowest = current.lowIndex(lowest);
                    
                    // quit when we've gone as far left as we can
                    int seek = current.leftSeek(n);
                    if (seek == 0) 
                        break;
                     else if (seek < 0) 
                        current = current.left;
                     else 
                        current = current.right;
                    

                
                if (lowest == Integer.MAX_VALUE) 
                    return null;
                 else 
                    return new Integer(lowest);
                

            

            public SegmentNode findMatch(final int seekMin, final int seekMax)
            
                if ((this.min > seekMin) && (this.max < seekMax)) 
                    return this;
                 else if ((this.min > seekMin) && (this.left != null)) 
                    return this.left.findMatch(seekMin, seekMax);
                 else 
                    if (this.right == null) 
                        return null;
                    
                    return this.right.findMatch(seekMin, seekMax);
                
            

            public SegmentNode findMatchRight(final int seekMin, final int seekMax)
            
                if ((this.min > seekMin) && (this.max < seekMax)) 
                    return this;
                 else if (this.max < seekMax) 
                    if (this.right == null) 
                        return null;
                    
                    return this.right.findMatchRight(seekMin, seekMax);
                 else 
                    if (this.left == null) 
                        return null;
                    
                    return this.left.findMatchRight(seekMin, seekMax);
                
            

            /**
             * Search for the first number in the tree which is lower than n.
             * 
             * @param n
             * @return
             */
            public Integer findRightmostLowestIndex(final int n)
            
                if (null == this.left) 
                    return null;
                

                int lowest = Integer.MAX_VALUE;
                SegmentNode current = this.right;

                // update the lowest with the right node if it is greater
                // than the current node.
                while (true) 

                    // collect the best value from the right and/or left //this.max
                    // < b
                    // if they are greater than N
                    if ((null != current.left) && (current.left.max < n)) 
                        lowest = current.left.lowIndex(lowest);
                    
                    if ((null != current.right) && (current.right.max < n)) 
                        lowest = current.right.lowIndex(lowest);
                    
                    if ((null == current.left) && (null == current.right)
                            && (current.min < n)) 
                        lowest = current.lowIndex(lowest);
                    

                    // quit when we've gone as far left as we can
                    int seek = current.rightSeek(n);
                    if (seek == 0) 
                        break;
                     else if (seek < 0) 
                        current = current.left;
                     else 
                        current = current.right;
                    

                
                if (lowest == Integer.MAX_VALUE) 
                    return null;
                 else 
                    return new Integer(lowest);
                
            

            /**
             * 
             * @param seekMin
             * @param seekMax
             * @return
             */
            public SegmentNode findSegmentRoot(final int seekMin, final int seekMax)
            

                return null;
            

            public int leftSeek(final int n)
            
                if ((null == this.left) && (null == this.right)) 
                    return 0;
                    //  else if ((null != this.left) && (this.left.max > n)) 
                 else if ((null != this.left) && ((n < this.left.max))
                        || (this.left.contains(n))) 
                    return -1;
                    //  else if ((null != this.right) && (this.right.min <= n)) 
                 else if ((null != this.right) && ((n >= this.right.min))) 
                    return +1;
                 else 
                    return 0;
                
            

            public int rightSeek(final int n)
            
                if ((null == this.left) && (null == this.right)) 
                    return 0;
                 else if ((null != this.left) && (this.left.max >= n)) 
                    return -1;
                 else if ((null != this.right) && (this.right.min < n)) 
                    return +1;
                 else 
                    return 0;
                
            

            @Override
            public String toString()
            
                StringBuilder value = new StringBuilder();
                if (null != this.left) 
                    value.append("  " + this.left.min + ", " + this.left.max
                            + " , ");
                 else 
                    value.append(" " + this.min + " , ");
                

                if (null != this.right) 
                    value.append("  " + this.right.min + ", " + this.right.max
                            + " ");
                 else 
                    value.append(" " + this.max + " , ");
                
                return value.toString();
            

            private int lowIndex(final int lowest)
            
                if (lowest < this.lowIndex) 
                    return lowest;
                 else 
                    return this.lowIndex;
                
            
        

        public static int bruteForceSearch(final int[] array, final int a,
                final int b, final int c)
        
            // search from c onward

            /**
             * search for the first value of the array that falls between a and b
             * ignore everything before index of c
             */
            for (int i = c; i < array.length; i++) 
                if ((a < array[i]) && (array[i] < b)) 
                    return i;
                
            
            return -1;
        

        SegmentNode[] trees;

        public ArraySearcher(final int[] array)
        
            buildTree(array);
        

        public void buildTree(final int[] array)
        
            ArrayList<SegmentNode> mNodes = new ArrayList<SegmentNode>();
            for (int i = 0; i < array.length; i++) 
                SegmentNode mCurrentNode = new SegmentNode();
                mCurrentNode.lowIndex = i;
                mCurrentNode.min = array[i];
                mCurrentNode.max = array[i];
                mNodes.add(mCurrentNode);
            
            ArrayList<SegmentNode> unsortedClone =
                    new ArrayList<SegmentNode>(mNodes);

            // n (ln (n) )
            Collections.sort(mNodes);

            LinkedList<SegmentNode> nodesList = new LinkedList<SegmentNode>(mNodes);

            this.trees = new SegmentNode[nodesList.size()];
            for (int i = 0; i < this.trees.length; i++) 
                this.trees[i] = merge(nodesList, 0, nodesList.size() - 1);

                // we remove the ith one at each iteration
                nodesList.remove(unsortedClone.get(i));
            
        

        /**
         * 
         * @param nodes
         * @param i
         * @param j
         * @return
         */
        public SegmentNode merge(final List<SegmentNode> nodes, final int i,
                final int j)
        
            if (i > j) 
                throw new AssertionError();
            
            SegmentNode left;
            SegmentNode right;
            int count = j - i;

            if (count == 1) 
                SegmentNode mParent = merge(nodes.get(i), nodes.get(i + 1));
                return mParent;
             else if (count == 0) 
                return nodes.get(i);
             else 
                int mid = (count / 2) + i;
                left = merge(nodes, i, mid);
                right = merge(nodes, mid + 1, j);
            
            return merge(left, right);
        

        /**
         * Build a parent segment from two other segments.
         * 
         * @param a
         * @param b
         * @return
         */
        public SegmentNode merge(final SegmentNode a, final SegmentNode b)
        
            SegmentNode parent = new SegmentNode();
            parent.root = parent;
            parent.min = a.min;
            parent.left = a;
            parent.max = b.max;
            parent.right = b;

            if (a.lowIndex > b.lowIndex) 
                parent.lowIndex = b.lowIndex;
                b.root = parent;
             else 
                parent.lowIndex = a.lowIndex;
                a.root = parent;
            

            return parent;
        

        /**
         * The work horse, find all the points with indexes greater than c that lie
         * between a and b.
         * 
         * @param a
         * @param b
         * @param c
         * @return
         */
        public Integer search(final int a, final int b, final int c)
        
            if (c < this.trees.length) 
                SegmentNode root = this.trees[c];

                if ((a > root.max) || (b < root.min)) 
                    return null;
                

                SegmentNode center = root.findCenter(a, b);
                if (null == center) 
                    return null;
                

                // special case to deal with a node with no children.
                if ((center.left == null) && (center.right == null)) 
                    if ((a < center.min) && (b > center.max)) 
                        return new Integer(center.lowIndex);
                     else 
                        return null;
                    
                

                Integer right = center.findRightmostLowestIndex(b);
                Integer left = center.findLeftmostLowestIndex(a);

                if ((null == right) && (null == left)) 
                    return null;
                 else if (null == right) 
                    return left;
                 else if (null == left) 
                    return right;
                 else if (right.compareTo(left) > 0) 
                    return left;
                 else 
                    return right;
                

             else 
                return null;
            
        
    

顺便说一句,我仍在使用这样的测试来验证它是否与蛮力方法:

    static void testBoth(final int[] array, final ArraySearcher searcher,
            final int a, final int b, final int c)
    
        System.out.println("ArraySearcherTest.testBoth(array, mSearcher, " + a
                + ", " + b + ", " + c + ");");
        int expected = ArraySearcher.bruteForceSearch(array, a, b, c);
        Integer calcObj = searcher.search(a, b, c);
        int calcInt = -1;
        if (null != calcObj) 
            calcInt = calcObj.intValue();
        
        assertEquals(expected, calcInt);

    

    @Test
    public void randomizedProblemTester()
    
        for (int i = 0; i < 100; i++) 
            // build arrays from 5 to 20 elements long
            int[] array = new int[TestUtils.randomInt(5, 20)];
            System.out.print("int[] array = ");
            for (int j = 0; j < array.length; j++) 
                // with values from 0 to 100
                array[j] = TestUtils.randomInt(0, 100);
                System.out.print(array[j] + ", ");
            
            System.out.println(";");

            ArraySearcher mSearcher = new ArraySearcher(array);
            for (int j = 0; j < array.length; j++) 
                int a = TestUtils.randomInt(0, 100);
                int b = TestUtils.randomInt(a, 100);
                int c = TestUtils.randomInt(0, 20);
                ArraySearcherTest.testBoth(array, mSearcher, a, b, c);
            

        


   

【讨论】:

findLowestIndex 写的有问题。它只返回传入的值或 null。看起来很容易修复,只需将最终返回 null 更改为我认为更好的东西。适用于您的示例,因为它们都返回您开始时传入的第三个参数。由于我对分段树不是很熟悉,因此仍在考虑效率方面的考虑,但无论如何看起来很有希望。 效率和技术对我来说很合适。好的!需要大量的单元测试来证明这段代码是正确的,但我还没有彻底检查过。最好按照我所说的进行解释,无论如何这比代码更重要。 首先,感谢您的回答!我浏览了您的代码,我无法获得其中的每一个部分,我非常感谢您的解释,我热切地等待着。再次感谢! :) Ech...您是对的(在您对编辑的评论中)。当解决方案跨越多棵树时,这并不能解决它。希望你能解决这个问题,我很想看看有没有解决办法。 这项新技术听起来应该可以很好地处理 O(log n) 查询,但我不相信设置是 O(n log n)。当然,排序是 O(n log n),但是你正在构建 n棵树,我认为构建每棵树需要 O(n log n),所以我认为你的设置时间为 O(n^2 log n )。【参考方案5】:

也许我错过了什么。这不符合您的要求吗?

for (int i = c; i < n; i++)

    if (a < ar[i] && ar[i] < b)
        return i;

【讨论】:

不过,这将是 O(n),O(log n) 似乎意味着二进制搜索,所以我认为问题中缺少一些东西。 是的,这就是我错过的。我只是跳过了日志部分:) @ChrisChilvers O(log n) 也可以暗示某种线段树。实际上的东西我在问题中没有提到,我会立即更新。 @Abody97,二叉树的降序可以被认为是二叉搜索。【参考方案6】:

构建一个索引数组并对它们进行排序。也就是说,给定数组[20, 0, 10, 15, 5],您将创建一个初始数组[0, 1, 2, 3, 4]。现在您对索引数组进行排序以反映按排序顺序排列的项目。排序后的索引将是[1, 4, 2, 3, 0]。你最终得到两个数组:

original = [20, 0, 10, 15, 5]
tag = [1, 4, 2, 3, 0]

可以通过标签数组对原数组进行二分查找。也就是说,您的比较函数比较original[tag[x]]original[tag[y]]。这解决了“索引在哪里”的问题。然后,您可以对段 tag[c] ... tag[n] 使用二进制搜索。

看起来应该可以。您需要一个稳定的排序,以便相等的数字保持它们的相对顺序。

【讨论】:

所以基本上,您正在压缩数字。由于每个索引都反映了排序数组中原始整数的顺序,所以段tag[c]...tag[n]没有排序,那么你将如何对其执行二进制搜索?可能是我搞错了,如果可能请解释一下。 @Abody97:嗯,你是对的。为什么这在我脑海中起作用?让我考虑一下。【参考方案7】:

如果我没看错的话,有 n-1 个查询,只有 c 改变, 在那种情况下,为什么不向后解决查询? 首先进行最后一个查询,因为它涉及数组的最后一个元素 检查元素是否位于 a 和 b 之间 如果是,则将结果存储在数组中 ans[n-1]=n-1 否则让我们放 ans[n-1]= -1,对于从 n-2 到 0 的任何下一个查询 j

if a[j] is not between a and b 
     ans[j] = ans[j+1] 
else 
     ans[j] = j

这一切都可以在 O(n) 内完成。

【讨论】:

以上是关于在数组中搜索特定范围内的整数的主要内容,如果未能解决你的问题,请参考以下文章

在大型数据库中搜索特定 ID?

在java中搜索特定记录的数组列表

在 MongoDB 中的对象数组中搜索特定属性

如何在二维 numpy 数组中搜索特定 XY 对的位置?

在字符串数组中搜索特定字符串。 [关闭]

在一个数组中搜索特定元素并复制另一个数组中的整个对应行