我有一个对象列表排序,我想找到一个对象的第一次出现和最后一次出现。在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;
答案 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
方法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;
}
}
注意:执行上述方法时必须对数组进行排序。