Dry run the input by hand and note micro steps taken to reach the output.(This helps in devising the algorithm)
Using inputs/outputs other than the sample test cases or using input/output which covers the maximum conditions of the initial algorithm you built as well as those which cover edge or unique cases. (This makes sure you did not miss any specific case).
Checking if properties of data structures I know fit the situation(e.g property of heap keeping max on top always) or try using different data structures for the same problem.
Checking if a problem can be solved using some standard concepts e.g divide and conquer,backtracking, dynamic programming etc.
Using constraints to judge the complexity of the expected solution.
() It’s beautifully explained here by Coding Ninjas.
Find repetition of work in code, these are places where you can optimize your solutions.
This one is very unique and awesome. Reverse engineer the problem, start from the output and go to the input. An example would be defining the search space of output and then using a binary search and then validating the input at every point. Basically trying to remove points which cannot be the solution.
Look at the constraints and try to tick them all i.e make sure your algorithm uses them somewhere, there are rarely some problems where the constraints don’t matter to the solution.
Not a tip but just for a positive mind set, I keep this in mind.
“The problem is solvable and definitely has a solution.”
Keep adding if you have any more.