减少这两种方法的时间复杂性?

时间:2014-02-23 17:41:46

标签: java algorithm

/*
 * Returns true if this and other are rankings of the same
 * set of strings; otherwise, returns false. Throws a
 * NullPointerException if other is null. Must run in O(n)
 * time, where n is the number of elements in this (or other).
 */
public boolean sameNames(Ranking other)
{
    ArrayList<String> str1 = new ArrayList<String>();
    ArrayList<String> str2 = new ArrayList<String>();

    for(int i = 0; i < this.getNumItems(); i++){
        str1.add(this.getStringOfRank(i));
    }
    for(int i = 0; i < other.getNumItems(); i++){
        str2.add(other.getStringOfRank(i));
    }
    Collections.sort(str1);
    Collections.sort(str2);
    if(str1.size() == str2.size())
        return str1.containsAll(str2);
    else
        return false;
}

好的,在上面的代码中,使用str1.containsAll(str2)会破坏我的O(n)时间复杂度,因为我认为在这种情况下它是O(n ^ 2)。我的问题是如何在不使用O(n ^ 2)的情况下比较两个数组/数组列表的内容。我能想到的只是嵌套for循环,当然是O(n ^ 2)。

/*
 * Returns the rank of name. Throws an IllegalArgumentException
 * if name is not present in the ranking. Must run in O(log n)
 * time, where n = this.getNumItems().
 */
public int getRankOfString(String name)
{       
    Cities[] nameTest = new Cities[city.length];
    int min = 0;
    int max = city.length;
    System.arraycopy(city, 0, nameTest, 0, city.length);
    Arrays.sort(nameTest, Cities.BY_NAME);
    while(max >= min){
        int mid = (min + max)/2;
        if(nameTest[mid].getName().equals(name))
            return nameTest[mid].getRank();
        else if(nameTest[mid].getName().compareTo(name) < 0)
            min = mid + 1;
        else
            max = mid-1;
    }
    throw new IllegalArgumentException();
}

这一次,这必须是O(log n)。所以我使用二进制搜索,但它只适用于排序数组,所以我必须调用Arrays.sort(),但我不能搞乱实际数组的顺序所以我必须使用System.arraycopy复制数组()。这很可能是O(n +(n log n)+ log n),它不是log n。我不知道我可以搜索什么其他方式,看起来log n是最好的,但那是二进制搜索并且会强迫我先排序数组,这只会增加时间......

P.S。我不被允许使用地图或集...... :(

任何帮助都会很棒。

很抱歉,排名对象包含可以调用的城市名称数组以及可以调用的每个城市的排名数组(只是整数)。 sameNames()只是测试两个排名对象拥有相同的城市,getRankofString()输入了一个集合名称,然后检查该名称是否在排名对象中,如果是,则返回其对应的排名。希望清理它

是的,不能使用哈希任何东西。我们基本上只限于搞乱数组和数组列表和东西。

3 个答案:

答案 0 :(得分:0)

让我们计算每个字符串的出现次数。它有点类似于counting sort

  1. 使用散列函数t创建hash table f(),其中键是字符串,值是整数(最初为0)。
  2. 遍历第一个字符串,每个字符串都t[f(string)]++
  3. 遍历第二个字符串,为每个字符串执行t[f(string)]++
  4. 迭代t中的非零值,如果一切都是偶数 - 则返回true。否则 - 假。
  5. 线性时间复杂度。

答案 1 :(得分:0)

第一种方法的复杂性至少为O(n^2),由2*O(n*f(n)) + 2*O(n log n) + O(n^2)给出。 O(n log n)来自Collections.sort(),这些调用也会“摧毁你O(n)'的复杂性。

由于两个数组列表在您尝试containsAll时已经排序并且长度相等,因此该调用等效于某种等式(一个列表中的第一个元素应该等于第二个元素中的第一个元素,等等)。您可以轻松地手动比较两个列表(无法想到执行此操作的任何内置函数)。

因此,如果您可以将getStringOfRank()的复杂性保持在O(log n)下,那么第一段代码的整体复杂性可以降低到O(n log n)(但 你的帖子中没有显示 功能。


第二个函数(与第一段代码无关)具有计算所指出的复杂度O(n log n)。如果你已经复制,那么对城市数组进行排序,二进制搜索是没有意义的。不要复制,不要排序,只是比较数组中的每个城市,将此函数的整个复杂性放到O(n)。或者,只需保留city数组的已排序副本并对其使用二进制搜索。

无论哪种方式,创建数组副本,为每个函数调用排序该副本高度无效 - 如果要在循环内调用此函数,就像使用getStringOfRank()一样在上面,在循环之前构造排序副本并将其用作参数:

private boolean getRankOfString(String name, Cities[] sortedCities) {
    // only binary search code needed here
}

偏离主题

基于第二个函数,您可以在代码的某个地方声明Cities[] city。如果它遵循惯例,它应该更像City[] cities(类名称奇异,数组名称应该是使用复数的名称)

答案 2 :(得分:0)

所以第一个只需比较两者是否具有完全相同的名称而不是其他内容?

这个怎么样

public static boolean compare(List<String> l1, List<String> l2) {
  if (l1.size() != l2.size()) return false;
  long hash1 = 0;
  long hash2 = 0;
  for (int i = 0 ; i < l1.size() ; i++) {
    hash1 += l1.get(i).hashCode();
    hash2 += l2.get(i).hashCode();
  }
  return hash1 == hash2;
}

理论上,我认为你可能会遇到哈希冲突。