在排序数组中查找整数出现的边界的问题(Java)

时间:2015-12-28 03:08:10

标签: java arrays sorting data-structures binary-search

我正在尝试在排序数组中找到键的边界。例如,函数接受一个数组和一个键,并根据数组的内容和给定的键返回下限和上限(例如public static int [] boundFinder)。

下限将是密钥出现的最低索引,上限将是密钥出现的最高索引。以下是具有正确输出的示例输入:

排序数组:{2,3,4,5,5,5,5,5,9,9,14,40}

键:5

输出:3,7

在我的代码中,我使用二进制搜索,因为数组已排序。但是,我很容易获得上限,但是在获得下限方面一直存在问题。我知道我可以修改我的二进制搜索方法,但是尝试使用Java的数学库来获得我的边界的正确最小值和最大值。我还将结果存储到数组中以返回给用户。

如果我的方法可以改进,请告诉我(下面的代码)。我想知道我是否可能使用不同的数据结构或算法以最佳速度解决这个问题?我知道我总是可以遍历整个阵列,但这不是最好的。

提前谢谢!

public static int[] boundFinder(int[] array, int key) {
    int [] resultArr = new int[2];

    int floorIndex = -1;
    int ceilingIndex = array.length;

    while (floorIndex + 1 < ceilingIndex) {
        resultArr[0] = Math.min(resultArr[0], binarySearch(array, floorIndex, ceilingIndex, key));
        resultArr[1] = Math.max(resultArr[1], binarySearch(array, floorIndex, ceilingIndex, key));
        floorIndex++;
    }

    return resultArr;
}

public static int binarySearch(int[] array, int floorIndex, int ceilingIndex, int key) {
    while (floorIndex + 1 < ceilingIndex) {
        int distance = ceilingIndex - floorIndex;
        int halfDistance = distance/2;
        int guessIndex = floorIndex + halfDistance;

        int guessValue = array[guessIndex];

        if (guessValue == key) {
            return guessIndex;
        } else if (guessValue > key) {
            ceilingIndex = guessIndex;
        } else {
            floorIndex = ceilingIndex;
        }
    }
    return 0;
}

4 个答案:

答案 0 :(得分:3)

您的逻辑适用于上(右)边界,因为您不断增加数组的较低起始索引,当您到达元素所在元素的最后一个索引时,二进制搜索将找到该元素并返回。

相同的逻辑不适用于较低(左)边界,因为在找到元素后立即返回。一旦到达元素所在的最后一个索引,它将始终返回0,这就是发生的事情。

您的方法的主要缺点是binarysearch方法调用的数量等于数组中元素的数量。因此算法的复杂性变为O(n log (n))。这比O(n)更糟糕,你可以通过简单的线性搜索来实现。{/ p>

您需要编写两个单独的实现来获取数组中元素的left mostright most索引。

因为一旦在数组中找到元素,就需要向右或向左移动以找到元素的边界索引。移入任一端边界的逻辑与其他边界不同。

找到元素后,检查索引的左侧,如果它也等于键,则再次调用搜索。

public static int leftSearch(int a[], int key, int l, int h) {
  if (l<=h) {
    int mid = (l+h)/2;
    if (a[mid] == key) {
      if (mid > 0 && a[mid-1] == key) {
        return leftSearch(a, key, l, mid-1);
      } else {
        return mid;
      }
    }
    if (a[mid] > key) {
      return leftSearch(a, key, l, mid-1);
    } else {
      return leftSearch(a, key, mid+1, h);
    }
  }
  return -1;
}

找到元素后,检查索引的右侧,如果它也相等,则再次调用搜索。

public static int rightSearch(int a[], int key, int l, int h) {
  if (l<=h) {
    int mid = (l+h)/2;
    if (a[mid] == key) {
      if (mid<h && a[mid+1] == key) {
        return rightSearch(a, key, mid + 1, h);
      } else {
        return mid;
      }
    }
    if (a[mid] > key) {
      return rightSearch(a, key, l, mid-1);
    } else {
      return rightSearch(a, key, mid+1, h);
    }
  }
  return -1;
}

主要方法:

public static void main(String[] args) {
  int array[] = new int[]{ 2, 3, 4, 5, 5, 5, 5, 5, 9, 9, 14, 40 };
  int leftIndex = leftSearch(array, 5, 0, array.length-1);
  System.out.println(leftIndex);
  int rightIndex = rightSearch(array, 5, 0, array.length-1);
  System.out.println(rightIndex);
}

输出:

3
7

答案 1 :(得分:0)

我只会告诉你一个点击我的方法:

int min=-1; int max=0;
public int[] boundFinder(int[] array,int key)
{
    int[] resultArray=new int[2];
    for(int i=0;i<array.length;i++)
{
   if(array[i]==key)
{
     max=i;
     if(min==-1)//so that only lower bound is assigned to min.
     {
         min=i;
      }
}
}
 resultArray[0]=min;  //stores the bounds
 resultArray[1]=max;
 return resultArray;
}

min的值只会更改一次,因为我们使用的是if语句。但是max将具有最终或最高限制值。最后,我已经存储了这些数组中的值(resultArray)并返回它。

  • 如果您想让它更简单,您可以尝试使用具有一些预定义函数的array list来返回索引。

答案 2 :(得分:0)

您可以按照以下方式自定义二进制搜索,以达到您的要求......

public static void main(String[] args){
        int[] array = { 2, 3, 4, 5, 5, 5, 5, 5, 9, 9, 14, 40 };
        //int[] array = { 1, 2, 5, 6, 6, 6, 6, 6, 6, 6, 6, 10 };
        int key = 5; 

        int len = array.length;

        int [] resultArr = new int[2];
        resultArr[0]=0;
        resultArr[1]=len-1;


        while(!(array[resultArr[0]]==key && array[resultArr[1]]==key)){
            int mid = (resultArr[0]+resultArr[1])/2;
            if(array[mid]>key){
                    resultArr[1] = mid-1;
            }
            else if (array[mid]<key){
                    resultArr[0]=mid+1;
            }
            else{
                if(array[resultArr[1]]!=key){
                    resultArr[1]=resultArr[1]-1;
                }
               else if (array[resultArr[0]]!=key)
                    resultArr[0]=resultArr[0]+1;
            }
        }

        System.out.println("LowerBoundary - "+resultArr[0]+" UpperBoundary - "+resultArr[1]);

    }

答案 3 :(得分:0)

使用单个二进制搜索实现,而不是两个单独的二进制搜索实现,它总是找到最左边的出现。使用Declare @number table( value int ) insert into @number values(0); insert into @number values(1); insert into @number values(2); insert into @number values(3); insert into @number values(4); insert into @number values(5); insert into @number values(6); insert into @number values(7); insert into @number values(8); insert into @number values(9); Declare @MaxLimit int=999 ; WITH CTE AS ( SELECT value FROM @number A UNION ALL SELECT a.value + 10 FROM CTE A WHERE A.value < @MaxLimit-9 ) SELECT value FROM cte --WHERE value <= 999 ORDER BY value 调用一次以找到第一个匹配项,然后使用key再次调用它,以在 key+1之后找到第一个出现的数字。您想从第二次调用返回的结果中减去1。

因此,您的key方法会执行此操作(假设您使用的是Java内置的binarySearch方法):

boundFinder

这假设int min = binarySearch(array, key); int max = binarySearch(array, key+1) - 1; key都在数组中。如果你不能假设key+1在数组中,那么你会写:

key+1