Java 为什么这个O(n^2)代码的执行速度比O(n)快?

Java 为什么这个O(n^2)代码的执行速度比O(n)快?,java,time-complexity,big-o,Java,Time Complexity,Big O,我已经为两种方法编写了代码,以找出LeetCode上字符串中的第一个唯一字符 问题陈述: 给定一个字符串,查找第一个非重复字符串 字符并返回其索引。如果它不存在,则返回-1 样本测试用例: class Solution { public int firstUniqChar(String s) { HashMap<Character,Integer> charHash = new HashMap<>(); int res = -1

我已经为两种方法编写了代码,以找出LeetCode上字符串中的第一个唯一字符

问题陈述: 给定一个字符串,查找第一个非重复字符串 字符并返回其索引。如果它不存在,则返回-1

样本测试用例:

class Solution {
    public int firstUniqChar(String s) {

        HashMap<Character,Integer> charHash = new HashMap<>();

        int res = -1;

        for (int i = 0; i < s.length(); i++) {

            Integer count = charHash.get(s.charAt(i));

            if (count == null){
                charHash.put(s.charAt(i),1);
            }
            else {
                charHash.put(s.charAt(i),count + 1);
            }
        }

        for (int i = 0; i < s.length(); i++) {

            if (charHash.get(s.charAt(i)) == 1) {
                res = i;
                break;
            }
        }

        return res;
    }
}
class Solution {
    public int firstUniqChar(String s) {

        char[] a = s.toCharArray();
        int res = -1;

        for(int i=0; i<a.length;i++){
            if(s.indexOf(a[i])==s.lastIndexOf(a[i])) {
                res = i;
                break;
            }
        }
        return res;
    }
}
s=“leetcode”返回0

s=“loveleetcode”,返回2

方法1(O(n))(如果我错了,请纠正我):

class Solution {
    public int firstUniqChar(String s) {

        HashMap<Character,Integer> charHash = new HashMap<>();

        int res = -1;

        for (int i = 0; i < s.length(); i++) {

            Integer count = charHash.get(s.charAt(i));

            if (count == null){
                charHash.put(s.charAt(i),1);
            }
            else {
                charHash.put(s.charAt(i),count + 1);
            }
        }

        for (int i = 0; i < s.length(); i++) {

            if (charHash.get(s.charAt(i)) == 1) {
                res = i;
                break;
            }
        }

        return res;
    }
}
class Solution {
    public int firstUniqChar(String s) {

        char[] a = s.toCharArray();
        int res = -1;

        for(int i=0; i<a.length;i++){
            if(s.indexOf(a[i])==s.lastIndexOf(a[i])) {
                res = i;
                break;
            }
        }
        return res;
    }
}
类解决方案{
公共int firstUniqChar(字符串s){
HashMap charHash=新的HashMap();
int res=-1;
对于(int i=0;i
方法2(O(n^2)):

class Solution {
    public int firstUniqChar(String s) {

        HashMap<Character,Integer> charHash = new HashMap<>();

        int res = -1;

        for (int i = 0; i < s.length(); i++) {

            Integer count = charHash.get(s.charAt(i));

            if (count == null){
                charHash.put(s.charAt(i),1);
            }
            else {
                charHash.put(s.charAt(i),count + 1);
            }
        }

        for (int i = 0; i < s.length(); i++) {

            if (charHash.get(s.charAt(i)) == 1) {
                res = i;
                break;
            }
        }

        return res;
    }
}
class Solution {
    public int firstUniqChar(String s) {

        char[] a = s.toCharArray();
        int res = -1;

        for(int i=0; i<a.length;i++){
            if(s.indexOf(a[i])==s.lastIndexOf(a[i])) {
                res = i;
                break;
            }
        }
        return res;
    }
}
类解决方案{
公共int firstUniqChar(字符串s){
char[]a=s.toCharArray();
int res=-1;

对于(int i=0;i

对于非常短的字符串,例如单个字符,创建
HashMap
、重新调整其大小、在将
char
装箱和拆箱到
character
时查找条目的成本可能会超过
String.indexOf()
的成本,这可能被JVM视为热门和内联

另一个原因可能是RAM访问的成本。如果查找中涉及额外的
哈希映射
字符
整数
对象,则可能需要额外访问RAM和RAM。单次访问约100ns,这可能会增加

看一看。本课程说明了性能与复杂性不同,内存访问可能是算法的杀手。

考虑:

  • f1(n)=n2
  • f2(n)=n+1000
很明显,f1是O(n2),f2是O(n)。对于一个小的输入(比如,n=5),我们有f1(n)=25,但f2(n)>1000

仅仅因为一个函数(或时间复杂度)是O(n),另一个是O(n2)这并不意味着前者对于n的所有值都较小,只是有一些n超出了它就会出现这种情况。

是一种理论上衡量算法在内存消耗或计算时间方面的方式,使用
n
-元素或主操作的数量,并且始终是
n->Infin城市

实际上,在您的示例中,
N
相当小。虽然向哈希表添加元素通常被视为摊销O(1),但它也可能导致内存分配(同样,取决于哈希表的设计)。这可能不是O(1)-并且还可能导致进程为另一个页面对内核进行系统调用

采用
O(n^2)
解决方案-
a
中的字符串将很快在缓存中找到自己,并且可能会不间断地运行。单个内存分配的成本可能高于一对嵌套循环

在现代CPU体系结构的实践中,从缓存读取的速度比从主存读取的速度快几个数量级,
N
在使用理论上最优的算法之前将非常大,优于线性数据结构和线性搜索。二叉树对于缓存效率来说尤其是个坏消息


[编辑]它是Java:哈希表包含对装箱
Java.lang.Character
对象的引用。每一次添加都会导致内存分配Karol已经为您的特殊情况提供了很好的解释。我想就时间复杂度的大O表示法添加一条一般性注释

一般来说,这个时间复杂度并不能告诉你太多关于实际性能的信息,它只是告诉你一个特定算法需要的迭代次数

让我这样说:如果执行大量快速迭代,这仍然比执行极少数极慢的迭代要快。

O(n2)只是第二种方法的时间复杂度最差的情况

对于
bbbbbb…bbbbbbbbb aaaaaaaaaaaaaaaaaaa…aaaaaaaaaaaaa等字符串,如果存在
x
b和
x
a,则每个循环迭代大约需要
x
步骤来确定索引,因此执行的总步骤约为
2x2
。对于
x
约30000,大约需要1~2秒,而另一种解决方案的性能要好得多

在“在线试用”中,计算出对于上面的字符串,方法2比方法1慢50倍。对于更大的
x
,差异甚至更大(方法1大约需要0.01秒,方法2需要几秒)

但是:

class Solution {
    public int firstUniqChar(String s) {

        HashMap<Character,Integer> charHash = new HashMap<>();

        int res = -1;

        for (int i = 0; i < s.length(); i++) {

            Integer count = charHash.get(s.charAt(i));

            if (count == null){
                charHash.put(s.charAt(i),1);
            }
            else {
                charHash.put(s.charAt(i),count + 1);
            }
        }

        for (int i = 0; i < s.length(); i++) {

            if (charHash.get(s.charAt(i)) == 1) {
                res = i;
                break;
            }
        }

        return res;
    }
}
class Solution {
    public int firstUniqChar(String s) {

        char[] a = s.toCharArray();
        int res = -1;

        for(int i=0; i<a.length;i++){
            if(s.indexOf(a[i])==s.lastIndexOf(a[i])) {
                res = i;
                break;
            }
        }
        return res;
    }
}
对于从
{a,b,c,…,z}
[1]中独立、统一选择每个字符的字符串,预期时间复杂度应为O(n)

假设Java使用NaiveString搜索算法,该算法一个接一个地搜索字符,直到找到匹配项,然后立即返回。搜索的时间复杂度是考虑的字符数

可以很容易地证明(证明类似于)字母表
{a,b,c,…,z}
上统一独立字符串中特定字符的预期位置是O(1)。因此,每个
indexOf
lastIndexOf
调用在预期的O(1)时间内运行,整个算法需要预期的O(n)时间

[1] 例如在美国,据说

您可以假定字符串只包含小写字母

但是,问题中没有提到这一点。

我已经移植了t