📜  时间复杂度和空间复杂度

📅  最后修改于: 2022-05-13 01:56:09.866000             🧑  作者: Mango

时间复杂度和空间复杂度

通常,使用不同算法解决计算机科学问题的方法总是不止一种。因此,非常需要使用一种方法来比较解决方案,以判断哪个解决方案更优。方法必须是:

  • 独立于运行算法的机器及其配置。
  • 显示与输入数量的直接相关性。
  • 可以清楚地区分两种算法,没有歧义。

使用了两种这样的方法,时间复杂度空间复杂度,下面将讨论:

时间复杂度:算法的时间复杂度量化了算法运行所花费的时间,作为输入长度的函数。请注意,运行时间是输入长度的函数,而不是运行算法的机器的实际执行时间。

为了计算算法的时间复杂度,假设执行一个操作花费恒定时间 c ,然后计算N上输入长度的总操作数。考虑一个例子来理解计算过程:假设一个问题是查找数组中是否存在一对(X, Y) , A 的N个元素的和为Z 。最简单的想法是考虑每一对并检查它是否满足给定条件。

伪代码如下:

int a[n];
for(int i = 0;i < n;i++)
  cin >> a[i]
  

for(int i = 0;i < n;i++)
  for(int j = 0;j < n;j++)
    if(i!=j && a[i]+a[j] == z)
       return true

return false

下面是上述方法的实现:

C++
// C++ program for the above approach
#include 
using namespace std;
 
// Function to find a pair in the given
// array whose sum is equal to z
bool findPair(int a[], int n, int z)
{
    // Iterate through all the pairs
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
 
            // Check if the sum of the pair
            // (a[i], a[j]) is equal to z
            if (i != j && a[i] + a[j] == z)
                return true;
 
    return false;
}
 
// Driver Code
int main()
{
    // Given Input
    int a[] = { 1, -2, 1, 0, 5 };
    int z = 0;
    int n = sizeof(a) / sizeof(a[0]);
 
    // Function Call
    if (findPair(a, n, z))
        cout << "True";
    else
        cout << "False";
    return 0;
}


Java
// Java program for the above approach
import java.lang.*;
import java.util.*;
 
class GFG{
 
// Function to find a pair in the given
// array whose sum is equal to z
static boolean findPair(int a[], int n, int z)
{
     
    // Iterate through all the pairs
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
         
            // Check if the sum of the pair
            // (a[i], a[j]) is equal to z
            if (i != j && a[i] + a[j] == z)
                return true;
 
    return false;
}
 
// Driver code
public static void main(String[] args)
{
     
    // Given Input
    int a[] = { 1, -2, 1, 0, 5 };
    int z = 0;
    int n = a.length;
     
    // Function Call
    if (findPair(a, n, z))
        System.out.println("True");
    else
        System.out.println("False");
}
}
 
// This code is contributed by avijitmondal1998


Python3
# Python3 program for the above approach
 
# Function to find a pair in the given
# array whose sum is equal to z
def findPair(a, n, z) :
     
    # Iterate through all the pairs
    for i in range(n) :
        for j in range(n) :
 
            # Check if the sum of the pair
            # (a[i], a[j]) is equal to z
            if (i != j and a[i] + a[j] == z) :
                return True
 
    return False
 
# Driver Code
 
# Given Input
a = [ 1, -2, 1, 0, 5 ]
z = 0
n = len(a)
 
# Function Call
if (findPair(a, n, z)) :
    print("True")
else :
    print("False")
     
    # This code is contributed by splevel62.


C#
// C# program for above approach
using System;
 
class GFG{
 
// Function to find a pair in the given
// array whose sum is equal to z
static bool findPair(int[] a, int n, int z)
{
     
    // Iterate through all the pairs
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
         
            // Check if the sum of the pair
            // (a[i], a[j]) is equal to z
            if (i != j && a[i] + a[j] == z)
                return true;
 
    return false;
}
 
// Driver Code
static void Main()
{
     // Given Input
    int[] a = { 1, -2, 1, 0, 5 };
    int z = 0;
    int n = a.Length;
     
    // Function Call
    if (findPair(a, n, z))
        Console.WriteLine("True");
    else
        Console.WriteLine("False");
}
}
 
// This code is contributed by sanjoy_62.


Javascript


C++
count = 0
for (int i = N; i > 0; i /= 2)
  for (int j = 0; j < i; j++)
    count++;


Java
int count = 0 ;
for (int i = N; i > 0; i /= 2)
    for (int j = 0; j < i; j++)
        count++;
 
//This code is contributed by Shubham Singh


Python3
count = 0
i = N
while(i > 0):
  for j in range(i):
    count+=1
  i /= 2
   
  # This code is contributed by subhamsingh10


C#
int count = 0 ;
for (int i = N; i > 0; i /= 2)
    for (int j = 0; j < i; j++)
        count++;
 
// This code is contributed by Shubham Singh


Javascript
let count = 0
for(let i = N; i > 0; i /= 2)
    for(let j = 0; j < i; j++)
        count += 1;
  
// This code is contributed by Shubham Singh


C++
// C++ program for the above approach
#include 
using namespace std;
 
// Function to count frequencies of array items
void countFreq(int arr[], int n)
{
    unordered_map freq;
 
    // Traverse through array elements and
    // count frequencies
    for (int i = 0; i < n; i++)
        freq[arr[i]]++;
 
    // Traverse through map and print frequencies
    for (auto x : freq)
        cout << x.first << " " << x.second << endl;
}
 
// Driver Code
int main()
{
    // Given array
    int arr[] = { 10, 20, 20, 10, 10, 20, 5, 20 };
    int n = sizeof(arr) / sizeof(arr[0]);
 
    // Function Call
    countFreq(arr, n);
    return 0;
}


Java
// Java program for the above approach
import java.util.*;
class GFG{
 
  // Function to count frequencies of array items
  static void countFreq(int arr[], int n)
  {
    HashMap freq = new HashMap<>();
 
    // Traverse through array elements and
    // count frequencies
    for (int i = 0; i < n; i++) {
      if(freq.containsKey(arr[i])){
        freq.put(arr[i], freq.get(arr[i])+1);
      }
      else{
        freq.put(arr[i], 1);
      }
    }
 
    // Traverse through map and print frequencies
    for (Map.Entry x : freq.entrySet())
      System.out.print(x.getKey()+ " " +  x.getValue() +"\n");
  }
 
  // Driver Code
  public static void main(String[] args)
  {
    // Given array
    int arr[] = { 10, 20, 20, 10, 10, 20, 5, 20 };
    int n = arr.length;
 
    // Function Call
    countFreq(arr, n);
  }
}
 
// This code is contributed by gauravrajput1


Python3
# Python program for the above approach
 
# Function to count frequencies of array items
def countFreq(arr, n):
    freq = dict()
     
    # Traverse through array elements and
    # count frequencies
    for i in arr:
        if i not in freq:
            freq[i] = 0
        freq[i]+=1
         
    # Traverse through map and print frequencies
    for x in freq:
        print(x, freq[x])
 
# Driver Code
 
# Given array
arr =  [10, 20, 20, 10, 10, 20, 5, 20 ]
n = len(arr)
 
# Function Call
countFreq(arr, n)
 
# This code is contributed by Shubham Singh


输出
False

假设计算机中的每个操作都需要大约恒定的时间,让它是c 。执行的代码行数实际上取决于Z的值。在算法分析过程中,主要考虑最坏的情况,即,当不存在总和等于Z的元素对时。在最坏的情况下,

  • 输入需要N*c 次操作。
  • 外循环i循环运行N次。
  • 对于每个i ,内部循环j循环运行N次。

所以总执行时间是N*c + N*N*c + c 。现在忽略低阶项,因为低阶项对于大输入相对无关紧要,因此仅采用最高阶项(没有常数),在这种情况下为N*N 。不同的符号用于描述函数的限制行为,但由于采用了最坏的情况,因此将使用 big-O 符号来表示时间复杂度。

因此,上述算法的时间复杂度为O(N 2 ) 。请注意,时间复杂度仅基于数组A中的元素数量,即输入长度,因此如果数组长度增加,执行时间也会增加。

增长顺序是执行时间如何取决于输入的长度。在上面的示例中,很明显,执行时间与数组的长度成二次方相关。增长顺序将有助于轻松计算运行时间。

另一个例子:让我们计算以下算法的时间复杂度:

C++

count = 0
for (int i = N; i > 0; i /= 2)
  for (int j = 0; j < i; j++)
    count++;

Java

int count = 0 ;
for (int i = N; i > 0; i /= 2)
    for (int j = 0; j < i; j++)
        count++;
 
//This code is contributed by Shubham Singh

Python3

count = 0
i = N
while(i > 0):
  for j in range(i):
    count+=1
  i /= 2
   
  # This code is contributed by subhamsingh10

C#

int count = 0 ;
for (int i = N; i > 0; i /= 2)
    for (int j = 0; j < i; j++)
        count++;
 
// This code is contributed by Shubham Singh

Javascript

let count = 0
for(let i = N; i > 0; i /= 2)
    for(let j = 0; j < i; j++)
        count += 1;
  
// This code is contributed by Shubham Singh

这是一个棘手的案例。乍一看,复杂度似乎是O(N * log N)N代表j 的循环, log(N)代表i 的循环。但这是错误的。让我们看看为什么。

想想count++会运行多少次。

  • i = N时,它将运行N次。
  • i = N / 2时,它将运行N / 2次。
  • i = N / 4时,它将运行N / 4次。
  • 等等。

count++将运行的总次数是N + N/2 + N/4+…+1= 2 * N 。所以时间复杂度将是O(N)

下面列出了一些一般的时间复杂度以及它们在竞争性编程中被接受的输入范围:

Input LengthWorst Accepted Time Complexity

Usually type of solutions

10 -12

O(N!)

Recursion and backtracking

15-18

O(2N * N)

Recursion, backtracking, and bit manipulation

18-22

O(2N * N)

Recursion, backtracking, and bit manipulation

30-40

                       O(2N/2 * N)

Meet in the middle, Divide and Conquer

100

O(N4)

Dynamic programming, Constructive

400

O(N3)

Dynamic programming, Constructive

2K

O(N2* log N)

Dynamic programming, Binary Search, Sorting, 
Divide and Conquer

10K

O(N2)

Dynamic programming, Graph, Trees, Constructive

1M

O(N* log N)

Sorting, Binary Search, Divide and Conquer

100M

O(N), O(log N), O(1)

Constructive, Mathematical, Greedy Algorithms

空间复杂度:算法的空间复杂度量化了算法运行所占用的空间量,作为输入长度的函数。考虑一个例子:假设一个问题是找到数组元素的频率。

伪代码如下:

int freq[n];
int a[n];

for(int i = 0; i>a[i];
   freq[a[i]]++;
}  

下面是上述方法的实现:

C++

// C++ program for the above approach
#include 
using namespace std;
 
// Function to count frequencies of array items
void countFreq(int arr[], int n)
{
    unordered_map freq;
 
    // Traverse through array elements and
    // count frequencies
    for (int i = 0; i < n; i++)
        freq[arr[i]]++;
 
    // Traverse through map and print frequencies
    for (auto x : freq)
        cout << x.first << " " << x.second << endl;
}
 
// Driver Code
int main()
{
    // Given array
    int arr[] = { 10, 20, 20, 10, 10, 20, 5, 20 };
    int n = sizeof(arr) / sizeof(arr[0]);
 
    // Function Call
    countFreq(arr, n);
    return 0;
}

Java

// Java program for the above approach
import java.util.*;
class GFG{
 
  // Function to count frequencies of array items
  static void countFreq(int arr[], int n)
  {
    HashMap freq = new HashMap<>();
 
    // Traverse through array elements and
    // count frequencies
    for (int i = 0; i < n; i++) {
      if(freq.containsKey(arr[i])){
        freq.put(arr[i], freq.get(arr[i])+1);
      }
      else{
        freq.put(arr[i], 1);
      }
    }
 
    // Traverse through map and print frequencies
    for (Map.Entry x : freq.entrySet())
      System.out.print(x.getKey()+ " " +  x.getValue() +"\n");
  }
 
  // Driver Code
  public static void main(String[] args)
  {
    // Given array
    int arr[] = { 10, 20, 20, 10, 10, 20, 5, 20 };
    int n = arr.length;
 
    // Function Call
    countFreq(arr, n);
  }
}
 
// This code is contributed by gauravrajput1

Python3

# Python program for the above approach
 
# Function to count frequencies of array items
def countFreq(arr, n):
    freq = dict()
     
    # Traverse through array elements and
    # count frequencies
    for i in arr:
        if i not in freq:
            freq[i] = 0
        freq[i]+=1
         
    # Traverse through map and print frequencies
    for x in freq:
        print(x, freq[x])
 
# Driver Code
 
# Given array
arr =  [10, 20, 20, 10, 10, 20, 5, 20 ]
n = len(arr)
 
# Function Call
countFreq(arr, n)
 
# This code is contributed by Shubham Singh
输出
5 1
10 3
20 4

这里两个长度为N的数组和变量i用于算法中,因此使用的总空间为N * c + N * c + 1 * c = 2N * c + c ,其中c是所占用的单位空间。对于许多输入,常数c是微不足道的,可以说空间复杂度为O(N)

还有辅助空间,不同于空间复杂度。主要区别在于空间复杂度量化了算法使用的总空间,辅助空间量化了除了给定输入之外的算法中使用的额外空间。在上面的示例中,辅助空间是 freq[] 数组使用的空间,因为它不是给定输入的一部分。所以总的辅助空间是N * c + c ,这只是O(N)