As an Android Kotlin developer, understanding coding patterns is essential for writing clean, efficient, and scalable code. If you're preparing for coding interviews or just want to improve your problem-solving skills, mastering these patterns can significantly enhance your approach to problem-solving.
In this blog, we'll explore some fundamental coding patterns in Kotlin with real-world examples. These patterns are crucial for interviews and general software development.
1. Sliding Window Pattern
The sliding window pattern is useful for solving problems related to contiguous subarrays, substrings, and sequences.
Example: Maximum Sum Subarray of Size K
fun maxSumSubarray(arr: IntArray, k: Int): Int {
    var maxSum = 0
    var windowSum = 0
    var start = 0
    for (end in arr.indices) {
        windowSum += arr[end]
        
        if (end >= k - 1) {
            maxSum = maxOf(maxSum, windowSum)
            windowSum -= arr[start]
            start++
        }
    }
    return maxSum
}2. Two Pointers Pattern
This pattern is commonly used when dealing with sorted arrays or linked lists to find pairs that match a condition.
Example: Pair Sum in Sorted Array
fun hasPairWithSum(arr: IntArray, target: Int): Boolean {
    var left = 0
    var right = arr.size - 1
    while (left < right) {
        val sum = arr[left] + arr[right]
        when {
            sum == target -> return true
            sum < target -> left++
            else -> right--
        }
    }
    return false
}3. Fast & Slow Pointers Pattern
This pattern is useful for cycle detection in linked lists and similar structures.
Example: Detecting Cycle in Linked List
class ListNode(val value: Int) {
    var next: ListNode? = null
}
fun hasCycle(head: ListNode?): Boolean {
    var slow = head
    var fast = head
    
    while (fast?.next != null) {
        slow = slow?.next
        fast = fast.next?.next
        if (slow == fast) return true
    }
    return false
}4. Merge Intervals Pattern
This pattern is useful for interval-related problems, such as merging overlapping intervals.
Example: Merging Overlapping Intervals
data class Interval(val start: Int, val end: Int)
fun mergeIntervals(intervals: List<Interval>): List<Interval> {
    if (intervals.isEmpty()) return emptyList()
    
    val sortedIntervals = intervals.sortedBy { it.start }
    val merged = mutableListOf(sortedIntervals[0])
    
    for (i in 1 until sortedIntervals.size) {
        val last = merged.last()
        val current = sortedIntervals[i]
        
        if (current.start <= last.end) {
            merged[merged.lastIndex] = Interval(last.start, maxOf(last.end, current.end))
        } else {
            merged.add(current)
        }
    }
    return merged
}5. Backtracking Pattern
Backtracking is useful for problems like permutations, combinations, and solving puzzles.
Example: Generating All Subsets
fun generateSubsets(nums: IntArray): List<List<Int>> {
    val result = mutableListOf<List<Int>>()
    fun backtrack(start: Int, current: MutableList<Int>) {
        result.add(ArrayList(current))
        for (i in start until nums.size) {
            current.add(nums[i])
            backtrack(i + 1, current)
            current.removeAt(current.size - 1)
        }
    }
    backtrack(0, mutableListOf())
    return result
}6. Dynamic Programming (DP) Pattern
DP is a powerful technique for optimizing recursive solutions by storing intermediate results.
Example: Fibonacci Sequence Using Memoization
fun fibonacci(n: Int, memo: MutableMap<Int, Int> = mutableMapOf()): Int {
    if (n <= 1) return n
    if (memo.containsKey(n)) return memo[n]!!
    
    memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo)
    return memo[n]!!
}7. Greedy Algorithm Pattern
Greedy algorithms work by making the locally optimal choice at each step.
Example: Minimum Coins for Change
fun minCoins(coins: IntArray, amount: Int): Int {
    coins.sortDescending()
    var remaining = amount
    var count = 0
    
    for (coin in coins) {
        if (remaining == 0) break
        count += remaining / coin
        remaining %= coin
    }
    return if (remaining == 0) count else -1
}8. Graph Traversal Pattern
Graph algorithms often use BFS and DFS for traversal and search problems.
Example: BFS Traversal in Graph
fun bfs(graph: Map<Int, List<Int>>, start: Int): List<Int> {
    val queue = ArrayDeque<Int>()
    val visited = mutableSetOf<Int>()
    val result = mutableListOf<Int>()
    
    queue.add(start)
    visited.add(start)
    
    while (queue.isNotEmpty()) {
        val node = queue.removeFirst()
        result.add(node)
        
        for (neighbor in graph[node] ?: emptyList()) {
            if (neighbor !in visited) {
                visited.add(neighbor)
                queue.add(neighbor)
            }
        }
    }
    return result
}Here’s a comprehensive list of coding patterns commonly used in problem-solving and coding interviews:
Array & String Patterns
- Sliding Window 
- Two Pointers 
- Fast & Slow Pointers 
- Merge Intervals 
- Kadane’s Algorithm (Maximum Subarray) 
- Dutch National Flag (Sort Colors) 
- Cyclic Sort (Find Missing Numbers) 
Recursion & Backtracking Patterns
- Backtracking (Subset, Permutation, Combination) 
- Branch & Bound 
- Divide and Conquer 
Dynamic Programming (DP) Patterns
- Knapsack (0/1 & Unbounded) 
- Fibonacci Series (Memoization & Tabulation) 
- Longest Common Subsequence (LCS) 
- Palindrome Partitioning 
- Coin Change / Minimum Steps to Reduce a Number 
Greedy Algorithm Patterns
- Activity Selection 
- Huffman Encoding 
- Interval Scheduling 
- Job Scheduling with Deadlines 
Graph Traversal Patterns
- Breadth-First Search (BFS) 
- Depth-First Search (DFS) 
- Dijkstra’s Algorithm (Shortest Path) 
- Bellman-Ford Algorithm 
- Floyd-Warshall Algorithm 
- Topological Sorting (Kahn’s Algorithm) 
- Union-Find (Detect Cycle in Graphs) 
- Minimum Spanning Tree (Kruskal, Prim’s Algorithm) 
Tree & Binary Search Patterns
- Binary Search 
- Binary Search on Answer (Minimize Max Distance, Aggressive Cows) 
- Inorder, Preorder, Postorder Traversal 
- Lowest Common Ancestor (LCA) 
- Trie (Prefix Tree) Usage 
- Segment Tree / Fenwick Tree (Range Queries) 
Heap & Priority Queue Patterns
- Top K Elements (Kth Largest, Kth Smallest) 
- Median of a Stream 
- Merge K Sorted Lists 
Bit Manipulation Patterns
- XOR Manipulation (Find Missing Number, Single Non-Duplicate) 
- Bitmask DP 
Matrix Patterns
- Spiral Traversal 
- Flood Fill (DFS/BFS in Grid) 
- Matrix Exponentiation 
This covers most of the major coding patterns used in problem-solving and interviews.
Conclusion
Understanding these patterns will help you write efficient, readable, and optimized Kotlin code. If you're preparing for interviews, mastering these patterns is crucial.
📢 Don't forget to share this with fellow developers and subscribe to my newsletter for more Kotlin and Android tips! 🚀
Akshay Nandwana
Founder AndroidEngineers
You can connect with me on:
Book 1:1 Session here Click Here
Join our upcoming classes
Our Courses
Get the latest Android development articles delivered to your inbox.