# 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.