简谈算法复杂度

时间复杂度 和 空间复杂度就是算法存在的意义

1. 算法复杂度分为时间复杂度和空间复杂度
空间复杂度:算法执行需要花费的内存(暂不讨论)
时间复杂度:算法执行需要花费的时间
算法的时间复杂度,用来度量算法的运行时间,记作: T(n) = O(f(n)). 它表示随着输入大小n的增大,算法执行需要的时间都增长速度可以用f(n)来描述。

2. 定理:
2.1常数项对函数的增长速度影响并不大,所以当T(n) = c, c为一个常数的时候,我们说这个算法的时间复杂度为O(1)

void func1() {
    int i = 100;  //执行一次
    int j = 0;    //执行一次
    j = i;       //执行一次
    j ++;       //执行一次
}

该算法一共执行 T(n) = 1 + 1 + 1 + 1 = 4 = O(1);

2.2如果T(n)不等于一个常数项时,直接将常数项省略

void func2() {
    int j = 100;   //执行一次
    int i = 0;     //执行一次
    for (int i = 0; i <= 2*j; i++) {      //执行2j次
        int k = 0;                  //执行2j次
    }
}

该算法一共执行 T(n) = 1 + 2j + 2j = 4j + 1 = O(j);

2.3因为函数的介数对函数的增长速度的影响是最显著的,所以我们忽略与最高阶相乘的常数

void func1(int n) {
    for (int i = 0; i <= n; i++) {          //执行n次
        for (int j = 0; j <= n; j++) {      //执行n^2次
            for (int k = 0; k <= n; k++) {  //执行n^3次
                System.out.println(111);
            }
        }
    }
}

该算法一共执行 T(n) = n + n^2 + n^3 = O(n^3);

2.4算法考虑的是最坏情况下程序执行的次数,而非最好情况下程序执行的次数

public void func1() {
    char array1[] = {'a','b','c','d','e'};          //执行1次
    char array2[] = {'h','i','a','j','k'};           //执行1次
    for (int i = 0; i <= array1.length; i++) {    //执行array1.length次
        for (int j = 0; i <= array2.length; i++) {//执行array2.length次
            if (array1[i] == array2[j]) {
                return;
            }
        }
    }
}

该算法一共执行3次,3位常数 T(n) = O(1);

 void func1(char array1[], char array2[]) {
     for (int i = 0; i <= array1.length; i++) {
         for (int j = 0; i <= array2.length; i++) {
             if (array1[i] == array2[j]) {
                 return;
             }
            }
        }
 }

该算法一共执行 T(n) = O(array1.length * array2.length);

3. 例题:
例题1:

// 假设 n > i
public void func1(int n) {
    int i = 0;        //执行一次
    while(i < n) {    
        i += 2;
    }
}

假设当经过P次以后 i>=n成立,则当

p=1: f(1) = 0 + 2;
p=2: f(2) = f(1) + 2;
p=3: f(3) = f(2) + 2;
…
p=p: f(p) = f(p-1) + 2;

等式左右两边相加得:
f(1)+f(2)+f(3)+…+f(p) = (0+2)+(f(1)+2)+(f(2)+2)+…+(f(p-1)+2);
左右两边消去相同项得:
f(p)=2p.
现有2p=n p=n/2
则该方法的时间复杂度为:O(n/2),根据定理2.2得该方法的时间复杂读为O(n)

例题2:

// 假设 n > i
void func(int n) {
    int i = 1;       //执行一次
    while(i < n) {
        i *= 2;      
    }
}

假设当经过P次以后 i>=n成立,则当

p=1: f(1) = 1 * 2;
p=2: f(2) = f(1) * 2;
p=3: f(3) = f(2) * 2;
…
p=p: f(p) = f(p-1) *2;

等式左右两边相乘得:
f(1)*f(2)*f(3)*…*f(p) = (1*2)*(f(1)*2)*(f(2)*2)*…*(f(p-1)*2);
左右两边消去相同项得:
f(p)=2^p
现有2^p=n p=log2n
则该方法的时间复杂度为:O(log2n)

例题3:

void func(int n) {
  int i = 0;
  while(i * i * i <= n)
    i++;
}

假设当经过P次以后 iii>n成立,则当
p=p: f(p) = (f(p) + (p-1)) ^3;
则一定有:
f(p) = f(i+p)^3 + a(f(i+p-1)^2) + b(f(i+p-1)) + c > n
则f(i)^3 +x (f(i)^2) + y(f(i)) + z > n
根据定理2.3得:
f(i)^3 > n i = log3n
则该方法的时间复杂度为:O(log3n)

例题4:

// 假设 n > i
void func1(int n) {
    int i = 0;
    int s = 0;
    while(s < n) {
        ++ i;
        s += i;
    }
}

假设经p次循环后结束,s>=n
则:

f(1) = 1;
f(2) = f (1) + 2;
f(3) = f (2) + 3;
......
f(p) = f(p-1) + p;

等式左右两边相加得:
f(1)+f(2)+f(3)+…+f(p) = 1+(f(1)+2)+(f(2)+3)+…+(f(p-1)+p);
左右两边消去相同项得:

f(p)=1+2+3…+p;
f(p) = p(1+p)/2(等差数列求和通项公式)

则该方法的时间复杂度为:O(log2n)

例题5:

// 假设 n > i
void func(int n) {
    if(n <= 1) return 1;
    return n * func(n-1);
}
当n = 1 时:
n = 1时, f(1) = 1;
n = 2时, f(2) = 2 * f(1);
n = 3时, f(3) = 3 * f(2);
…
n = n时, f(n) = n * f(n-1);

两边相乘得:

f(1) * f(2) * f(3) * … * f(n) = 1 * (2 * f(1)) * (3 * f(2)) * … * (n * f(n-1)) 
则f(n) = n!
则:O(n) = O(n!) = O(n) (用归纳法可得O(n!) = O(n))

4. 算法在编程中的应用:
假设当存在字符集两个数组char array1[],char array2[],判断当两个字符集有重复字符时就返回true,否则返回false,其中char array1[]长度为n, char array2[]为n。

普通青年:

public Boolean func1(char array1[], char array2[]) {
    for (int i = 0; i <= array1.length; i++) {
        for (int j = 0; i <= array2.length; i++) {
            if (array1[i] == array2[j]) {
                return Boolean.TRUE;
            }
            }
        }
    return Boolean.FALSE;
}

该算法一共执行 T(n) = O(n^2);

算法青年:

public Boolean fun1(char array1[], char array2[]) {
    Map arrayMap =  new HashMap<>(array1.length * 2);
    for(int i = 0; i < array1.length; i++) {
        arrayMap.put(array1[i], i);
    }

    for(int j = 0; j < array2.length; j++) {
        if (arrayMap.containsKey(array2[j])) {
            return Boolean.TRUE;
        }
    }
    return Boolean.FALSE;
}

查看Map底层源码,根据key找value的时间复杂度为O(1);
该算法一共执行 T(n) = O(n + n) = O(n);

当n越大时,第一种实现方式和第二种实现方式体现出来的差距越大,这就是算法的魅力!

讨论数量: 0

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!