【问题标题】:Removing from an Array without holes从无孔阵列中移除
【发布时间】:2014-05-05 13:49:43
【问题描述】:

我有一个Cars 中的Array,其中包含Car 对象,现在我在获取车号并需要删除所有具有该车号的对象的方法上停留了一段时间,并且然后返回没有任何漏洞的数组。这是我的作业,所以我不能使用任何arrayListArray. 方法。

我已尝试执行以下操作,但由于某种原因,当我尝试运行它时,它不符合要求并给我一个 IndexOutOfBounds 错误并让我参考我的 toString 方法(它会打印所有数组中的单元格)。

noOfCars,节省了现在实际存在的汽车数量,因为我们使用容器构造函数构建了数组,以便为​​未来的汽车保存更多位置以防万一。

public void removeCarNumbers(int CarNum) {
    int count = 0;
    for(int i = 0; i < this.noOfCars; i++) {
        if(this.cars[i].getCarNum() == CarNum) {
            this.cars[i] = this.cars[noOfCars- 1 - count];
            count++;
        }
    }
    CarsLines [] newArr = new CarLines[this.noOfCars- count];

    for(int i = 0; i< this.noOfCars - count; i++)
    {
        newArr[i] = this.cars[i];
    }
    this.cars= newArr;
}

*edit:在玩了更多之后,我仍然收到一个错误,指出我的 toString() 方法。它也有问题吗? 公共字符串 toString() { 字符串输出 = ""; for (int i = 0; i

【问题讨论】:

  • 明确一点,如果您收到“IndexOutOfBounds”错误,它正在编译。
  • 你为什么不用List??
  • @user3604476 我对您添加的toString 方法进行了编辑

标签: java arrays


【解决方案1】:

试试:

// count
int count = 0;
for(int i = 0 ; i < this.cars.length ; ++i) {
    if(this.cars[i].getCarNum() == CarNum) {
        ++count;
    }
}
// create new array
CarsLines[] newArr = new CarLines[this.cars.length - count];
int index = 0;
for(int i = 0 ; i < this.cars.length ; ++i) {
    if(this.cars[i].getCarNum() != CarNum) {
        newArr[index++] = this.cars[i];
    }
}
// assign
this.cars = newArr;

【讨论】:

  • 是的。这几乎就是他正在做的事情。
  • 你得到我的投票,因为这个答案肯定是正确的。只需向 OP 说明,练习的目的可能是重新排列元素。
  • 也许吧。但由于数组的长度,它不能完全到位。重新排列后,原数组的末尾为空。
  • 这就是ArrayList 的工作方式——显然有一些时髦的逻辑来扩大/缩小数组。
  • 完全没问题。任何有助于正确回答问题的方法:)
【解决方案2】:

我不认为这两个循环正在做你认为的那样。在第一个循环中,您应该消除具有相同数字的Cars,也许将它们设置为null

for (int i = 0; i < this.cars.length; i++) {
    if (this.cars[i] != null && this.cars[i].getCarNum() == CarNum) {
        this.cars[i] == null;
    } else {
        count++;
    }
}

现在这将消除重复,并保留唯一Cars 的计数。在第二个循环中,只填写非null 值:

CarsLines [] newArr= new CarLines[count];
int index = 0;
for(int i = 0; i< this.cars,length; i++)
{
    if (this.cars[i] != null) { // Only pass in the non-null values
        newArr[index] = this.cars[i]; 
        index++;
    }
}
this.cars= newArr;

在您创建几乎相同的列表副本之前,因为您是在一对一的基础上填写的,即相同的索引。

另一种可能的方法是就地转换:

for (int i = 0; i < this.cars.length; i++) {
    if (this.cars[i] != null && this.cars[i].getCarNum() == CarNum) {
        for (int j = i+1; j < this.cars.length; j++) { // This loop will shift the 
            this.cars[j-1] = this.cars[j];             // whole list down one when
        }                                              // a duplicate is found,
                                                       // overwriting it.
        i--;              // sets the index back to recheck the shifted list.
        this.noOfCars--;  // You removed one duplicate by overwriting it
    }
}

响应 @Boris The Spiders 评论,您可以通过消除内部 for 循环来降低此算法的复杂性。我建议通过跟踪最后一个有效位置并将其用于更新来做到这一点。

int writeIndex = 0;
for (int readIndex = 0; readIndex < this.cars.length; readIndex++) {
    if (this.cars[readIndex] != null && this.cars[readIndex].getCarNum() == CarNum) {
        // pass on this because nextIndex should track only the valid cars.
    } else {
        this.cars[writeIndex++] = this.cars[readIndex];
    }
    if (writeIndex < readIndex) {
        this.cars[i] = null;
    }
}

这个想法是您保留一个用于读取的索引 (i) 和一个用于写入的索引writeIndex。如果您的readIndex 领先于您的writeIndex(这意味着您找到了重复),您应该在阅读后将其清除,如果您将转移到之前的writeIndex

编辑在回答您的问题时,您应该格外小心noOfCars 的计算方式,因为这可能是您的Exception 的来源,而是尝试更可靠的索引通过使用数组中的内在 length 属性的方法:

public String toString() {
    String output = "";
    for (int i = 0; i < this.cars.length; i++) {
        if (this.cars[i] != null) {
            output += "" + this.cars[i].toString() + "\n";
        } else {
            // condition that the i-th car is null
            output += "null\n"; // one possible approach
            // you could also do nothing and your toString would output just the non-null cars
         }
    }
    return output;
}

为了可靠性,以效率为代价,您可以实现count() 方法来仔细检查您的跟踪。也许作为一个临时检查,直到你确定它工作正常。

public int getNumCars() {
    int c = 0;
    for (int i = 0; i < this.cars.length; i++) {
        if (this.cars[i] != null) {
            c++;
        }
    }
    return c;
}

【讨论】:

  • 你最好循环两次,这个算法是O(n^2),而它应该是O(n)。循环一次设置null 然后循环第二次移位;当你找到第一个 null 时转移 1,如果你找到第二个 null 开始转移 2 等等。当然,你可以用一个循环来做到这一点 - 但这会变得非常复杂。
  • @BoristheSpider 抱歉,我做错了心算,你的建议是有效的,我在编辑中尝试了类似的方法。
【解决方案3】:

对于您移除的每辆汽车,请尝试将数组中的最后一个元素交换到“洞”中。为有效的最大元素保留一个计数器,并在每次将元素换出到保留中时将其递减。你基本上是在原地重新排列数组元素

int max=this.noOfCars.length; 
int i=0;
do{
        if(this.cars[i]==null)
            break; 
        if(this.cars[i].getCarNum() == CarNum) {
            this.cars[i] = this.cars[max-1];
            this.cars[max-1] = null;
        }
         else{
            i++;
         } 
   }while(1==1);

PS:您可能需要添加一些边界条件检查(如空数组)

【讨论】:

    【解决方案4】:

    这是一个简单的例子,说明如何使用单个循环而不是嵌套循环来执行此操作。

    这个想法是你跟踪你可能删除的元素(以及你找到了多少null元素),然后,如果不删除当前元素,只需将其移回那个数字。

    public static void main(final String[] args) throws IOException {
        final Integer[] data = {1, 2, 2, 3, 4, 5, 1, 2, 3, 7, 8, 2, null, null, null};
        int shift = 0;
        for (int i = 0; i < data.length; ++i) {
            if (data[i] == null || data[i] == 2) {
                shift++;
                data[i] = null;
            } else if (shift > 0) {
                data[i - shift] = data[i];
                data[i] = null;
            }
        }
        System.out.println(Arrays.toString(data));
    }
    

    输出:

    [1, 3, 4, 5, 1, 3, 7, 8, null, null, null, null, null, null, null]
    

    所以您可以看到2s 已被删除,其余部分已上移。

    【讨论】:

      猜你喜欢
      • 2016-10-13
      • 2021-07-14
      • 1970-01-01
      • 2013-07-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多