【问题标题】:C++ Remove duplication in a set of listC ++删除一组列表中的重复项
【发布时间】:2014-12-22 04:12:44
【问题描述】:

我正在尝试删除 this question 中返回列表中的重复项

给定一组候选数 (C) 和一个目标数 (T),找出 C 中候选数总和为 T 的所有唯一组合。

C 中的每个数字在组合中只能使用一次。

注意:

  1. 所有数字(包括目标)都是正整数。

  2. 组合中的元素 (a1, a2, ... , ak) 必须按非降序排列。 (即 a1 ≤ a2 ≤ … ≤ ak)。

  3. 解决方案集不得包含重复的组合。

例如,给定候选集 10,1,2,7,6,1,5 和目标 8, 一个解决方案集是:

[1, 7] 
[1, 2, 5] 
[2, 6] 
[1, 1, 6] 

我的问题是如何有效地删除重复? 以下是我的代码:

public class Solution {
    public static void main(String[] args) {
        int[] input = { 10, 1, 2, 7, 6, 1, 5 };
        // int[] input = { 2, 1, 1, 4, 4, 2 };
        System.out.println(combinationSum2(input, 8));
    }

    private static class Entry {
        List<Integer> list;
        int target;
        int index; // the previous index

        public Entry(int target) {
            list = new LinkedList<Integer>();
            this.target = target;
        }

        public int add(int num, int index) {
            this.list.add(num);
            this.index = index;
            this.target -= num;
            return target;
        }

        public Entry copy() {
            Entry copy = new Entry(this.target);
            copy.list = new ArrayList<>();
            copy.list.addAll(list);
            copy.target = target;
            copy.index = index;
            return copy;
        }

    }

    public static List<List<Integer>> combinationSum2(int[] input, int target) {
        List<List<Integer>> ret = new LinkedList<List<Integer>>();

        if (null == input || input.length <= 0)
            return ret;

        Arrays.sort(input);

        int N = input.length;
        Queue<Entry> pool = new LinkedList<Entry>();
        for (int i = 0; i < N; i++) {
            if (input[i] <= target) {
                Entry entry = new Entry(target);
                entry.add(input[i], i);
                pool.add(entry);
            }
        }

        while (!pool.isEmpty()) {
            Entry cur = pool.poll();
            if (cur.target == 0) {
                ret.add(cur.list);
            } else if (cur.target > 0) {
                for (int i = cur.index + 1; i < N; i++) {
                    if (cur.target - input[i] >= 0) {
                        Entry copy = cur.copy();
                        copy.add(input[i], i);
                        pool.offer(copy);
                    } else {
                        break;
                    }
                }
            }
        }

        return ret;
    }
}

我的第一个想法是对返回列表中的列表进行排序,它们逐个比较以消除重复。但是有没有更快的方法?或者有什么建议?

【问题讨论】:

    标签: c++ algorithm list combinations deduplication


    【解决方案1】:

    我的建议是使用 HashSet 来防止添加任何现有条目。 首先要做的是覆盖 Entry 类的 equals 和 hashCode 函数。 (more material)

    private static class Entry {
        List<Integer> list;
        int target;
        int index;
        int hash; // <---- add this
    
        public Entry(int target) {
            list = new LinkedList<Integer>();
            this.target = target;
            hash = target;
        }
    
        public int add(int num, int index) {
            this.list.add(num);
            this.index = index;
            this.target -= num;
            hash = hash * 17 + num;
            return target;
        }
    
        public Entry copy() {
            Entry copy = new Entry(this.target);
            copy.list = new ArrayList<>();
            copy.list.addAll(list);
            copy.target = target;
            copy.index = index;
            copy.hash = hash;
            return copy;
        }
    
        @Override
        public boolean equals(Object obj) {
            Entry e = (Entry) obj;
            if ((this.target != e.target) || (this.list.size() != e.list.size())) {
                return false;
            }
            for (int i = 0; i < this.list.size(); i++) {
                if (!this.list.get(i).equals(e.list.get(i)))
                    return false;
            }
            return true;
        }
    
        @Override
        public int hashCode() {
            return hash;
        }
    }
    

    下一步是使用哈希集来过滤结果。

    Set<Entry> nodup = new HashSet<Entry>();
    
    while (!pool.isEmpty()) {
        Entry cur = pool.poll();
        if (cur.target == 0) {
            nodup.add(cur);
        } else if (cur.target > 0) {
            // ... your code
        }
    }
    
    for (Entry entry : nodup) {
        ret.add(entry.list);
    }
    

    【讨论】:

      【解决方案2】:

      您可以通过将 List 转换为 Java 中的 HashSet 来从 Java 中的 List 中删除重复或重复的元素。但在此之前请记住,Set 不会保留由 List 保证的插入顺序,实际上这是 Java 中 List 和 Set 之间的主要区别。

      因此,当您将 List 转换为 HashSet 时,所有重复元素都将被删除,但插入顺序将丢失。

      更详细的解释可以看here

      【讨论】:

        【解决方案3】:

        您可以使用散列作为另一种解决方案,尽管它会在空间方面使用O(n)(时间相同)。

        本质上,从头到尾遍历列表。对于每个新遇到的元素,我们检查它是否在哈希集中(HashSet):如果是,我们将其删除;否则我们把它放进去。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-09-12
          • 2017-10-12
          • 2021-04-24
          • 2023-01-17
          • 2017-12-15
          • 2017-11-24
          • 1970-01-01
          相关资源
          最近更新 更多