21

ソートされたオブジェクトのリストがあり、オブジェクトの最初の出現と最後の出現を見つけたいです。C ++では、std :: equal_range(または1つのlower_boundと1つの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を使用しています。

4

9 に答える 9

11

JavaではCollections.binarySearch、ソートされたリストで等しい範囲の下限を見つけるために使用します(Arrays.binarySearch配列に同様の機能を提供します)。これにより、同じ範囲内の位置が得られ、それ以上の保証はありません。

リストに指定されたオブジェクトと等しい複数の要素が含まれている場合、どれが見つかるかは保証されません。

次に、等しい範囲の終わりに達するまで、前方に直線的に繰り返し、次に後方に繰り返します。

これらのメソッドは、インターフェイスを実装するオブジェクトに対して機能しComparableます。を実装しないクラスの場合、特定のタイプの要素を比較するためのカスタムComparableのインスタンスを提供できます。 Comparator

于 2013-03-24T20:56:00.007 に答える
8

Javaライブラリ関数を使用するだけでなく、独自のLowerBound関数とUpperBound関数を定義することによって、下限と上限を見つけることができます。

{#case-1}

数値が存在しない場合、下限と上限の両方が同じになります。つまり、その場合、lbubは配列の挿入ポイントになります。つまり、配列を並べ替えたままにするために数値を挿入する必要があります。

例-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 =配列内のその数値よりちょうど大きい次の数値のインデックス。ieub =その数値のインデックス+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;
}

//Upper_Boundの場合

 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;
  }
于 2020-09-12T00:04:14.800 に答える
3

Javaには、配列内の要素の下限/上限を計算するバイナリ検索機能がすでに組み込まれているため、カスタムメソッドを実装する必要はありません。

上限/下限または等しい範囲について話すときは、含まれている要素ではなく、常にコンテナー(この場合はArrayList)のインデックスを意味します。配列について考えてみましょう(配列がソートされていると仮定します。そうでない場合は、最初にソートします)。

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

「下限」関数は、配列のインデックスを返す必要があります。配列を並べ替えたままにするために、要素を挿入する必要があります。「上界と下界」は、配列内の最小の要素のインデックスを返す必要があります。これは、検索された要素よりも大きいものです。例えば

lowerBound(nums, 6)

3は配列の位置(0からカウントを開始)であるため、3を返す必要があります。配列をソートし続けるには6を挿入する必要があります。

The

upperBound(nums, 6)

4は配列内の最小要素の位置であり、 5または6よりも大きいため(位置4の番号7) 、4を返す必要があります。

標準ライブラリの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-位置0の要素2。ただし、2は配列内の最小数であるため、要素1の下限はありません。同じロジックが、配列内の最大数よりも大きい要素にも適用されます。数25の下限/上限を探すと、次のようになります。

  idx = Collections.binarySearch(nums, 25) 

ix=-10。下限を計算できます:lb = Math.abs(-10)-2 = 8、これは配列の最後のインデックスですが、22はすでに配列の最大の要素であり、位置9の要素。

equal_rangeは、下限インデックスから上限まで(ただし上限を含まない)の範囲内の配列のすべてのインデックスを指定します。たとえば、上記の配列の5番の等しい範囲はインデックスです

 [2,3]

配列に番号6がないため、番号6の等しい範囲は空です。

于 2019-09-04T12:45:34.830 に答える
2

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;
}

ただし、キーが配列に存在しない場合、上記のスニペットはキーの下限を示します

于 2020-05-31T14:27:02.283 に答える
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);
    } 
}
于 2013-03-24T21:08:45.267 に答える
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;
}
于 2015-06-15T05:39:43.770 に答える
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の実装です。検索する要素がベクターまたはリストに存在する必要はないことに注意してください。この実装は、要素の上限と下限のみを提供します。

于 2017-08-21T19:34:58.773 に答える
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;

    }
}

注:上記の方法を実行している間、配列をソートする必要があります。

于 2021-06-23T06:08:34.770 に答える
0

独自のメソッドを定義せずにlower_boundを見つけて、すべてを最初から実行したい場合は、次のコードスニペットを使用してください。お気づきかもしれませんが、このメソッドはArrayListではなくプリミティブ配列でのみ機能します。これは、CollectionsクラスにBinaryserachの開始インデックスと停止インデックスを指定する関数がないためです(java16以降)。

  • alは配列です(String[] al = new String[N];)
  • tokenアレイで探しているものです。
Arrays.sort(al, 0, N); 
int index = Arrays.binarySearch(al, 0, N , token);
while(index > 0 && al[index].equals(al[index - 1])){
       index = Arrays.binarySearch(al, 0, index, token); //lower_bound in java.
}

上限については、コードを簡単に変更できます。

于 2021-09-17T12:19:15.207 に答える