【问题标题】:Generic binary search Java通用二进制搜索 Java
【发布时间】:2015-05-27 19:29:55
【问题描述】:

我一直在努力使这段代码工作。我必须创建二进制搜索的通用二进制版本。我不确定如何在没有可比较接口的情况下比较两种泛型类型

import java.util.ArrayList; 

public class BinarySearcher<T> {
    private T[] a;

    public BinarySearcher(T[] words) {
        a = words;
    }

    public int search(T v) {
        int low = 0;
        int high = a.length - 1;
        while (low <= high) {
            int mid = (low + high) / 2;
            T midVal = a[mid];  

            if (v.compareTo(midVal) < 0) {
                low = mid - 1;
            }   

            else if (v.compareTo(midVal) > 0) {
                high = mid  + 1; 
            }
        }

        return -1;
    }

    public int compareTo(T a) {
        return this.value.compare - b;
    }
}

这是测试器类:

import java.util.Arrays;
import java.util.Scanner;
/**
  This program tests the binary search algorithm.
*/
public class BinarySearchTester {
    public static void main(String[] args) {
        String[] words = {"Alpha", "Bravo", "Charlie", "Delta", "Echo", 
            "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", 
            "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo", 
            "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "X-Ray", 
            "Yankee", "Zulu"};
        BinarySearcher<String> searcher = new BinarySearcher<String>(words);
        System.out.println(searcher.search("November"));
        System.out.println("Expected: 13");
        System.out.println(searcher.search("October"));
        System.out.println("Expected: -1");
    }
}

【问题讨论】:

  • 如果你想得到 shmancy -- 改变: int mid = (low + high) / 2; to: int mid = (low + high) >>> 1;那里的性能提升很小(至少在我的编译器/jvm上)

标签: java


【解决方案1】:

public class BinarySearcher&lt;T extends Comparable&lt;T&gt;&gt; { ...

这将使您的 BinarySearcher 可以处理可以与 compareTo 进行比较的所有内容,compareTo 应该足够通用。

【讨论】:

    【解决方案2】:

    您的泛型方法无法比较任意类型T 的两个实例,而对T 没有任何限制,也没有关于如何比较两个Ts 的其他信息。

    你有几个选择:

    T添加一个约束:

    public class BinarySearcher<T extends Comparable<T>>
    

    或者将Comparator&lt;T&gt; 传递给您的search 方法,可以调用该方法进行比较:

    public int search(T v, Comparator<T> comp) {
        // ...
        if (comp.compare(v, midVal < 0)) {
        // ...
    }
    

    旁注:我会避免在您的search 方法中调用compareTo 两次;你不知道比较这两个对象是否昂贵。而不是这个:

    if (v.compareTo(midVal) < 0) {
        low = mid - 1;
    }   
    else if (v.compareTo(midVal) > 0) {
        high = mid  + 1; 
    }
    

    做这样的事情:

    int result = v.compareTo(midVal);
    if (result < 0) {
        low = mid - 1;
    }   
    else if (result > 0) {
        high = mid  + 1; 
    }
    

    【讨论】:

      【解决方案3】:

      即使做了上面建议的所有更改,您使用的条件也不正确(您更改了低指针和高指针),并且您需要在 else if 之后添加一个 else 子句来返回索引。

      public class BinarySearcher<T extends Comparable<T>> {
          private T[] a;
      
          public BinarySearcher(T[] words) {
              a = words;
          }
      
          public int search(Comparable<T> v) {
              int low = 0;
              int high = a.length - 1;
      
              while (low <= high) {
                  int mid = (low + high) / 2;
                  T midVal = a[mid];
                  int result = v.compareTo(midVal);
      
                  if (result < 0) {
                      high = mid - 1;
                  }
      
                  else if (result > 0) {
                      low = mid + 1;
                  } 
      
                  else {
                      return mid;
                  }
              }
      
              return -1;
          }
      }
      

      【讨论】:

        【解决方案4】:

        不可能比较没有实现Comparable 接口的类型。不使用compareTo()

        【讨论】:

          【解决方案5】:

          试试这个:

          class BinarySearcher<T extends Comparable<T>>
          

          【讨论】:

            【解决方案6】:

            正如其他人已经提到的那样,让 T 扩展 Comparable 可以解决您的问题。 Buuuut 你为什么要重新发明轮子?您可以轻松使用现有的通用 Java 二进制搜索:

            System.out.println(Arrays.binarySearch(words, "November", null));
            

            请注意,如果将 null 作为可比较参数传递,则采用自然元素顺序!

            享受吧!

            【讨论】:

            • 基于 Test 类,我们不应该使用 BinarySearcher。我不太确定如何使用比较器
            【解决方案7】:

            @Erik 的回答展示了如何修改您的代码,使其要求参数类型 T 具有可比性。

            另一种选择是使用Comparator&lt;T&gt;;例如

            import java.util.ArrayList; 
            
            public class BinarySearcher<T> {
            
                private T[] a;
                private Comparator<T> c;
            
                public BinarySearcher(T[] words, Comparator<T> comparator) {
                    a = words;
                    c = comparator;
                }
            
                public int search(T v) {
                    int low = 0;
                    int high = a.length - 1;
                    while (low <= high) {
                        int mid = (low + high) / 2;
                        T midVal = a[mid];  
                        if (c.compare(v, midVal) < 0) {
                            low = mid - 1;
                        }   
                        else if (c.compare(v, midVal) > 0) {
                            high = mid  + 1; 
                        }
                    }
                    return -1;
                }
            }
            

            可以通过修改原始构造函数来组合这两种方法,以使用将要比较的第一个值转换为Comparable 实例的比较器。这可能会进行不安全的转换,如果对应于 T 的实际类型实际上不是 Comparable,则会导致 ClassCastException


            从设计的角度来看,如果BinarySearcher 构造函数检查words 是否已排序,或者对数组本身进行排序,那将是一个更好的主意。如果words 没有正确排序,二分搜索会给出错误的答案。

            【讨论】:

              【解决方案8】:

              以下类可用于对任何数据类型进行二分搜索。

              import java.util.ArrayList;
              import java.util.Comparator;
              import java.util.List;
              
              public class GenericBinarySearch{
              
                  private int midPoint(int iMin, int iMax){
                      return iMin + (iMax - iMin)/2;
                  }
              
                  public <T> int search(List<T> list, T Key, int iMin, int iMax, Comparator<T> comparator){
                      if(list == null || list.size() == 0){
                          return -1;
                      }
                      int iMid = midPoint(iMin, iMax);
                      if(iMid > iMax || iMid < iMin){
                          return -1;
                      }
                      if(comparator.compare(list.get(iMid), Key) > 0){
                          return search(list, Key, iMin, iMid-1, comparator);
                      }else if(comparator.compare(list.get(iMid), Key) < 0){
                          return search(list, Key, iMid+1, iMax, comparator);
                      }else{
                          return iMid;
                      }
                  }
              
                  public static void main(String[] args) {
                      GenericBinarySearch bs = new GenericBinarySearch();
                      List<Integer> list = new ArrayList<Integer>();
                      list.add(1);
                      list.add(2);
                      list.add(3);
                      list.add(4);
                      list.add(5);
              
                      int key = 2;
                      int iMin = 0;
                      int iMax = list.size()-1;
              
                      //Java 8 - Lambda expressions 
                      // new Comparator( public T int compare(T o1, T o2) {
                      //      return o1.compareTo(o2);
                      // }) ---> same expression is replaced by 
                      // (T o1, T o2) -> o1.compareTo(o2) or (o1,o2) -> o1.compareTo(o2)   
                      int index = bs.search(list, key, iMin, iMax, (o1,o2) -> o1.compareTo(o2));
                      System.out.println(index);
                  }
              
              
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2015-01-21
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-11-30
                相关资源
                最近更新 更多