# How to Develop Algorithmic Thinking in Data Structures and Algorithms?

Algorithmic Thinking has recently become a buzzword among programmers. It is a method for solving problems based on a clear definition of steps: logically and repeatedly. This could help us understand several problem-solving strategies.

In programming, two skills are critical to solving a coding problem: 1) Identifying and designing an efficient solution approach and 2) Converting the solution approach into a correct code. Some programmers face challenges developing the first skill, which requires algorithmic thinking. On another side, sometimes interviewers offering problems during interviews claim that they are more interested in how an interviewee approaches a problem than the implementation of the solution. Showing expertise in algorithmic thinking and applying problem-solving strategies can be an attractive way to impress a potential employer.

The critical question is: how do we develop algorithmic thinking independently from learning programming? There are several ways of it! Let's explore.

### Algorithmic Thinking via Solving Puzzles

Solving algorithmic puzzles is a productive and enjoyable way to develop algorithmic thinking. This could help us think about a coding problem on a more abstract level to develop ideas related to problem-solving.

For example, we can learn several problem-solving strategies using puzzles: Decrease and conquer (Josephus problem), Divide and conquer (Finding a fake among eight coins), Dynamic programming (Egg dropping puzzle), Backtracking (8-queen puzzle), Greedy approach (Fractional knapsack problem), String manipulation (Fizz buzz problem), Mathematical approach (The water jug puzzle), etc. Here are some more examples of such types of puzzles:

Puzzle based on the properties of numbers theory Puzzle based on the divide and conquer approach Puzzle based on the properties of the graph Puzzle based on the incremental approach Puzzle based on the graph-based transformation Puzzle based on the dynamic programming Puzzle based on the incremental approach Puzzle based on the idea of backtracking Puzzle based on iterative elimination Puzzle based on the properties of the graph Puzzle based on the idea of elimination Puzzle based on the idea of transformation Puzzle based on mathematical counting Here are some essential ideas related to the algorithmic puzzle:

• Every puzzle has an input that defines an instance of the puzzle. The instance can be either specific (e.g., find a false coin among eight coins with a balance) or general (e.g., find a false coin among N coins with a balance).
• When dealing with a specific instance of a puzzle, one must focus on the particular instance given. It might be the case that other instances of the puzzle do not have the same solution or even do not have solutions at all!
• On the other hand, solving the general instance of the puzzle could be more satisfying and even more comfortable most of the time.
• But whether a puzzle is presented in its general form, it is always a good idea to solve a few small instances of it anyway because it can provide valuable insights into the puzzle given.

### Algorithmic Thinking via Practicing Steps of Problem-Solving

The solution to every problem has to go through well-defined steps, and practicing these steps helps us save time and understand patterns among several coding problems. Step 1: Understanding the problem statement

We need to understand the problem by drawing a clear visualization. We should ask these critical questions during this process: Do we know every word used within the problem? What data or information are provided as input? What data or results are requested as an output? In addition to this, we need to understand the computational and non-computational details relevant to the problem like data structures, specific constraints given on input, distribution of the input, etc.

Step 2: Selecting concepts and solution strategies based on experience

Based on the problem description, we need to make predictions regarding the concepts necessary to solve the problem. We should think: Are we going to use all the information available in the problem? Is it possible to eliminate unnecessary information? Have we solved any similar problems in the past? If yes, then take advantage of that experience. Identifying approaches or concepts or already known algorithms for similar problems can help us save a lot of time.

Step 3: Formulation of a solution strategy and pseudocode

We can explore the hand-written way to solve the problem by going through several examples and developing a general step-by-step strategy. To do this, we need to carefully think about each step and observe what actions are common to every example. Two types of thinking are important at this stage:

• Iterative Thinking: solving the problem by building the partial solution.
• Recursive Thinking: solving the problem using the smaller sub-problems.

We need to visualize solution steps on the paper by describing operations to transform the given input into the desired output. After this, we can move forward to write step-by-step solutions in simple English and Transform them into pseudocode or flowchart.

Step 4: Correct Programming Implementation

Now we can move forward to transform the pseudocode into a working code using our favorite programming languages like C++, Java, Python, etc. During this process, we need to select programming elements for effcient code implementation and simplify data handling: solution function, helper functions, loop initialization and termination, the base case of recursive code, scope and initialization of local variables, global variables, memory allocation, and deallocation, pre-processing, pointers manipulations, etc. Note: Please take care of the correct coding style :)

Step 5: Verifying correctness and optimizing further

Now, we need to test the programming implementation for bugs, analyze the time and space complexity, and think to optimize the solution further. These ideas can work best at this stage:

• Manually analyze the input-output pattern by performing a hand trace. If needed, draw a plot to describe the behavior of the variables or critical steps defined in the solution.
• We need to identify some input for which our algorithm does not provide the correct output. If there is an error or bug, identify and correct the steps responsible for that bug. At the end of the process, our solution must handle all boundary cases.
• After verifying correctness, now we should analyze the efficiency by counting the total number of operations with respect to the input size.
• Now the critical step is to optimize the solution and solve it using a fewer number of steps. But how do we do it? We can identify some essential details of the problem and try a different problem-solving approach. We should keep optimizing the solution further until there is no further possibility.
• Sometimes we can also optimize the solution code further by reducing some intermediate steps in the pseudocode.

Here are some popular coding questions to practice the steps of problem-solving. These questions can be solved using four or more approaches.

### Algorithmic Thinking via Understanding Real-life Applications

In our surroundings, many applications are using efficient problem-solving strategies to deliver the best user experience and performance. Understanding the idea behind such a strategy is a defining step in developing algorithmic thinking. For example:

• Caching strategy: LRU and LFU cache
• Scheduling strategy: Round-robin scheduling, Priority scheduling.
• Hashing strategy: Hash-based load balancing, Encrypting data in cryptography, Symbol table in a compiler, Spelling check.
• Sorting strategy: Comparison-based sorting, linear time sorting.

Enjoy learning, Enjoy thinking!