我的目标是从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)
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)
这被称为两指针技术,非常简单但有用,可以解决许多经典难题,如数组重复数据删除,合并两个排序数组,两个排序数组的交集等.
我认为我们在效率方面不会比@ 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有所依赖.
这将通过单次迭代就地执行:
保持2个索引:src并dst在原始数组上.两者都初始化为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)
| 归档时间: |
|
| 查看次数: |
8047 次 |
| 最近记录: |