Java:[性能]存储并搜索<integer,integer =“”>以查找最常出现的</integer,>

时间:2012-03-16 22:21:32

标签: java performance sorting optimization integer

我有这个问题并解决它不是问题,更像是最快的方式。 所以我要求你们更有经验帮助我找到一个快速的解决方案。

我有人,每个人定义为1000到3000之间的整数。 这些人中的每一个都可以分配给其他人,这看起来像: &lt; p1的整数,p2的整数&gt; 这些连接有一些规则,不会超过10000,但至少有一个 并且每对人只能出现一次,因此&lt; 1000,2000&gt;和&lt; 2000,1000&gt;不允许! 在Moment中,我将所有这些连接存储在LinkedList中,其中Connection是一个包含两个人的两个整数的类。

然后我需要找到所有连接中出现次数最多的人,如果有多个连接,我需要将所有连接都排除在外。

之后,我将遍历LinkedList并删除这些人参与的所有连接,并重做该过程,直到列表为空。

我遇到的一些问题是Concurred Access或使用错误的地图/列表以及缓慢的排序方法。

我目前没有代码,因为我看到了旧代码的性能,并且从头开始,现在除了处理输入之外什么都没有(已经优化了);)

对我最有帮助的是有人在查看我的案例并告诉我他对不同数据类型的不同解决方案有多快的经验。我想自己编写代码,我只需要一些提示如何正确执行。

感谢您的关注并希望得到答案。 如果某些事情不清楚,我会为此道歉并在询问后澄清它:)

5 个答案:

答案 0 :(得分:3)

如果我们以面向对象的方式看待这个,我们可以让每个Person存储他们的朋友列表:

class Person {
    private Set<Person> friends = new HashSet<>();

    public void addFriend(Person newFriend) {
        friends.add(newFriend);
        newFriend.friends.add(this);
    }

    public void removeFriend(Person oldFriend) {
        friends.remove(oldFriend);
        oldFriend.friends.remove(this);
    }

    public int numberOfFriends() {
        return friends.size();
    }

    public void disappear() {
        for (Person friend : friends) {
            friend.friends.remove(this);
        }
    }
}

这种方法的优点是所有操作都在不变的预期时间内完成。

这比保留友情链接列表要好得多,在这里查找单个人的朋友数量需要我们查看所有10000个友情的列表。

它也明显快于rogelware所描述的二维数组,其中找到朋友的数量需要检查所有2000个其他人的友谊,并且移除一个人需要清除所有其他2000人的友谊。

答案 1 :(得分:2)

你拥有的是无向图。存在一组连接之间的连接,并且每个连接都是双向的。

可以找到here的图表有四种常见表示形式。

您需要确定哪种表示最适合您的需求,以及是否可以对其进行调整以提高性能。

我的建议是使用邻接列表,但让每个节点存储一个链接到它的节点的列表,以及链接到它的所有节点的另一个列表。

例如

class Node {

    Integer personID;
    List<Integer> links;

}

// graph data type
Map<Integer, Node> graph;

现在,由于数据的存储方式,找出一个人的总连接数变得如此简单:

Integer personID = ...;
Node n = graph.get(personID);
int totalConnections = n.links.size();

然后您需要创建一个对象列表,用于存储人员ID和他们总共有多少链接,然后按总链接排序(这将对列表末尾的所有高总链接计数进行分组) )。

当然,您必须确保图形数据在初始化阶段正确构建。

要记住的一点是,这种表示会稍微增加图形的内存复杂度,但会显着降低算法的时间复杂度。你在计划,时间或记忆中更重视什么?

但是,根据图表中连接的密集程度,邻接矩阵可能更适合您的需求。

其他问题:

与ArrayList相比,java中的LinkedList对于大多数任务来说具有非常糟糕的性能。与ArrayList相比,它所做的一件事就是当你通过ListIterator在列表中间进行大量插入/删除时。如果你不使用ListIterator,那么性能再次糟糕。由于LinkedLists的实现,java Collections API中的默认排序算法在排序LinkedLists时性能非常差;

使用foreach循环并在循环期间修改集合时,会发生与集合API的并发访问异常。您需要使用Iterator或ListIterator循环遍历集合,并通过Iterator / ListIterator添加/删除元素。

答案 2 :(得分:0)

如果空间不是问题,我会使用矩阵来存储连接。

第一个维度是p1,第二个维度是p2。我会有一个

boolean[][] connection = new boolean [2001][2001];

(我会考虑从0到2000)。

当455和985之间存在连接时,我必须检查两个方向。例如:

connection[455][985] = true;
connection[985][455] = true;

如果我想测试两个人之间是否存在连接,我会做

 if(connection[455][985]) //the other end will have the same values

这会浪费太多空间,但它会非常快速且易于使用。

答案 3 :(得分:0)

我在评论中粗略概述了我的意思:

class Person {
    long id;

    Person(long id) {
        this.id = id;
    }

    @Override
    public boolean equals(Object o) {
        // Compare by id
    }

    @Override
    public int hashCode() {
        // Hash by id
    }
}

连接

class Connection {
    Person person1;
    Person person2;

    Connection(Person person1, Person person2) {
        if (person1.equals(person2)) throw new IllegalArgumentException("Cannot connect a person to itself");

        if (person1.id < person2.id) {
            this.person1 = person1;
            this.person2 = person2;
        } else {
            // The person1 field should contain the person with the smaller id
            this.person1 = person2;
            this.person2 = person1;
        }
    }

    @Override
    public boolean equals(Object o) {
        // Compare person1 and person2
    }

    @Override
    public int hashCode() {
        // Hash person1 and person2
    }
}

的ConnectionManager

class ConnectionManager {
    Set<Connection> connections = new HashSet<Connection>();
    Map<Person, Set<Person>> adjacency = new HashMap<Person, Set<Person>>();

    public void connect(Person p1, Person p2) {
        Connection connection = new Connection(p1, p2);
        if (connections.add(connection)) {
            getAdjacency(p1).add(p2);
            getAdjacency(p2).add(p1);
        } else {
            throw new RuntimeException(String.format("Persons %d and %d are already connected", p1.id, p2.id));
        }
    }

    private Set<Person> getAdjacency(Person person) {
        Set<Person> result = adjacency.get(person);
        if (result == null) {
            adjacency.put(person, result = new HashSet<Person>());
        }
        return result;
    }

    public void disconnect(Person p1, Person p2) {
        if (connections.remove(new Connection(p1, p2))) {
            getAdjacency(p1).remove(p2);
            getAdjacency(p2).remove(p1);
        } else {
            throw new RuntimeException(String.format("No connection between persons %d and %d exists", p1.id, p2.id));
        }
    }

    public Collection<Map.Entry<Person, Set<Person>>> getMostConnected() {
        int maxConnections = 0;
        List<Map.Entry<Person, Set<Person>>> result = new ArrayList<Map.Entry<Person, Set<Person>>>();
        // return all the entries with the maximum size;

        for (Map.Entry<Person, Set<Person>> entry : adjacency.entrySet()) {
            int connections = entry.getValue().size();

            if (connections > maxConnections) {
                result.clear();
                maxConnections=connections;
            }

            if (connections == maxConnections) {
                result.add(entry);
            } 
        }

        return result;
    }


    public Set<Person> getConnections(Person person) {
        return new HashSet(getAdjacency(person));
    }
}
为简洁起见,省略了getter / setter和equals() / hashCode()实现 - 无论IDE为后者生成什么都没关系。

此代码本质上是一个矩阵,用邻接列表表示。它不是O(1)的唯一部分是搜索连接最多的人的部分,即O(n)。

您可以使用PriorityQueue保存Set<Person>地图中存储的adjacency对象,并将设置的尺寸作为“优先级”来降低性能损失。无论何时触及这样的集合,都要将其从队列中删除,更改它,然后再次插入。 (但我的预感是,这只能通过连接和断开人们的速度来加快连接人员的速度。)

免责声明:上述代码完全未经测试,只是为了让您了解可以尝试的内容。

答案 4 :(得分:0)

不要使用LinkedList,使用2个元素的整数数组,或两个字段的特殊类。

class Relation {

    private int id1, id2;

    public Relation(int id1, int id2) {   
         if( id1 > id2 ) {   
             this.id2 = id1;
             this.id1 = id2;
         }
         else {
             this.id1 = id1;
             this.id2 = id2;
         }
    }


    public int hashCode() { 
        return id1 ^ id2;
    }

    public boolean equals(object o) {
        return 
             ((Relation)o).p1 == p1 &&
             ((Relation)o).p2 == p2;
    }

}

如果您需要检查唯一性,最后两种方法适用于HashSet

然后将所有关系放入HashSet<Relation>,并将它们备份为数组或Vector<Relation>

之类的线性结构