{"id":1817,"date":"2024-10-15T10:41:19","date_gmt":"2024-10-15T10:41:19","guid":{"rendered":"https:\/\/algocademy.com\/blog\/algorithmic-thinking-developing-problem-solving-skills-for-programming-success\/"},"modified":"2026-10-15T10:34:48","modified_gmt":"2026-10-15T10:34:48","slug":"algorithmic-thinking-developing-problem-solving-skills-for-programming-success","status":"publish","type":"post","link":"https:\/\/algocademy.com\/blog\/algorithmic-thinking-developing-problem-solving-skills-for-programming-success\/","title":{"rendered":"Algorithmic Thinking: How to Build Problem-Solving Skills"},"content":{"rendered":"<p>Algorithmic thinking is the ability to turn a vague problem into a precise sequence of steps that always works and is fast enough for the input size. It has less to do with knowing named algorithms than with a habit: define the problem exactly, solve a small case by hand, notice what you did, generalise it, and check its cost. You build it with practice on that habit, and the rest of this post shows the habit applied to a real problem.<\/p>\n<h2>The five steps<\/h2>\n<ol>\n<li><strong>Restate the problem precisely.<\/strong> What are the inputs, their types and size limits? What exactly is the output? What counts as valid? Ask about empty input, duplicates, negative numbers and ties.<\/li>\n<li><strong>Solve small examples by hand.<\/strong> Pick two or three inputs, including a tricky one, and work out the answers without thinking about code. Pay attention to what you actually do.<\/li>\n<li><strong>Write down the simplest correct method.<\/strong> Usually brute force. It gives you a correctness baseline and a complexity to beat.<\/li>\n<li><strong>Find the waste and remove it.<\/strong> Where does the simple method repeat work, or look at data it could have ruled out? That question leads to a better data structure or technique.<\/li>\n<li><strong>Verify.<\/strong> Trace your steps on the examples and edge cases, state the time and space complexity, and compare against the brute force on random inputs if you can.<\/li>\n<\/ol>\n<p>Decomposition (breaking the problem into smaller ones) and abstraction (ignoring details that do not matter, such as treating a map as a graph) run through all five steps. Our post on <a href=\"https:\/\/algocademy.com\/blog\/breaking-down-complex-problems-into-manageable-parts-a-programmers-guide\/\">breaking down complex problems<\/a> covers decomposition in more detail.<\/p>\n<h2>Worked example: longest substring without repeating characters<\/h2>\n<p><strong>1. Restate.<\/strong> Input: a string <code>s<\/code>. Output: the length of the longest contiguous substring in which no character appears twice. For <code>\"abcabcbb\"<\/code> the answer is 3 (<code>\"abc\"<\/code>). For <code>\"\"<\/code> it is 0.<\/p>\n<p><strong>2. By hand.<\/strong> In <code>\"pwwkew\"<\/code> I scan left to right. <code>\"pw\"<\/code> is fine; the next <code>w<\/code> repeats, so I must drop everything up to and including the first <code>w<\/code>. Continue from the second <code>w<\/code>: <code>\"wke\"<\/code> is length 3, then the next <code>w<\/code> repeats again. The answer is 3. Note what I did: I kept a stretch of characters, and when a repeat appeared I moved the start of the stretch forward past the earlier copy.<\/p>\n<p><strong>3. Brute force.<\/strong> Try every start index and extend until a repeat appears:<\/p>\n<pre><code class=\"language-python\">def brute(s):\n    best = 0\n    for i in range(len(s)):\n        seen = set()\n        for j in range(i, len(s)):\n            if s[j] in seen:\n                break\n            seen.add(s[j])\n            best = max(best, j - i + 1)\n    return best<\/code><\/pre>\n<p>This is correct. Its time is O(n\u00b2) in the worst case, and it also re-reads the same characters from many different start positions.<\/p>\n<p><strong>4. Remove the waste.<\/strong> When the inner loop restarts at <code>i + 1<\/code>, it re-scans characters it already knew were distinct. My hand method never went backwards. It kept a start index and, for each character, the last position where it appeared. On a repeat inside the current stretch, jump the start to just after that earlier position.<\/p>\n<pre><code class=\"language-python\">def window(s):\n    last = {}              # char -&gt; most recent index\n    start = best = 0\n    for i, ch in enumerate(s):\n        if ch in last and last[ch] &gt;= start:\n            start = last[ch] + 1\n        last[ch] = i\n        best = max(best, i - start + 1)\n    return best<\/code><\/pre>\n<p>Each character is visited once, so this is O(n) time, with extra space bounded by the number of distinct characters.<\/p>\n<p><strong>5. Verify.<\/strong> Run both functions on examples, including <code>\"abba\"<\/code>, which exposes the need for the check <code>last[ch] &gt;= start<\/code>. Without it, when the last <code>a<\/code> is read, the code would jump the start backwards to index 1. Output from the code above:<\/p>\n<pre><code class=\"language-text\">'abcabcbb' 3 3\n'bbbbb' 1 1\n'pwwkew' 3 3\n'' 0 0\n'a' 1 1\n'abba'     2 2<\/code><\/pre>\n<p>Each row is the input followed by the results of <code>brute<\/code> and <code>window<\/code>. The two also agreed on 2,000 random strings over a three-letter alphabet. Comparing a fast solution against a slow, obviously correct one is the cheapest strong test you have. This is the sliding window pattern; the <a href=\"https:\/\/algocademy.com\/link\/?lang=py&amp;problem=longest-subarray-without-reapeating&amp;solution=1\">Longest Subarray Without Repeating<\/a> lessons on AlgoCademy take the same problem from brute force to the optimised version in steps.<\/p>\n<h2>The mental moves behind the steps<\/h2>\n<ul>\n<li><strong>Pattern matching:<\/strong> &#8220;this looks like finding a pair in a sorted array&#8221; lets you reuse a known technique. This comes from having solved many problems; see <a href=\"https:\/\/algocademy.com\/blog\/how-to-identify-patterns-in-coding-challenges-a-comprehensive-guide\/\">how to identify patterns in coding challenges<\/a>.<\/li>\n<li><strong>Invariants:<\/strong> state something that stays true throughout your loop (&#8220;<code>start<\/code> is the smallest index such that <code>s[start..i]<\/code> has no repeats&#8221;). If you can state it, you can usually prove the loop is correct.<\/li>\n<li><strong>Cost awareness:<\/strong> estimate operations from the input limits. About 10<sup>8<\/sup> simple operations is a rough ceiling for a few seconds in most languages, so n = 10<sup>5<\/sup> rules out O(n\u00b2) but allows O(n log n).<\/li>\n<li><strong>Trade-offs:<\/strong> time versus memory, simplicity versus speed. The best algorithm on paper is not always the right one: for 50 items, a clear O(n\u00b2) loop is fine.<\/li>\n<\/ul>\n<h2>Mistakes that slow people down<\/h2>\n<ul>\n<li><strong>Coding before understanding.<\/strong> If you cannot solve three small examples on paper, you cannot write the code yet.<\/li>\n<li><strong>Skipping the brute force.<\/strong> It feels like wasted time but it defines correctness and shows you where the waste is.<\/li>\n<li><strong>Chasing the clever trick.<\/strong> Most problems need a standard technique applied carefully, not a flash of insight.<\/li>\n<li><strong>Never testing edge cases.<\/strong> Empty input, a single element, all equal values, and the largest allowed input.<\/li>\n<li><strong>Reading solutions too early.<\/strong> Try for a fixed time (say 30 minutes), then read, then re-implement from scratch the next day.<\/li>\n<\/ul>\n<h2>A simple practice plan<\/h2>\n<ol>\n<li>Learn one technique at a time (hash tables, two pointers, sliding window, binary search, recursion, BFS and DFS) and do 5 to 10 problems on it.<\/li>\n<li>For every problem, write the five steps in a notebook, even if briefly. The act of stating the brute force and its waste is the training.<\/li>\n<li>Re-solve problems you needed help with after a week.<\/li>\n<li>Move to mixed sets, choosing the technique yourself.<\/li>\n<li>Explain solutions out loud, as in an interview. Explaining exposes gaps that silent solving hides.<\/li>\n<\/ol>\n<p>Pseudocode is a good middle step between the idea and the code: it forces the steps to be exact without the syntax. Visualisers such as VisuAlgo can help you see what an algorithm does to the data, but only if you predict each step before you watch it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>What algorithmic thinking is, the five steps of doing it, one problem solved from brute force to an O(n) answer, and a practice plan for building the skill.<\/p>\n","protected":false},"author":1,"featured_media":1816,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[23],"tags":[],"class_list":["post-1817","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-problem-solving"],"_links":{"self":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts\/1817"}],"collection":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/comments?post=1817"}],"version-history":[{"count":1,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts\/1817\/revisions"}],"predecessor-version":[{"id":8685,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts\/1817\/revisions\/8685"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/media\/1816"}],"wp:attachment":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/media?parent=1817"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/categories?post=1817"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/tags?post=1817"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}