# Working with algorithms

## Working with algorithms

### Understanding Algorithms

• An algorithm is a step-by-step process used to solve problems or make decisions.
• The efficiency of an algorithm relates to how effectively it can solve a problem, considering factors such as processing time and memory utilisation.
• Complexity of an algorithm takes into account the volume of work it has to perform depending on the size of the input data.

### Components of Algorithms

• Algorithms comprise of well-defined instructions that are presented in an order suitable for problem-solving.
• Algorithms include variables, which store values for use and manipulation throughout the algorithm.
• Control structures such as loops, branches (if, else if, else) and switch cases play a crucial role in directing the flow of an algorithm.
• Algorithms must include a halt condition to prevent the algorithm from running indefinitely.

### Measures of Algorithm Efficiency

• The time complexity of an algorithm refers to the computational complexity that describes the amount of computational time taken by an algorithm to run, as a function of the size of the input to the program.
• Space complexity of an algorithm quantifies the amount of space or memory taken by an algorithm to run as a function of the length of the input.
• Balancing time and space complexity is often a key aspect of efficient algorithm design.

### Testing and Debugging Algorithms

• An essential part of working with algorithms involves debugging, which is the process of identifying syntax errors, logical errors or runtime errors within an algorithm.
• Unit testing allows you to verify the correctness of an algorithm, it involves testing individual sections of an algorithm’s source code.
• For a comprehensive test, supply the algorithm with a variety of inputs including but not limited to, edge cases, invalid inputs, zero inputs and large inputs.

### Analysing and Optimising Algorithms

• Big O notation is used to describe the performance or complexity of an algorithm. It shows how the run time of the algorithm increases as the input size increases.
• The notion of worst-case scenario is of great importance. It represents the maximum amount of time an algorithm can take to solve a problem.
• Always strive for optimisation. After designing an algorithm, look for opportunities to make it more efficient by reducing the number of processing steps or the amount of memory required.
• Consider trade-offs. While a more efficient algorithm might require more complex code, a simpler algorithm might be easier to read and maintain.

### Algorithms and Data Structures

• Algorithms frequently interact with data structures, such as arrays, linked lists, stacks, queues, trees, and graphs.
• Understanding the characteristics of different data structures can significantly improve the efficiency of an algorithm by choosing the appropriate data structure for the task at hand.
• There is a strong link between algorithms and data structures, so algorithmic concepts often go hand-in-hand with data structures.

### Design Techniques for Algorithms

• A brute force approach tries all possible combinations to solve a problem. It is simple but often computationally intensive.
• Divide and conquer approach involves breaking the problem down into smaller sub-problems that are easier to solve.
• Greedy algorithms make locally optimal choices at each step in the hope of finding a global optimum.
• Dynamic programming approach breaks the problem into smaller subproblems and stores the results of these subproblems to avoid recomputation.