Java等价于c ++ equal_range(或lower_bound& upper_bound)

时间:2013-03-24 20:50:18

标签: java binary-search lower-bound upperbound

我有一个对象列表排序,我想找到一个对象的第一次出现和最后一次出现。在C ++中,我可以轻松地使用std :: equal_range(或者只使用一个lower_bound和一个upper_bound)。

例如:

bool mygreater (int i,int j) { return (i>j); }

int main () {
  int myints[] = {10,20,30,30,20,10,10,20};
  std::vector<int> v(myints,myints+8);                         // 10 20 30 30 20 10 10 20
  std::pair<std::vector<int>::iterator,std::vector<int>::iterator> bounds;

  // using default comparison:
  std::sort (v.begin(), v.end());                              // 10 10 10 20 20 20 30 30
  bounds=std::equal_range (v.begin(), v.end(), 20);            //          ^        ^

  // using "mygreater" as comp:
  std::sort (v.begin(), v.end(), mygreater);                   // 30 30 20 20 20 10 10 10
  bounds=std::equal_range (v.begin(), v.end(), 20, mygreater); //       ^        ^

  std::cout << "bounds at positions " << (bounds.first - v.begin());
  std::cout << " and " << (bounds.second - v.begin()) << '\n';

  return 0;
}

在Java中,似乎没有简单的等价?我应该如何处理与

相同的范围
List<MyClass> myList;

顺便说一下,我使用的是标准导入java.util.List;

9 个答案:

答案 0 :(得分:5)

在Java中,使用Collections.binarySearch在排序列表中查找相等范围的下限(Arrays.binarySearch为数组提供类似的功能)。然后继续线性迭代,直到达到相等范围的末尾。

这些方法适用于实现Comparable接口的方法。对于未实现Comparable的类,您可以提供自定义 Comparator的实例,以便比较特定类型的元素。

答案 1 :(得分:3)

我们可以借助Java库函数以及定义自己的 LowerBound和UpperBound函数来找到下界和上限。

{#case-1}

如果不存在该数字,则下界和上限都将是 相同的在这种情况下, lb ub 将是数组的插入点,即应插入数字以保持数组排序的点。

示例1:

6 1 // 6 is the size of the array and 1 is the key
2 3 4 5 6 7 here lb=0 and ub=0 (0 is the position where 1 should be inserted to keep the array sorted)

6 8 // 6 is the size of the array and 8 is the key
2 3 4 5 6 7  here lb=6 and ub=6 (6 is the position where 8 should be inserted to keep the array sorted)

6 3 // 6 is the size of the array and 3 is the key
1 2 2 2 4 5  here lb=4 and ub=4 (4 is the position where 3 should be inserted to keep the array sorted)


    

{#case-2(a)}

如果该数字存在且频率为1。即出现的次数为1

lb =该数字的索引。
ub =下一个数字的索引,该数字刚好大于数组中的该数字 即 ub =该数字的索引+1

示例2:

6 5 // 6 is the size of the array and 5 is the key
1 2 3 4 5 6 here lb=4 and ub=5
    

{#case-2(b)}

如果存在数字并且频率大于1。 数字出现多次。在这种情况下 lb 将是该数字第一次出现的索引。 ub 将是该数字+1的最后一次出现的索引。 即该数字的索引刚好大于数组中的键。

示例3:

 11 5 // 11 is the size of the array and 5 is the key
 1 2 3 4 5 5 5 5 5 7 7 here lb=4 and ub=9

Lower_Bound和Upper_Bound的实现

方法1: 通过库功能

// a是数组,x是目标值

int lb=Arrays.binarySearch(a,x); // for lower_bound

int ub=Arrays.binarySearch(a,x); // for upper_bound

if(lb<0) {lb=Math.abs(lb)-1;}//if the number is not present

else{ // if the number is present we are checking 
    //whether the number is present multiple times or not
    int y=a[lb];
    for(int i=lb-1; i>=0; i--){
        if(a[i]==y) --lb;
        else break;
    }
}
  if(ub<0) {ub=Math.abs(ub)-1;}//if the number is not present

  else{// if the number is present we are checking 
    //whether the number is present multiple times or not
    int y=a[ub];
    for(int i=ub+1; i<n; i++){
        if(a[i]==y) ++ub;
        else break;
    }
    ++ub;
}

方法2: 通过定义自己的功能

//下限

static int LowerBound(int a[], int x) { // x is the target value or key
  int l=-1,r=a.length;
  while(l+1<r) {
    int m=(l+r)>>>1;
    if(a[m]>=x) r=m;
    else l=m;
  }
  return r;
}

//表示上限_p

 static int UpperBound(int a[], int x) {// x is the key or target value
    int l=-1,r=a.length;
    while(l+1<r) {
       int m=(l+r)>>>1;
       if(a[m]<=x) l=m;
       else r=m;
    }
    return l+1;
 }

     

或者我们可以使用

int m=l+(r-l)/2;

但如果我们使用

int m=(l+r)>>>1; // it is probably faster

但是使用以上任何一个计算m的公式都可以防止溢出

在C和C ++(>>>)运算符不存在的情况下,我们可以这样做:

int m= ((unsigned int)l + (unsigned int)r)) >> 1;

//在程序中实现

import java.util.*;
import java.lang.*;
import java.io.*;
public class Lower_bound_and_Upper_bound {

public static void main (String[] args) throws java.lang.Exception
{
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    StringTokenizer s = new StringTokenizer(br.readLine());
    int n=Integer.parseInt(s.nextToken()),x=Integer.parseInt(s.nextToken()),a[]=new int[n];
    s = new StringTokenizer(br.readLine());
    for(int i=0; i<n; i++) a[i]=Integer.parseInt(s.nextToken());
    Arrays.sort(a);// Array should be sorted. otherwise lb and ub cant be calculated
    int u=UpperBound(a,x);
    int l=LowerBound(a,x);
    System.out.println(l+" "+u);
 }
}

#用于计算下限和上限的等效C ++代码

  #include<bits/stdc++.h>
  #define IRONMAN ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
  using namespace std;
  typedef long long int ll;
  int main() {
    IRONMAN
    int n,x;cin>>n>>x;
    vector<int> v(n);
    for(auto &i: v) cin>>i;
    ll lb=(lower_bound(v.begin(),v.end(),x))-v.begin();// for calculating lb
    ll ub=(upper_bound(v.begin(),v.end(),x))-v.begin();// for calculating ub
    cout<<lb<<" "<<ub<<"\n";
    return 0;
  }

答案 2 :(得分:1)

cpp中Lower_bound的Java等效值为

public static int lower(int arr[],int key){
    int low = 0;
    int high = arr.length-1;
    while(low < high){
        int mid = low + (high - low)/2;
        if(arr[mid] >= key){
            high = mid;
        }
        else{
            low = mid+1;
        }
    }
    return low;
}

但是如果数组中不存在键,则上面的代码段将给出下界

cpp中的upper_bound的Java等效值为

public static int upper(int arr[],int key){
    int low = 0;
    int high = arr.length-1;
    while(low < high){
        int mid = low + (high - low+1)/2;
        if(arr[mid] <= key){
            low = mid;
        }
        else{
            high = mid-1;
        }
    }
    return low;
}

但是如果数组中不存在键,则上面的代码段将给出键的下限

答案 3 :(得分:0)

您可以尝试这样的事情:

    public class TestSOF {

        private ArrayList <Integer> testList = new ArrayList <Integer>();
        private Integer first, last;

        public void fillArray(){

            testList.add(10);
            testList.add(20);
            testList.add(30);
            testList.add(30);
            testList.add(20);
            testList.add(10);
            testList.add(10);
            testList.add(20);
        }

        public ArrayList getArray(){

            return this.testList;
        }

        public void sortArray(){

            Collections.sort(testList);
        }

        public void checkPosition(int element){

            if (testList.contains(element)){

                first = testList.indexOf(element);
                last = testList.lastIndexOf(element);

                System.out.println("The element " + element + "has it's first appeareance on position " 
            + first + "and it's last on position " + last);
            }

            else{

                 System.out.println("Your element " + element + " is not into the arraylist!");
           }
        }

        public static void main (String [] args){

            TestSOF testSOF = new TestSOF();

            testSOF.fillArray();
            testSOF.sortArray();
            testSOF.checkPosition(20);
        } 

}

答案 4 :(得分:0)

在二进制搜索中,当您找到该元素时,您可以继续在其左侧进行二进制搜索,以便查找第一个匹配项,并向右搜索以查找最后一个元素。 这个想法应该用代码清楚:

/*
B: element to find first or last occurrence of
searchFirst: true to find first occurrence, false  to find last
 */
Integer bound(final List<Integer> A,int B,boolean searchFirst){
    int n = A.size();
    int low = 0;
    int high = n-1;
    int res = -1;   //if element not found
    int mid ;
    while(low<=high){
        mid = low+(high-low)/2;
        if(A.get(mid)==B){
            res=mid;
            if(searchFirst){high=mid-1;}    //to find first , go left
            else{low=mid+1;}                // to find last, go right
        }
        else if(B>A.get(mid)){low=mid+1;}
        else{high=mid-1;}
    }
    return res;
}

答案 5 :(得分:0)

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Collections;
import java.util.Vector;

public class Bounds {

    public static void main(String[] args) throws IOException {
        Vector<Float> data = new Vector<>();
        for (int i = 29; i >= 0; i -= 2) {
            data.add(Float.valueOf(i));
        }
        Collections.sort(data);
        float element = 14;
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out));
        String string = bf.readLine();
        while (!string.equals("q")) {
            element=Float.parseFloat(string);
            int first = 0;
            int last = data.size();
            int mid;
            while (first < last) {
                mid = first + ((last - first) >> 1); 
                if (data.get(mid) < element)  //lower bound. for upper use <= 
                    first = mid + 1; 
                else 
                    last = mid;
            }
            log.write("data is: "+data+"\n");
            if(first==data.size())
                first=data.size()-1;
            log.write("element is : " + first+ "\n");
            log.flush();
            string= bf.readLine();
        }
        bf.close();
    }

}

这是与c ++类似的lower_bound和upper_bound的实现。 请注意,您要搜索的元素不需要出现在向量或列表中。此实现仅提供元素的上限和下限。

答案 6 :(得分:0)

只需使用二进制搜索

private static int lowerBound(int[] a, int low, int high, int element){
    while(low < high){
        int middle = low + (high - low)/2;
        if(element > a[middle])
            low = middle + 1;
        else 
            high = middle;
    }
    return low;
}


private static int upperBound(int[] a, int low, int high, int element){
    while(low < high){
        int middle = low + (high - low)/2;
        if(a[middle] > element)
            high = middle;
        else 
            low = middle + 1;
    }
    return low;
}

答案 7 :(得分:0)

Java已经具有内置的二进制搜索功能,该功能可以计算数组中元素的上下限,因此无需实现自定义方法。

当我们谈论上下限或相等范围时,我们总是指容器的索引(在这种情况下为ArrayList),而不是所包含的元素。 让我们考虑一个数组(假设数组已排序,否则我们先对其排序):

List<Integer> nums = new ArrayList<>(Arrays.asList(2,3,5,5,7,9,10,18,22));

“下界”函数必须返回数组的索引,必须在其中插入元素以保持排序的数组。 “上限”必须返回数组中最小元素的索引,该索引大于要查找的元素。 例如

lowerBound(nums, 6)

必须返回3,因为3是数组的位置(从0开始计数),必须插入6以保持数组排序。

The

upperBound(nums, 6)

必须返回4,因为数组中最小元素的位置为4,即比5或6(位置4的数字7)更大

在C ++中的标准库中,两种算法都已在标准库中实现。在Java中,您可以使用

Collections.binarySearch(nums, element)

对数时间复杂度计算位置。

如果数组包含元素,则 Collections.binarySearch 返回元素的第一个索引(在2上方的数组中)。否则,它将返回一个负数,该负数指定下一个更大元素在数组中的位置,从数组的最后一个索引开始向后计数。在此位置找到的数字是数组中最小元素,大于要查找的元素

例如,如果您致电

int idx = Collections.binarySearch(nums, 6)

该函数返回-5。如果从数组的最后一个索引(-1,-2,...)开始倒数,则索引-5指向数字7-数组中大于元素6的最小数字。

结论: 如果排序后的数组包含寻找元素,则下限是元素的位置,上限是下一个较大元素的位置。

如果数组不包含元素,则下限为位置

Math.abs(idx) - 2

上限是位置

Math.abs(idx) - 1

其中

idx = Collections.binarySearch(nums, element)

请始终牢记边境情况。例如,如果您在上面指定的数组中寻找1:

idx = Collections.binarySearch(nums, 1)

函数返回-1。因此,upperBound = Math.abs(idx)-1 = 0-元素2位于位置0。但是元素1没有下限,因为2是数组中最小的数字。 相同的逻辑适用于大于数组中最大数字的元素:如果查找数字的下限/上限,则将获得

  idx = Collections.binarySearch(nums, 25) 
ix = -10。您可以计算下限:lb = Math.abs(-10)-2 = 8,这是数组的最后一个索引,但是没有上限,因为22已经是数组中的最大元素,并且没有元素位于位置9。

equal_range在范围从下限索引到上限(但不包括上限)的范围内指定数组的所有索引。 例如,上面数组中数字5的相等范围是索引

 [2,3]

数字6的相等范围为空,因为数组中没有数字6。

答案 8 :(得分:0)

以这种方式尝试下限和上限。 很容易实现。

import java.util.Arrays;

class LowerBoundUpperBound{
    public static void main(String[] args) {
        int a[] = {1, 2, 3, 4, 5, 5, 5, 5, 5, 7, 7};

        int key = 5;
        int pos = Arrays.binarySearch(a, key);
        int lb = (pos < 0) ? ~pos - 1 : getlb(pos, a);
        int ub = (pos < 0) ? ~pos : getUb(pos, a);

        System.out.println("Lower Bound=" + lb);
        System.out.println("Upper Bound=" + ub);

        // You can also try on a[] = {1 ,2 ,3 ,4 ,5 ,6};
        // For key=5, lb=3 and ub=5


    }

    private static int getlb(int pos, int[] a) {
        while (pos - 1 >= 0 && a[pos] == a[pos - 1]) pos--;
        return pos - 1;
    }

    private static int getUb(int pos, int[] a) {
        while (pos + 1 < a.length && a[pos] == a[pos + 1]) pos++;
        return pos + 1;

    }
}

注意:执行上述方法时必须对数组进行排序。