给定来自CodingBat的zeroFront notAlone任务:
返回一个数组,该数组包含与给定数组完全相同的数字,但重新排列以使所有零都在数组的开头分组。非零数字的顺序并不重要。因此{1,0,0,1}
变为{0,0,1}
。您可以修改并返回给定数组或制作一个新数组。
zeroFront([1, 0, 0, 1]) → [0, 0, 1, 1]
zeroFront([0, 1, 1, 0, 1]) → [0, 0, 1, 1, 1]
zeroFront([1, 0]) → [0, 1]
我对这个问题的解决方案在某些情况下会抛出ArrayIndexOutOfBoundsException:
public int[] zeroFront(int[] nums) {
if (nums.length == 0) {
return nums;
}
int[] zeroFront = new int[nums.length];
int zeroCounter = 0;
for (int i = 0; i < zeroFront.length; i++) {
if (nums[i] == 0) {
zeroCounter++;
}
}
for (int i = 0; i < zeroCounter; i++) {
zeroFront[i] = 0;
}
for (int i = 0; i < nums.length; i++) {
if (nums[i] != 0) {
zeroFront[zeroCounter + i] = nums[i];
}
}
return zeroFront;
}
我的问题如下:
如何解决我的问题?
如何使用Stream API解决此任务?
问题出在最后一个循环上。您不想将零的数量附加到当前位置,因为这显然会导致越界异常的索引。您正在遍历整个数组。您可以这样做:
public static int[] zeroFront(int[] nums) {
if (nums.length == 0) {
return nums;
}
int[] zeroFront = new int[nums.length];
int zeroCounter = 0;
for (int i = 0; i < zeroFront.length; i++) {
if (nums[i] == 0) {
zeroCounter++;
}
}
for (int num : nums) {
if (num != 0) {
zeroFront[zeroCounter++] = num;
}
}
return zeroFront;
}
如果要使用流,可以执行以下操作:
public int[] zeroFront(int[] nums) {
Map<Boolean, List<Integer>> partitions = Arrays.stream(nums).boxed().collect(Collectors.partitioningBy(i -> i == 0));
List<Integer> result = new ArrayList<>(partitions.get(true));
result.addAll(partitions.get(false));
return result.stream().mapToInt(i -> i).toArray();
}
请记住,使用流的解决方案可能没有那么好的性能,因为有很多装箱和拆箱操作正在进行。我们还必须在集合和数组之间进行转换。
zeroFront的索引不正确。它不应该是零计数器i,而应该是零计数器非零计数器,其中非零计数器是迄今为止看到的非零数量。请注意,这不等于i,i是迄今为止看到的所有零和非零的总数。
要解决此问题,只需保留另一个非零计数:
int nonZeroCount = 0;
for (int num : nums) {
if (num != 0) {
zeroFront[zeroCounter + nonZeroCount++] = num;
}
}
还请注意,无需循环将zeroFront的第一个非零计数器元素设置为0。int数组的元素会自动初始化为0。
对于流解决方案,您可以利用这样一个事实,即false
在
true
之前排序,并按
x!=0
:
public static int[] zeroFrontStreams(int[] nums) {
return Arrays.stream(nums).boxed().sorted(Comparator.comparing(x -> x != 0)).mapToInt(x -> x).toArray();
}
但请注意,由于这涉及排序,因此与O(n)解决方案相比,它的时间复杂性更差。还有很多装箱/拆箱操作。
或者,如果您只想在代码中的某个地方使用流,可以使用它将数组分为零和非零,然后以其他方式将非零复制到zeroFront的尾部:
public static int[] zeroFrontStreams(int[] nums) {
var partitions = Arrays.stream(nums).boxed().collect(Collectors.partitioningBy(x -> x == 0));
int[] zeroFront = new int[nums.length];
int[] nonZeros = partitions.get(false).stream().mapToInt(x -> x).toArray();
int zeroCounter = partitions.get(true).size();
System.arraycopy(nonZeros, 0, zeroFront, zeroCounter, nonZeros.length);
return zeroFront;
}
没有(un)装箱,但有一些代码重复的
Arrays.stream(nums). filter
:
public static int[] zeroFront(int[] nums) {
int[] zeroFront = new int[nums.length];
long zeroCounter = Arrays.stream(nums).filter(x -> x == 0).count();
int[] nonZeros = Arrays.stream(nums).filter(x -> x != 0).toArray();
System.arraycopy(nonZeros, 0, zeroFront, (int)zeroCounter, nonZeros.length);
return zeroFront;
}
这项任务可以在单个流语句中使用流IPA来完成,而无需使用自定义收集器进行排序。
要创建收集器,我们可以使用静态方法Collector.of()
。我们需要提供收集器内部使用的可变容器,并指定如何将流元素添加到容器中,以及如何在并行执行的情况下合并两个容器。
在下面的代码中,ArrayDesk用作可变容器。它是一个由数组支持的集合,允许在后面和前面以恒定时间O(1)插入新元素。
因为我们无法从并行执行中获得任何优势,所以实现了合并器函数来抛出断言错误。
Finisher函数将底层集合转换为数组。
该代码通过了编码BAT的所有测试:
public int[] zeroFront(int[] nums) {
return Arrays.stream(nums)
.boxed()
.collect(Collector.of(
ArrayDeque::new,
(Deque<Integer> deque, Integer next) -> {
if (next == 0) deque.addFirst(next);
else deque.addLast(next);
},
(left, right) -> { throw new AssertionError("should not be processed in parallel"); },
deque -> deque.stream().mapToInt(Integer::intValue).toArray()
));
}
要在CodingBat上运行此代码,需要使用完全限定名java。util。流动收集器。
您的错误源于以下行:zeroFront[zeroccounter i]=nums[i]
。相反,您需要对每个新的赋值递增zeroCounter,正如埃尔文·西拉吉(ErvinSzilagyi)的回答所示。
此外,下面的
for
循环是多余的,因为当数组int[]
被分配内存时,它的所有元素都将使用0
的默认值初始化:
for (int i = 0; i < zeroCounter; i++) {
zeroFront[i] = 0;
}
给定CodingBat中的任务sameEnds: 如果数组开头和结尾的数字组相同,则返回true。例如,对于,n=0和n=2的endpoint相同,n=1和n=3的endpoint相同。您可以假设n在0范围内。。nums。长度(含)。 我对这个问题的解决方案通过了绝大多数测试,但不是所有测试: 我的问题如下: 如何修复我的解决方案 是否可以使用流API解决此任务
给定来自CodingBat的任务sumNumbers sumNumbers: 给定一个字符串,返回字符串中出现的数字之和,忽略所有其他字符。数字是一行中一个或多个数字字符的序列。(注意:Character.isDigit(char)测试字符是否为字符“0”、“1”、…、'9'. 整数parseInt(string)将字符串转换为int.) 我对这个问题的解决方案如下: 是否可以使用流API解决此问
给定CodingBat中的任务notAlone: 如果数组中的元素前后都有值,并且这些值与它不同,那么我们会说它是“单独的”。返回给定数组的一个版本,其中给定值的每个单独实例都被其左侧或右侧较大的值替换。 我对这个问题的解决方案通过了绝大多数测试,但不是所有测试: 我的问题如下: 如何解决我的问题? 是否可以使用Stream API解决此任务? 测试结果
给定CodingBat的任务镜像: 给定一个字符串,请在给定字符串的开头和结尾处查找镜像(向后)字符串。 换句话说,在给定字符串的最开始,以及在字符串的最末尾以相反的顺序(可能重叠)出现零个或多个字符。例如,字符串具有镜像结尾。 示例: 我对此任务的解决方案如下: 是否可以使用Stream API解决此问题?
我正在尝试解决hackerrank中的一个“几乎已排序”的挑战。问题是: 给定一个包含元素的数组,可以只使用以下操作之一按升序对该数组进行排序吗? 交换两个元素。反转一个子段。 输入格式 第一行包含一个整数,指示数组的大小。 下一行包含以空格分隔的整数。 样本输入#1 2 4 2 示例输出 #1 是< br >交换1 2 示例输入 #2 3 3 1 2 样品输出#2 不 示例输入 #3 6 1 5