-
Book Overview & Buying
-
Table Of Contents
-
Feedback & Rating
C# Data Structures and Algorithms - Second Edition
By :
As mentioned previously, algorithms are almost everywhere, and even intuitively, you use them each day while solving various tasks. There is an enormous amount of algorithms and a lot of their types, chosen according to different criteria. To simplify this topic, only a few types will be presented here, chosen from different classifications, to show you a variety and encourage you to learn more about them on your own. It is also quite common that the same algorithm is classified into a few groups.
First, let’s take a look at recursive algorithms, which are strictly connected with the idea of recursion and are the opposite of iterative algorithms. What does this mean? An algorithm is recursive if it calls itself to solve smaller subproblems of the original problem. The algorithm calls itself multiple times until the base condition is met.
This technique provides you with a powerful solution for solving problems, can limit the amount of code, and can be easy to understand and maintain. However, recursion has some drawbacks related to performance or the requirement for more space in the stack’s memory, which could lead to stack overflow problems. Fortunately, you can prevent some of these issues using dynamic programming, an optimization technique that supports recursion.
Recursion can be used in several algorithms, including the following:
n!)Another group of algorithms is named divide and conquer. It is related to the algorithmic paradigm of solving a problem by breaking it down into smaller subproblems (the “divide” step), calling them recursively until they are simple enough to be solved directly (“conquer”), and combining the results of subproblems to get the final result (“combine”). This approach has many advantages, also taken from the pros of recursion, including ease of implementation, understanding, and maintenance. By dividing the problem into many subproblems, it supports parallel computing, which can lead to performance improvements. Unfortunately, this paradigm also has some disadvantages, including the necessity for a proper base case definition to terminate the execution of the algorithm. Performance issues, similar as in the case of recursive algorithms, can exist as well.
Divide and conquer is a popular approach for solving various algorithmic problems and you can see its implementations in a broad range of applications:
Next, we’ll cover back-tracking algorithms. They are used for solving problems that consist of a sequence of decisions, each depending on the decisions that have already been taken, incrementally building the solution. When you realize that the decisions that have been taken do not provide the correct solution, you backtrack. Of course, you can support this approach with recursion to try various variants and therefore find a suitable solution, if one exists.
You can use this approach for many tasks, including the following:
Now that we’ve covered the recursive, divide-and-conquer, and back-tracking algorithms, it’s high time to present another type, namely greedy algorithms. A greedy algorithm builds the solution piece by piece by choosing the best option in each step, not concerned about the overall solution, and being short-sighted in its operation. For this reason, there is no guarantee that the final result is optimal. However, in many scenarios, using the local optimal solutions can lead to global optimal solutions or can be good enough.
Here are some examples:
Now, it’s time to add more “magic” to your algorithms via heuristics! A heuristic algorithm calculates a near-optimal solution for an optimization problem and is especially useful for scenarios when the exact methods are not available or are too slow. Thus, you can see a significant speed boost, but with a decreased accuracy of the result. Such an approach is popular and adequate for solving various real-world problems, often complex and big, and is applied in many different fields of science, even those regarding bioinformatics.
Heuristic algorithms have many applications and subtypes:
Since we’re talking about various types of algorithms, it is worth mentioning dynamic programming. It is a technique that optimizes recursive algorithms by limiting the necessity of computing the same result multiple times. This technique can be used in one of two approaches:
Both of these approaches can significantly decrease the time complexity and increase performance, and therefore speed up your algorithm. Every time you use recursion, it is a good idea to try to optimize it using dynamic programming. If you want to learn how to optimize calculating a number from the Fibonacci series, go to Chapter 9, See in Action.
You can also use dynamic programming to find the shortest path between all pairs of vertices in a graph by using the Floyd-Warshall algorithm, as well as in Dijkstra’s algorithm. Another application is for solving the Tower of Hanoi mathematical game. Possibilities are even broader and you can also apply it to artificial neural networks.
While we’re presenting various types of algorithms, we should also consider brute-force algorithms. A brute-force algorithm is a general solution for solving a problem by checking all possible options and choosing the best one. It is an approach that can have huge time complexity and its operation can take a lot of time, so it can be useless in real-world scenarios. However, a brute-force algorithm is often the first choice when you need to solve some algorithmic problem. There’s nothing bad in doing this as you can learn more about the domain of the problem you wish to solve and see some results for simpler cases. Nevertheless, while developing an algorithm, it is a good idea to enhance it significantly by using other paradigms.
Here are some examples of where you can use brute-force algorithms:
After introducing a few types of algorithms, you can see that some of them provide you with a faster solution while others can have huge time complexity. But what does this mean? You will learn about computational complexity, especially time complexity, in the next section.
Change the font size
Change margin width
Change background colour