在Java中从数组中删除负数

17 java algorithm

我的目标是从Java中删除数组中的所有负数.

我写了下面的代码.如何提高代码的复杂性或是否有一个好的算法?

public static void main(String[] args) {
    int[] array = { 1, -3, 4, -9, 3, 4, 70, -10, 0, 7 };
    System.out.println(Arrays.toString(removeNegativeNumbers(array)));
}

public static int[] removeNegativeNumbers(int[] num) {
    List<Integer> list = new ArrayList<>();
    for (int n : num) {
        if (n >= 0) {
            list.add(n);
        }
    }
    int[] result = new int[list.size()];
    for (int i = 0; i < result.length; i++) {
        result[i] = list.get(i).intValue();
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)

use*_*265 48

Java 8流怎么样?

Arrays.stream(num).filter(s -> s >= 0).toArray();
Run Code Online (Sandbox Code Playgroud)

  • 97%的时间这是你应该使用的 (9认同)
  • 正如OP要求的那样,这确实"提高了代码的复杂性". (5认同)
  • +1表示简短的Java语法.但是这个答案并没有表明OP要求的任何算法.这只是展示了Java Java如何成为一个单行的强大功能. (5认同)
  • 最佳解决方案,但0不是负数 (3认同)

Kai*_*dul 23

如何提高代码的复杂性或是否有一个好的算法?

这是一个O(n)具有恒定空间的线性算法(忽略输出数组所需的空间).当元素为非负数时,复制元素并前进输出数组和输入数组导引头.当元素为负数时,仅前进输入数组导引头(indx)并避免复制到输出数组.

public static int[] removeNegativeNumbers(int[] num) {
    int[] output = new int[num.length];
    int k = 0;
    for(int i = 0; i < num.length; i++) {
       if(num[i] >= 0) {
           output[k++] = num[i];
       }
    }
    return Arrays.copyOfRange(output, 0, k);
}
Run Code Online (Sandbox Code Playgroud)

节省空间的解决方案

您可以通过将输入数组转换为保持输出(如插入排序方式)来就地生成算法,以避免输出数组的空间开销.

public static int removeNegativeNumbers(int[] num) {
    int k = 0;
    for(int i = 0; i < num.length; i++) {
       if(num[i] >= 0) {
           num[k++] = num[i];
       }
    }
    // Now input array is holding the output data
    // Return the length of output array
    return k;
}

// Usage: int newLen = removeNegativeNumbers(array);
Run Code Online (Sandbox Code Playgroud)

这被称为两指针技术,非常简单但有用,可以解决许多经典难题,如数组重复数据删除,合并两个排序数组,两个排序数组的交集等.

  • +1有几个原因.首先,两个实现:一个简单的实现和一个更有效,"更棘手"的实现.其次,预先指定数组大小(与输入数组大小相同).*question*中的代码使用了一个`ArrayList`,它根据需要动态调整大小,并且API隐藏了这样一个事实:数据结构可以扩展*远远超出*算法的需要,因此隐藏了潜在的重要性低效率. (2认同)

Ins*_*sac 9

我认为我们在效率方面不会比@ kaidul-islam的答案(以及简洁性方面的@ user6904265)更好.

我只是在一个非常具体的情况下添加一个应该具有一定价值的解决方案,其中很少出现负数而且数组非常大.

基本思路是推迟实际副本,直到找到负值,然后使用System.arraycopy进行复制.如果没有找到否定,将返回源数组.

import java.util.*;

public class NotNegativeTest {

    public static void main(String[] args) {
         int[] array = { 1, -3, 4, -9, 3, 4, 70, -10, 0, 7 }; 
         System.out.println(Arrays.toString(removeNegativeNumbers(array))); 
    } 

    public static int[] removeNegativeNumbers(int[] num) {
         int[] output = new int[num.length];
         int k = 0;
         int i = 0; 
         int last=-1;
         int howmany=0;

         while(i < num.length) {
             if(num[i] < 0) {
                howmany=i-last-1;
                switch(howmany) {
                    case 0: break;
                    case 1: 
                            output[k]=num[last+1];
                            k++;
                            break;
                    default:        
                            System.arraycopy(num, last+1,  output, k, howmany);
                            k+=howmany;
                }
                last=i;
             } 
             i++;
         } 
         if (last>=0) {
              if(last!=i-1) {
                howmany=i-last-1;  
                System.arraycopy(num, last+1,  output, k, howmany);
                k+=howmany;
            }
        } else {
            return num;
        }

         return Arrays.copyOfRange(output, 0, k);
     }

}
Run Code Online (Sandbox Code Playgroud)

我找到了写JMH微基准测试的时间.

我用过这些配置:

Options opts = new OptionsBuilder()
  .include(MyBenchmark.class.getSimpleName())
  .mode(Mode.AverageTime)
  .warmupIterations(1)
  .warmupTime(TimeValue.seconds(5))
  .measurementIterations(10)
  .measurementTime(TimeValue.seconds(5))
  .jvmArgs("-server")
  .forks(1)
  .build();
new Runner(opts).run();
Run Code Online (Sandbox Code Playgroud)

(免责声明:我的第一次JMH测试,所以如果有人有一些建议,我会很乐意改变它并更新结果)

以下是结果:

# Run complete. Total time: 00:02:54

Benchmark                             Mode  Cnt  Score   Error  Units
MyBenchmark.testMethodUser6904265     avgt   10  0,201 ± 0,040   s/op
MyBenchmark.testMethodInsac           avgt   10  0,093 ± 0,022   s/op
MyBenchmark.testMethodKaidul          avgt   10  0,124 ± 0,029   s/op
Run Code Online (Sandbox Code Playgroud)

现在,在为我欢呼之前,请看一下测试的偏见:

 int[] array = new int[10000000];
 array[0]=-5;
 array[10000]=-3;
 array[40000]=-3;
 array[8000000]=-3;

 int[] answer=NotNegativeTest.removeNegativeNumbers(array);
Run Code Online (Sandbox Code Playgroud)

所以在这里要注意的不是我的方法赢了(测试是为我赢得的方法而写的:-)但是即使在这种极端情况下,通用的kaidul-islam方法也有多接近.

更新:这里是我写jmh基准的链接,所以你可以验证更真实的场景(如果有人发现我如何设置测试的问题,请告诉我).对于我在另一个答案上所做的测试,我对Trove有所依赖.


Shl*_*oim 7

这将通过单次迭代就地执行:

保持2个索引:srcdst在原始数组上.两者都初始化为0.

当数字为正数时,将其复制num[src]num[dst]并推进两个索引

当一个数字是负数时,只需提前src.

返回原始数组dst作为其新大小.

public static int removeNegativeNumbers(int[] num) {
  int dst=0;
  for (int src=0 ; src<num.length ; ++src)
    if (num[src]>=0)
      num[dst++] = num[src];
  return dst;
}
Run Code Online (Sandbox Code Playgroud)