【问题标题】:Find second min element from array从数组中查找第二个最小元素
【发布时间】:2018-08-06 10:01:19
【问题描述】:

任何人都可以将其转换为 Java 函数式 (lambda):

public int findSecondMin(int arr[]) {

    int min = Integer.MAX_VALUE, secondMin = Integer.MAX_VALUE;
    for (int i = 0; i < arr.length; i++) {
        if (min > arr[i]) {
            secondMin = min;
            min = arr[i];
        } else if (secondMin > arr[i]) {
            secondMin = arr[i];
        }
    }
    return secondMin;
}

我尝试对此应用过滤器,但它不起作用。

【问题讨论】:

    标签: java arrays lambda java-8 java-stream


    【解决方案1】:

    使用IntStream,您可以轻松对其进行排序并跳过第一个元素:

    public int findSecondMin(int[] arr)
    {
        return IntStream.of(arr).sorted().skip(1).findFirst().orElse(Integer.MAX_VALUE);
    }
    

    当然,您不必使用流。 java.util.Arrays 有一个很好的排序方法,然后你可以只取第二个元素:

    public int findSecondMin(int[] arr)
    {
        Arrays.sort(arr);
        return arr.length < 2 ? Integer.MAX_VALUE : arr[1];
    }
    

    为避免对整个数组进行排序,我们可以采用您的方法并将其调整为流上的自定义归约:

    public int findSecondMin(int[] arr)
    {
        return IntStream.of(arr).boxed().reduce(
            new int[] {Integer.MAX_VALUE, Integer.MAX_VALUE},
            (mins, i) -> {
                return new int[] {Math.min(i, mins[0]), Math.min(Math.max(i, mins[0]), mins[1])};
            }, (mins1, mins2) -> {
                int[] lesser = mins1[0] < mins2[0] ? mins1 : mins2;
                int[] larger = mins1[0] < mins2[0] ? mins2 : mins1;
                return new int[] {lesser[0], Math.min(lesser[1], larger[0])};
            }
        )[1];
    }
    

    与基于 for 循环的实现相比,它可能更难阅读,但可以并行工作。

    【讨论】:

    • 如果对数组进行排序,则变为 O(nlogn)。
    • 是的,这是一个缺点。但是我并没有真正看到基于流的解决方案如何在不依赖外部状态或最终与带有自定义 reduce 对象的原始 for 循环完全相同的情况下实现目标。至少,这些基于排序的解决方案非常易读。
    • @Malte Hartwig 能否请您解释一下时间复杂度,根据我的排序是 o(n^2)?
    • @SandeepTiwari 复杂性取决于选择的排序算法。像冒泡排序这样的基本方法确实倾向于 n2,但 Java 函数将依赖于更高级的技术。来自Arrays.sort() 的JavaDoc:“实现说明:排序算法是双轴快速排序”。您可以检查 JavaDoc 的流排序,然后检查这些算法。
    【解决方案2】:

    在所有数字都是唯一的情况下,即使没有排序也有一种方法。过滤掉最小值,然后再次要求另一个,得到第二个最小值。

    int firstMin = Arrays.stream(arr).min().getAsInt();
    int secondMin = Arrays.stream(arr).filter(i -> i != firstMin).min().getAsInt();
    
    System.out.println(firstMin);  // prints 2
    System.out.println(secondMin); // prints 3
    

    编辑:还有另一种使用 TreeSet 实现的方法来存储已排序的值。删除最低的元素并再次请求第一个元素 - 它导致第二个最低元素:

    SortedSet<Integer> sortedSet = Arrays.stream(arr)
                                         .boxed()
                                         .collect(Collectors
                                             .toCollection(TreeSet::new));
    sortedSet.remove(sortedSet.first());
    int secondMin = sortedSet.first();
    
    System.out.println(secondMin); // prints 3
    

    【讨论】:

      【解决方案3】:

      在这里您可以找到示例

          List<Integer> numbers = Arrays.asList(10, 2, 3, 4, 5, 6);
                  numbers.sort(Comparator.naturalOrder());
                  System.out.println(numbers.get(1));
      

      【讨论】:

        【解决方案4】:

        编辑: 如 cmets 中所述 - 以下解决方案不适用于并行处理!

        为了避免排序,我们可以使用reduce 函数。并归约为“pair like structure”(在模仿数组的例子中):

        public class Test {
        
            private static final int SECOND_LOWEST = 1;
            private static final int LOWEST = 0;
            @Test
            public void test() {
                Integer[] arr = {0, 3, 4, 5, 2, 3, 5, 6, 7, 8, 1};
                List<Integer> list = Arrays.asList(arr);
                int[] identity = {Integer.MAX_VALUE, Integer.MAX_VALUE};
                int[] result = list.stream()
                               .reduce(identity, (acc, elem) -> calculate(acc, elem), (acc1, acc2) -> acc1);
        
               System.out.println(Arrays.toString(result));
            }
        
            private int[] calculate(int[] acc, Integer elem) {
                if (acc[LOWEST] > elem) {
                    int prevLowest = acc[LOWEST];
                    acc[LOWEST] = elem;
                    acc[SECOND_LOWEST] = prevLowest;
                } else if (acc[SECOND_LOWEST] > elem) {
                        acc[SECOND_LOWEST] = elem;            
            }
            return acc;
        }
        

        【讨论】:

        • (acc1, acc2) -&gt; acc1... 只需添加parallel 看看你会得到的结果
        • 我没想到并行化。但你说得对,完整的解决方案应该考虑到这一点。
        • 既然要修改临时数组,就应该使用collect,它专门用来处理所谓的Mutable Reduction
        【解决方案5】:
        public int min(){
          int a[] ={2,5,1,3};
          int min, secondMin = a[0];
          for(int i=0;i<a.length;i++){
                if(min>a[i]){
                     secondMin =min;
                     min= a[i];
                   }
              }
          return secondMin;
        }
        

        我从上面的代码段中得到了第二分钟...试试这个.....

        【讨论】:

          猜你喜欢
          • 2011-04-12
          • 2019-08-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-01-08
          • 2015-12-02
          • 2011-08-29
          相关资源
          最近更新 更多