ScholarQuill logoScholarQuillUniversity Notes
  • Notes
  • Past Papers
  • Blogs
  • Todo
Login
ScholarQuill logoScholarQuillUniversity Notes
Login
NotesPast PapersBlogsTodo
More
SubjectsDiscussionCGPA CalculatorGPA CalculatorStudent PortalCourse Outline
About
About usPrivacy PolicyReportContact
Notes
Past Papers
Blogs
Todo
Analytics
    Current Subject
    🧩
    Data Structures & Algorithms
    CC-213
    Progress0 / 37 topics
    Topics
    1. Abstract Data Types2. Complexity Analysis3. Big Oh Notation4. Stacks (Linked Lists and Array Implementations)5. Recursion and analyzing recursive algorithms6. Divide and Conquer Algorithms7. Sorting Algorithms8. Selection Sort9. Insertion Sort10. Merge Sort11. Quick Sort12. Bubble Sort13. Heap Sort14. Shell Sort15. Radix Sort16. Bucket Sort17. Queue18. Dequeuer19. Priority Queues (linked and array implementations of queues)20. Linked List and Its Various Types21. Sorted Linked List22. Searching an Unsorted Array23. Binary Search for Sorted Arrays24. Hashing and Indexing25. Open Addressing and Chaining26. Trees and Tree Traversals27. Binary Search Trees28. Heaps29. M-Way Trees30. Balanced Trees31. Graphs32. Breadth-First Traversal33. Depth-First Traversal34. Topological Order35. Shortest Path36. Adjacency Matrix and Adjacency List Implementations37. Memory Management and Garbage Collection
    CC-213›Complexity Analysis
    Data Structures & AlgorithmsTopic 2 of 37

    Complexity Analysis

    7 minread
    1,138words
    Intermediatelevel

    Complexity Analysis

    Complexity analysis in computer science is the study of how the performance of an algorithm or a data structure grows with the size of the input. It helps us understand how efficient an algorithm is in terms of time (how fast it runs) and space (how much memory it uses).

    There are two main types of complexity:

    1. Time Complexity: How the execution time of an algorithm grows as the input size increases.
    2. Space Complexity: How the memory usage of an algorithm grows as the input size increases.

    Time Complexity

    Time complexity tells us how many operations an algorithm needs to perform relative to the size of the input. It’s often expressed using Big O notation, which describes the worst-case scenario of the algorithm.

    Big O Notation

    Big O notation helps to classify algorithms based on their time or space requirements. Here are some common Big O notations:

    1. O(1) – Constant Time: The algorithm takes the same amount of time regardless of the input size.

      • Example: Accessing an element in an array by its index.
      • C++ Example:
        int arr[5] = {1, 2, 3, 4, 5};
        int element = arr[2];  // O(1)
        
    2. O(log n) – Logarithmic Time: The algorithm's time increases logarithmically as the input size increases. This usually happens in algorithms that divide the input into smaller parts.

      • Example: Binary Search.
      • C++ Example:
        int binarySearch(int arr[], int size, int target) {
            int low = 0, high = size - 1;
            while (low <= high) {
                int mid = (low + high) / 2;
                if (arr[mid] == target) return mid;
                else if (arr[mid] < target) low = mid + 1;
                else high = mid - 1;
            }
            return -1;
        }
        // O(log n)
        
    3. O(n) – Linear Time: The time grows linearly with the input size. If the input doubles, the execution time doubles.

      • Example: Traversing an array.
      • C++ Example:
        void printArray(int arr[], int size) {
            for (int i = 0; i < size; i++) {
                cout << arr[i] << " ";
            }
        }
        // O(n)
        
    4. O(n log n) – Linearithmic Time: Often occurs in algorithms that involve both dividing the input (like O(log n)) and then processing each part (like O(n)).

      • Example: Merge Sort, Quick Sort.
      • C++ Example (Merge Sort):
        void merge(int arr[], int l, int m, int r) {
            // Merging logic
        }
        
        void mergeSort(int arr[], int l, int r) {
            if (l < r) {
                int m = l + (r - l) / 2;
                mergeSort(arr, l, m);
                mergeSort(arr, m + 1, r);
                merge(arr, l, m, r);
            }
        }
        // O(n log n)
        
    5. O(n²) – Quadratic Time: The time increases quadratically with the input size. If the input size doubles, the time quadruples. This usually happens in algorithms that have nested loops.

      • Example: Bubble Sort, Selection Sort.
      • C++ Example (Bubble Sort):
        void bubbleSort(int arr[], int n) {
            for (int i = 0; i < n - 1; i++) {
                for (int j = 0; j < n - i - 1; j++) {
                    if (arr[j] > arr[j + 1]) {
                        swap(arr[j], arr[j + 1]);
                    }
                }
            }
        }
        // O(n²)
        
    6. O(2ⁿ) – Exponential Time: The time doubles with each addition to the input size. This is typical in algorithms that solve problems by recursively exploring all possible solutions.

      • Example: Solving the Tower of Hanoi problem.
      • C++ Example:
        void towerOfHanoi(int n, char from_rod, char to_rod, char aux_rod) {
            if (n == 1) {
                cout << "Move disk 1 from rod " << from_rod << " to rod " << to_rod << endl;
                return;
            }
            towerOfHanoi(n - 1, from_rod, aux_rod, to_rod);
            cout << "Move disk " << n << " from rod " << from_rod << " to rod " << to_rod << endl;
            towerOfHanoi(n - 1, aux_rod, to_rod, from_rod);
        }
        // O(2ⁿ)
        
    7. O(n!) – Factorial Time: The time increases dramatically as the input size increases. This is typical in brute-force solutions that try every possible configuration.

      • Example: Solving the Traveling Salesman Problem with a brute-force approach.

    Space Complexity

    Space complexity measures how much memory an algorithm uses, relative to the size of the input. Just like time complexity, space complexity is expressed in Big O notation.

    For example, if you're using an extra array to store results, your space complexity might increase. Here are some typical space complexities:

    1. O(1) – Constant space: The algorithm uses the same amount of memory, regardless of the input size.

      • Example: Swapping two numbers in-place.
    2. O(n) – Linear space: The memory usage grows linearly with the input size.

      • Example: Storing an array of size n.

    Best Case, Worst Case, and Average Case

    When analyzing an algorithm, there are different scenarios:

    • Best Case: The minimum amount of time the algorithm will take (optimistic scenario).

      • Example: In a sorted array, the best case for linear search is finding the element in the first position, which takes O(1) time.
    • Worst Case: The maximum amount of time the algorithm will take (pessimistic scenario).

      • Example: In linear search, the worst case is when the element is at the end of the array or not present, taking O(n) time.
    • Average Case: The expected time the algorithm will take, considering all possible inputs. This can be harder to calculate but gives a realistic picture of performance.

    Example: Linear Search Complexity Analysis

    Let's consider linear search in an unsorted array.

    • Time Complexity:

      • Best Case: O(1) – If the element is found at the first index.
      • Worst Case: O(n) – If the element is not found or at the last index.
      • Average Case: O(n) – On average, the algorithm will look through half the elements.
    • Space Complexity: O(1) – No extra space is used apart from a few variables for indexing and comparisons.

    Why is Complexity Analysis Important?

    1. Efficiency: It helps in selecting the best algorithm for the task. If you have a large dataset, you want an algorithm that runs efficiently with low time complexity.

    2. Scalability: Complexity analysis tells you how the algorithm will behave as the input size grows. This is especially important when dealing with big data.

    3. Performance: Understanding the complexity helps you optimize both time and space usage, which is crucial for resource-constrained environments like embedded systems or mobile devices.

    Conclusion

    Complexity analysis helps us measure the efficiency of algorithms by focusing on how they perform with increasing input size. By using Big O notation, we can evaluate the time and space complexity, allowing us to make better choices when designing and implementing algorithms.

    Previous topic 1
    Abstract Data Types
    Next topic 3
    Big Oh Notation

    Past Papers

    Open this section to load past papers

    Click on Show Past Papers to see past papers.
    On This Page
      Reading Stats
      Est. reading time7 min
      Word count1,138
      Code examples0
      DifficultyIntermediate