【问题标题】:Removing Sublist from ArrayList从 ArrayList 中删除子列表
【发布时间】:2016-09-26 04:14:46
【问题描述】:

为简单起见,假设我有一个ArrayList,其索引只包含一个个位数的整数。例如:

6 4 5 6 0 6 3 4 1 6 1 6 0 6 8 3

我想过滤掉所有出现的子列表6 0 6,这样新的列表就变成了:

6 4 5 3 4 1 6 1 8 3

有没有办法做到这一点?使用ListIterator 似乎对我不起作用,因为我必须共同考虑三个连续的元素,老实说我不知道​​该怎么做。

这是我实现的方法的框架:

public static void filterList(ArrayList<Integer> list) {
    ListIterator<Integer> iterator = list.listIterator();
    int elem; 
    while (iterator.hasNext()) {
        // Remove any sublist of 6 0 6
    }
}

编辑:同样,为简单起见,我们假设不会有 60606 或类似的情况。

【问题讨论】:

  • 为什么不能使用list.removeAll(sublist)
  • 输入是60606会是什么情况?
  • @Naruto 这个例子没关系。我只是想说明这个想法。
  • @Sam 这将删除所有出现的60。它不会强制 3 个数字的顺序。
  • @4castle ya 你是对的..但是如果子列表包含元素(6 0 6),那么它会很有用。

标签: java arraylist


【解决方案1】:

您可以使用Collections.indexOfSubList 创建一个高效简洁的 O(nm) 解决方案:

public static void removeAllSubList(List<?> list, List<?> subList) {
    // find first occurrence of the subList in the list, O(nm)
    int i = Collections.indexOfSubList(list, subList);
    // if found
    if (i != -1) {
        // bulk remove, O(m)
        list.subList(i, i + subList.size()).clear();
        // recurse with the rest of the list
        removeAllSubList(list.subList(i, list.size()), subList);
    }
}

Ideone Demo

【讨论】:

  • 这听起来像是一个很好的解决方案。您介意显示代码供我和其他人参考吗?如果可行,我会将答案标记为已接受。
  • "这将是一个 O(n) 解决方案" 真的吗?我迫不及待地想发表你在计算机科学领域的突破性进展,many others failed to do it in O(N)
  • @AdrianColomitchi 如果它一直在搜索606,则为 O(n),如果它在搜索任何可变长度的子列表,则为 O(nm),或者如果使用您链接到的算法之一。
  • @FieryPhoenix 我已经更新了代码以供参考。如果你只需要删除6, 0, 6,你应该做一些优化。
  • 在查看了@AdrianColomichi 的解决方案的效率之后,我的答案完全不同。
【解决方案2】:

[已编辑 - 更好的单通道方法]

自定义、增强的indexOfSublistoffset 开始搜索;因此,每次删除某些内容时,我们都不会从 0 重新启动(就像我们在使用 Collections.indexOfSublist 时所做的那样,请参阅此答案的底部)。

static <T> int indexOfSublist(List<T> haystack, List<T> needle, int offset){
  int toRet=-1;
  int needleLen=needle.size();
  if(needleLen>0) {
    // it makes sense to search
    int haystackLen=haystack.size();
    for(;offset+needleLen<haystackLen && toRet<0; offset++) {
      int compIx;
      for(
          compIx=0; 
          (
                 compIx<needleLen 
              && false==haystack.get(offset+compIx).equals(needle.get(compIx))
          ); 
          compIx++
      );
      if(compIx==needleLen) { // found
        toRet=offset;
      }
    }
  }
  return toRet;
}

public static void filterList(List<Integer> haystack, List<Integer> needle) {
  for(
      int offset=0, ixOfNeedle=indexOfSublist(haystack, needle, offset);
      ixOfNeedle>=0;
      ixOfNeedle=indexOfSublist(haystack, needle, offset)
  ) {
    // found one place. We'll continue searching from here next time
    offset=ixOfNeedle;
    //////////////////////////////////////////
    // for a better removal sequence, see the 
    // 4castle's answer using sublists 
    for(int i=needle.size(); i>0; i--) {
      haystack.remove(ixOfNeedle);
    }
  }
}

Collections.indexOfSublist 是你所追求的。

public static void filterList(ArrayList<Integer> haystack, List<Integer> needle) {
    for(
       int ixOfNeedle=Collections.indexOfSublist(haystack, needle);
       ixOfNeedle>=0;
       ixOfNeedle=Collections.indexOfSublist(haystack, needle)
    ) {
      for(int i=needle.size(); i>0; i--) {
        haystack.remove(ixOfNeedle);
      }
    }
}

【讨论】:

  • 虽然易于阅读,但在较大的列表 O(mn^2) 中效率将非常低。您应该使用List#subList 对其进行优化。
  • @4castle - 这个怎么样?平均情况为 O(n*m/2) - 假设干草堆中的随机 len 与 needle 的子串匹配。
  • 当我说我们没有可以从某个索引开始的方法时,我说得太早了。使用List#subList 是我打算改进它的方式,因为您可以通过缩小您提供的列表来指定新的起始索引。
  • 当您已经可以使用第一个参数的subList指定起始索引时,无需编写indexOfSubList的自定义实现。
【解决方案3】:

我的建议是先搜索您的ArrayList,然后再将其变为ListIterator

public static void filterList(ArrayList<Integer> list) {
    bool firstInstance = false; //Saying we having found our first instance of our sub list
    for(int i=0;i<list.size();++i) {
       if(list.get(i) == 6) //Checks to see if our first index is a 6 or it pointless to check the next two numbers i.e. wasting resources
         if(list.get(i+1) == 0 && list.get(i+2) == 6 && !firstInstance) { //Make sure it a 6 0 6 list
           list.remove(i); //Removes first one
           list.remove(i); //Removes second one which now became our current index number
           list.remove(i); //Removes third one which now became our current index number
         } else
             firstInstance = true; //Our first instances has been found and will now remove duplicate ones!
    }
    ListIterator<Integer> iterator = list.listIterator();
    int elem; 
    while (iterator.hasNext()) {
        // Remove any sublist of 6 0 6-- Already Done
    }
}

【讨论】:

  • list.remove(i); list.remove(i+1); list.remove(i+2); -> list.remove(i); list.remove(i); list.remove(i);
  • 谢谢,为时已晚。我会马上做出调整。
【解决方案4】:
    //if
    List<String> originalList = new ArrayList<>();
    originalList.add("A");
    originalList.add("B");
    originalList.add("C");
    originalList.add("D");
    //and
    List<String> subList = new ArrayList<>();
    subList.add("A");
    subList.add("C");
    //then
    originalList = originalList.stream().filter(x -> !subList.contains(x)).collect(Collectors.toList());
    
    //originalList should now contain {"B","D"}

【讨论】:

    【解决方案5】:

    您可以使用数组和列表的组合作为找到以下解决方案,希望对您有所帮助。

        public void testData()
        {
            int tempArray[] = {6, 4, 5, 6, 0, 6, 3, 4, 1, 6, 1, 6, 0, 6, 8, 3};
            List<Integer> resultArray = new ArrayList<>();
    
            for(int i = 0; i < tempArray.length; i++)
            {
                if(tempArray[i] == 6 && tempArray[i+1] == 0 && tempArray[i + 2] == 6) 
                {
                    i += 2;
                }
                else
                {
                    resultArray.add(tempArray[i]);
                }
            }
    
            for(int tempInt : resultArray)
            {
                System.out.print("\t" + tempInt);
            }
        }
    

    注意:您可以在上面的函数中根据您的要求传递数组或返回结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-04-22
      • 1970-01-01
      • 1970-01-01
      • 2019-05-04
      • 2022-12-20
      • 2020-10-06
      相关资源
      最近更新 更多