# Algorithm Complexity

Algorithm complexity, often referred to as time complexity and space complexity, is a crucial concept in computer science that helps us understand how an algorithm's performance scales as the input size increases. It provides insights into how efficient an algorithm is in terms of time and memory usage.

## Time Complexity

Time complexity measures the amount of time an algorithm takes to run in relation to the input size. It is usually denoted by the symbol "O," which stands for "order of." The most common types of time complexities are constant time (O(1)), logarithmic time (O(log n)), linear time (O(n)), linearithmic time (O(n log n)), quadratic time (O(n^2)), and so on. The "O" notation allows us to describe an algorithm's efficiency without getting bogged down in precise execution times.

Consider a simple example of linear search through an array. The time complexity of this algorithm is O(n), where "n" is the number of elements in the array. As the array size increases, the number of comparisons the algorithm makes grows linearly.

## Space Complexity

Space complexity refers to the amount of memory an algorithm uses to solve a problem. It also uses the "O" notation to describe the upper bound on the memory consumption as the input size increases. Similar to time complexity, we have constant space (O(1)), linear space (O(n)), quadratic space (O(n^2)), and so forth.

As the input size increases, algorithms with higher complexity values will rise more quickly than those with lower complexity values.

## Comparison

Here are the most common complexities, sorted by efficiency from most to least efficient:

• O(1) - Constant
• O(log n) - Logarithmic
• O(n) - Linear Time
• O(n log n) - Linearithmic
• O(2^n) - Exponential Time

## Constant Complexity O(1)

Constant complexity, often denoted as O(1), describes an algorithm's efficiency where the execution time or memory usage remains constant regardless of the input size. This means that the algorithm's performance does not depend on how large the data is. It implies that the algorithm executes in a fixed number of operations, making it highly efficient for small and large inputs alike.

Usually this is the cheapest (works fastest/requires least memory) and best option.

## Logarithmic O(log n)

Logarithmic complexity (O(log n)) denotes an algorithm's efficiency where the workload grows slower as the input size increases. Each step reduces the problem size by a constant fraction, resulting in swift performance gains for larger inputs. Commonly seen in binary search and efficient data structures, logarithmic complexity is highly effective for tasks where the data can be repeatedly divided, enabling efficient problem-solving in large datasets.

Example for an algorithm achieving logarithmic complexity is binary search on a sorted array.

## Linear Complexity O(n)

Linear complexity, denoted as O(n), signifies that the performance of an algorithm grows linearly with the size of the input. As the input increases, the algorithm's execution time or memory usage increases proportionally. Each additional element in the input results in a corresponding increase in the algorithm's work, making it efficient but not as fast as constant complexity for larger inputs.

For example, using a simple for loop to search through an array of data(linear search) is of linear complexity.

## Linearithmic O(n log n)

O(n log n), known as linearithmic complexity, is an efficient algorithmic behavior where the execution time or memory usage grows proportionally to the input size multiplied by the logarithm of the input size. Common in divide-and-conquer strategies like efficient sorting (e.g., merge sort, heap sort), it strikes a balance between linear and logarithmic growth, making it faster than quadratic complexities for large datasets. This complexity often characterizes algorithms that repeatedly divide the input and process each subpart, leading to effective performance improvements in scenarios where data needs to be sorted or processed in a structured manner.