Lecture 03 - Searching and Sorting Algorithms (Data Structures)

Built-in ADTs in Languages

  • Modern languages provide efficient, well-tested implementations of common data structures in standard libraries.

  • As professional programmers, you rarely implement these from scratch, but understanding how they work "under the hood" helps:

    • Choose the right data structure for your needs

    • Recognize and avoid performance pitfalls

    • Build your own when built-ins fall short

  • Analogy: learning how an engine works makes you a better driver, mechanic, and problem solver, even if you don’t hand-build an engine.

Language Core Library vs ADT Examples

  • Python: Core Library includes list, set, dict

  • C++: STL (Standard Template Library) includes vector, list, stack, queue, deque, set, map

  • Java: Java Collections Framework (JCF) includes Collection, Set, List, Map, Queue, Deque

Searching and Sorting: Overview

  • Searching algorithm: a method to locate a specific item/value within a data collection or defined space.

  • Basic use cases: search for values (e.g., a number in an array, a key in a dictionary) using techniques like linear search, binary search, or hash lookups.

  • Beyond the list: searching can mean exploring a solution space (e.g., maze paths, puzzles, function optimization) where each position is a candidate solution.

  • Algorithmic goal: efficiently reduce the number of possibilities by exploiting structure (ordering, heuristics, probability) to find a valid or optimal solution within acceptable time/space bounds.

Linear Search

  • Definition: sequential scan through all elements.

  • Pseudocode (conceptual):

    • LinearSearch(values, key):

    • i = 0

    • for each value in values:

      • if value == key: return i

      • else: i = i + 1

    • return -1

  • Complexity:

    • Best case: O(1)O(1)

    • Average case: O(N)O(N)

    • Worst case: O(N)O(N)

Binary Search

  • Prerequisite: data must be sorted.

  • Principle: each step halves the search space.

  • Pseudocode (iterative):

    • BinarySearch(values, key):

    • low = 0

    • high = size(values) - 1

    • while high >= low:

      • mid = (high - low) / 2 + low

      • if values[mid] < key: low = mid + 1

      • else if values[mid] > key: high = mid - 1

      • else: return mid

    • return -1

  • Complexity:

    • Best case: O(1)O(1)

    • Average case: O(ablaN)O(abla N)

    • Worst case: O(ablaN)O(abla N)

    • Note: <br>abla<br>abla O(\, ext{log} \, N)</p></li></ul></li></ul><h3id="4e1eb666−1a32−442d−bd9f−d6b935bc033e"data−toc−id="4e1eb666−1a32−442d−bd9f−d6b935bc033e"collapsed="false"seolevelmigrated="true">BinarySearch(Recursive)</h3><ul><li><p>Recursiveversion:BinarySearch(values,key,low,high)</p><ul><li><p>ifhigh<low:return−1</p></li><li><p>mid=(high−low)/2+low</p></li><li><p>ifvalues[mid]<key:returnBinarySearch(values,key,mid+1,high)</p></li><li><p>elseifvalues[mid]>key:returnBinarySearch(values,key,low,mid−1)</p></li><li><p>else:returnmid</p></li></ul></li><li><p>Call:BinarySearch(values,key,0,size(values)−1)</p></li></ul><h3id="4f8812be−1d33−497f−a269−963323d52e7f"data−toc−id="4f8812be−1d33−497f−a269−963323d52e7f"collapsed="false"seolevelmigrated="true">OtherSearchTechniques(aslistedintheslides)</h3><ul><li><p>InterpolationSearch:heuristicthatusesthevaluedistributiontoguesswherethekeymightbe(detailsnotspelledoutinthetranscript).</p></li><li><p>JumpSearch:jumpaheadbyfixed−sizeblocksandthenlinearsearchwithintheblock(detailsnotspecifiedinthetranscript).</p></li><li><p>ExponentialSearch:expandsarangeexponentiallytolocateablockwherethekeymayreside(detailsnotspecifiedinthetranscript).</p></li><li><p>Note:Thetranscriptexplicitlylabelsthesestrategiesbutprovideslittletonocomplexitydetailsbeyondnames.</p></li></ul><h3id="a9e126c0−30aa−4637−9141−212d1168389b"data−toc−id="a9e126c0−30aa−4637−9141−212d1168389b"collapsed="false"seolevelmigrated="true">Sorting:DivideandConquerandRecursionConcepts</h3><ul><li><p>Theme:manysortingalgorithmsusedivideandconquer,oftenviarecursion,thencombinesub−solutions.</p></li><li><p>Visualmetaphorontheslideshighlightsbuildingupfromsmallerpieces(e.g.,cutting,merging,orpartitioningtreasuremaps).</p></li><li><p>Coresteps(DivideandConquer):</p><ul><li><p>Dividetheproblemintosmallersubproblemsofthesametype(ideallyroughlyequalinsize).</p></li><li><p>Conquerthepiecesrecursively,downtoasimplebasecase.</p></li><li><p>Combinethesub−solutionstoformtheoverallsolution.</p></li></ul></li><li><p>Efficiencycomesfromreducingproblemsizeateachdividestep.</p></li></ul><h3id="e68c87ba−5d8f−4804−968c−acd36eaf76f3"data−toc−id="e68c87ba−5d8f−4804−968c−acd36eaf76f3"collapsed="false"seolevelmigrated="true">InsertionSort</h3><ul><li><p>Buildsasortedlistoneitematatime.</p></li><li><p>Itistheonlyquadraticruntimesortshownasapractical,non−educationalexampleinproductioncontexts(perslide).</p></li><li><p>Pseudocodeconcept:</p><ul><li><p>fori=1toarray.size−1:</p></li><li><p>j=i</p></li><li><p>whilej>0andarray[j]<array[j−1]:</p><ul><li><p>swaparray[j]witharray[j−1]</p></li><li><p>j=j−1</p></li></ul></li></ul></li><li><p>Complexity:</p><ul><li><p>Bestcase:</p></li></ul></li></ul><h3 id="4e1eb666-1a32-442d-bd9f-d6b935bc033e" data-toc-id="4e1eb666-1a32-442d-bd9f-d6b935bc033e" collapsed="false" seolevelmigrated="true">Binary Search (Recursive)</h3><ul><li><p>Recursive version: BinarySearch(values, key, low, high)</p><ul><li><p>if high < low: return -1</p></li><li><p>mid = (high - low) / 2 + low</p></li><li><p>if values[mid] < key: return BinarySearch(values, key, mid + 1, high)</p></li><li><p>else if values[mid] > key: return BinarySearch(values, key, low, mid - 1)</p></li><li><p>else: return mid</p></li></ul></li><li><p>Call: BinarySearch(values, key, 0, size(values) - 1)</p></li></ul><h3 id="4f8812be-1d33-497f-a269-963323d52e7f" data-toc-id="4f8812be-1d33-497f-a269-963323d52e7f" collapsed="false" seolevelmigrated="true">Other Search Techniques (as listed in the slides)</h3><ul><li><p>Interpolation Search: heuristic that uses the value distribution to guess where the key might be (details not spelled out in the transcript).</p></li><li><p>Jump Search: jump ahead by fixed-size blocks and then linear search within the block (details not specified in the transcript).</p></li><li><p>Exponential Search: expands a range exponentially to locate a block where the key may reside (details not specified in the transcript).</p></li><li><p>Note: The transcript explicitly labels these strategies but provides little to no complexity details beyond names.</p></li></ul><h3 id="a9e126c0-30aa-4637-9141-212d1168389b" data-toc-id="a9e126c0-30aa-4637-9141-212d1168389b" collapsed="false" seolevelmigrated="true">Sorting: Divide and Conquer and Recursion Concepts</h3><ul><li><p>Theme: many sorting algorithms use divide and conquer, often via recursion, then combine sub-solutions.</p></li><li><p>Visual metaphor on the slides highlights building up from smaller pieces (e.g., cutting, merging, or partitioning treasure maps).</p></li><li><p>Core steps (Divide and Conquer):</p><ul><li><p>Divide the problem into smaller subproblems of the same type (ideally roughly equal in size).</p></li><li><p>Conquer the pieces recursively, down to a simple base case.</p></li><li><p>Combine the sub-solutions to form the overall solution.</p></li></ul></li><li><p>Efficiency comes from reducing problem size at each divide step.</p></li></ul><h3 id="e68c87ba-5d8f-4804-968c-acd36eaf76f3" data-toc-id="e68c87ba-5d8f-4804-968c-acd36eaf76f3" collapsed="false" seolevelmigrated="true">Insertion Sort</h3><ul><li><p>Builds a sorted list one item at a time.</p></li><li><p>It is the only quadratic runtime sort shown as a practical, non-educational example in production contexts (per slide).</p></li><li><p>Pseudocode concept:</p><ul><li><p>for i = 1 to array.size - 1:</p></li><li><p>j = i</p></li><li><p>while j > 0 and array[j] < array[j - 1]:</p><ul><li><p>swap array[j] with array[j - 1]</p></li><li><p>j = j - 1</p></li></ul></li></ul></li><li><p>Complexity:</p><ul><li><p>Best case:O(N)</p></li><li><p>Averagecase:</p></li><li><p>Average case:O(N^2)</p></li><li><p>Worstcase:</p></li><li><p>Worst case:O(N^2)</p></li></ul></li></ul><h3id="885e1b3f−c8b1−4264−9992−2eca4bff5aab"data−toc−id="885e1b3f−c8b1−4264−9992−2eca4bff5aab"collapsed="false"seolevelmigrated="true">MergeSort</h3><ul><li><p><strong>Concept:</strong>dividethearray,sorthalves,andmerge.</p></li><li><p><strong>Recurrence:</strong></p></li></ul></li></ul><h3 id="885e1b3f-c8b1-4264-9992-2eca4bff5aab" data-toc-id="885e1b3f-c8b1-4264-9992-2eca4bff5aab" collapsed="false" seolevelmigrated="true">Merge Sort</h3><ul><li><p><strong>Concept:</strong> divide the array, sort halves, and merge.</p></li><li><p><strong>Recurrence: </strong>T(n) = 2T(n/2) + O(n)</p></li><li><p><strong>Timecomplexity:</strong></p></li><li><p><strong>Time complexity:</strong>O(n \, log\, n)inallcases</p></li><li><p><strong>Keyoperation:</strong>Mergetwosortedhalvesinlineartime.</p></li><li><p><strong>Mergeprocedure</strong>(conceptual):</p><ul><li><p>Merge(left,right):mergetwosortedsubarraysintoasinglesortedarray.</p></li><li><p>Examplestructure:whileleftPos<=leftEndandrightPos<=rightEnd:compareandcopythesmallerelement;thencopyanyremainingelementsfromeitherside.</p></li><li><p>Finallycopymergedelementsbackintotheoriginalarray.</p></li></ul></li><li><p><strong>MasterTheoremapplication</strong>(MergeSort):a=2,b=2,f(n)=Θ(n)⇒Case2⇒T(n)=Θ(nlogn).</p></li><li><p><strong>Recursiontreevisualization</strong>:costperleveldoublessubproblemswhiletotalworkperlevelremainsΘ(n),yieldingΘ(nlogn)total.</p></li></ul><h3id="5605d907−4c45−4746−a6aa−dc42a9a0575d"data−toc−id="5605d907−4c45−4746−a6aa−dc42a9a0575d"collapsed="false"seolevelmigrated="true">Quicksort</h3><ul><li><p>Coreidea:chooseapivot,partitionthearraysothatvalueslessthanthepivotgoleftandvaluesgreatergoright.</p></li><li><p>Recursivelysorttheleftandrightpartitions.</p></li><li><p>Noseparatemergestep;orderingemergesfrompartitioning.</p></li><li><p>Pseudocode(partition):</p><ul><li><p>Partition(array,low,high):</p></li><li><p>mid=low+(high−low)/2</p></li><li><p>pivot=array[mid]</p></li><li><p>done=false</p></li><li><p>whilenotdone:</p><ul><li><p>whilearray[low]<pivot:low=low+1</p></li><li><p>whilepivot<array[high]:high=high−1</p></li><li><p>iflow>=high:done=true</p></li><li><p>else:swaparray[low]witharray[high]</p></li><li><p>low=low+1</p></li><li><p>high=high−1</p></li></ul></li><li><p>returnhigh//lastindexoflowpartition</p></li></ul></li><li><p>Quicksortfunction:</p><ul><li><p>Quicksort(array,low,high):</p></li><li><p>iflow>=high:return</p></li><li><p>endLow=Partition(array,low,high)</p></li><li><p>Quicksort(array,low,endLow)</p></li><li><p>Quicksort(array,endLow+1,high)</p></li></ul></li><li><p>Complexity(average/bestvsworst):</p><ul><li><p>Best/Averagecase:in all cases</p></li><li><p><strong>Key operation:</strong> Merge two sorted halves in linear time.</p></li><li><p><strong>Merge procedure</strong> (conceptual):</p><ul><li><p>Merge(left, right): merge two sorted subarrays into a single sorted array.</p></li><li><p>Example structure: while leftPos <= leftEnd and rightPos <= rightEnd: compare and copy the smaller element; then copy any remaining elements from either side.</p></li><li><p>Finally copy merged elements back into the original array.</p></li></ul></li><li><p><strong>Master Theorem application</strong> (Merge Sort): a = 2, b = 2, f(n) = Θ(n) ⇒ Case 2 ⇒ T(n) = Θ(n log n).</p></li><li><p><strong>Recursion tree visualization</strong>: cost per level doubles subproblems while total work per level remains Θ(n), yielding Θ(n log n) total.</p></li></ul><h3 id="5605d907-4c45-4746-a6aa-dc42a9a0575d" data-toc-id="5605d907-4c45-4746-a6aa-dc42a9a0575d" collapsed="false" seolevelmigrated="true">Quicksort</h3><ul><li><p>Core idea: choose a pivot, partition the array so that values less than the pivot go left and values greater go right.</p></li><li><p>Recursively sort the left and right partitions.</p></li><li><p>No separate merge step; ordering emerges from partitioning.</p></li><li><p>Pseudocode (partition):</p><ul><li><p>Partition(array, low, high):</p></li><li><p>mid = low + (high - low) / 2</p></li><li><p>pivot = array[mid]</p></li><li><p>done = false</p></li><li><p>while not done:</p><ul><li><p>while array[low] < pivot: low = low + 1</p></li><li><p>while pivot < array[high]: high = high - 1</p></li><li><p>if low >= high: done = true</p></li><li><p>else: swap array[low] with array[high]</p></li><li><p>low = low + 1</p></li><li><p>high = high - 1</p></li></ul></li><li><p>return high // last index of low partition</p></li></ul></li><li><p>Quicksort function:</p><ul><li><p>Quicksort(array, low, high):</p></li><li><p>if low >= high: return</p></li><li><p>endLow = Partition(array, low, high)</p></li><li><p>Quicksort(array, low, endLow)</p></li><li><p>Quicksort(array, endLow + 1, high)</p></li></ul></li><li><p>Complexity (average/best vs worst):</p><ul><li><p>Best/Average case:O(n \, log n)</p></li><li><p>Worstcase:</p></li><li><p>Worst case:O(n^2)</p></li></ul></li><li><p>Recurrenceapproximation(viaMasterTheorem):best/average </p></li></ul></li><li><p>Recurrence approximation (via Master Theorem): best/average ~T(n) = O(n \, log n),worst , worst ~T(n) = O(n^2)</p></li></ul><h3id="ca75f20a−856c−4c50−8864−74e61303ee76"data−toc−id="ca75f20a−856c−4c50−8864−74e61303ee76"collapsed="false"seolevelmigrated="true">MergeSortvsQuicksort:Comparison(high−level)</h3><ul><li><p>Algorithmtype:botharedivide−and−conquer.</p></li><li><p>Worst−casetime:MergeSort</p></li></ul><h3 id="ca75f20a-856c-4c50-8864-74e61303ee76" data-toc-id="ca75f20a-856c-4c50-8864-74e61303ee76" collapsed="false" seolevelmigrated="true">Merge Sort vs Quicksort: Comparison (high-level)</h3><ul><li><p>Algorithm type: both are divide-and-conquer.</p></li><li><p>Worst-case time: Merge SortO(n \, log n);Quicksort; QuicksortO(n^2)(worstcase).</p></li><li><p>Average−casetime:bothapprox(worst case).</p></li><li><p>Average-case time: both approxO(n \, log n)(Quicksortistypicallynearthisinpractice).</p></li><li><p>Best−casetime:both(Quicksort is typically near this in practice).</p></li><li><p>Best-case time: bothO(n \, log n)(fortypicalwell−behavedinputs).</p></li><li><p>Spacecomplexity:MergeSortuses(for typical well-behaved inputs).</p></li><li><p>Space complexity: Merge Sort usesO(n)auxiliaryspace;Quicksortusesauxiliary space; Quicksort usesO(\,log n)stackspaceonaverage.</p></li><li><p>Stability:MergeSortisstable;Quicksortisnotstableunlessmodified.</p></li><li><p>In−place:MergeSortisnotin−placebydefault;Quicksortisin−place(intypicalimplementations).</p></li><li><p>Usecases:MergeSortforlargedatasetsandlinkedlists;Quicksortforin−memoryarraysandgeneral−purposesortinginlibraries.</p></li><li><p>Libraryusageandimplementationconsiderations:oftenusedinparallel/distributedcontexts(Merge)versusin−memory,general−purposesorting(Quicksort).</p></li></ul><h3id="57c8a428−4825−483d−90dd−380a43825c06"data−toc−id="57c8a428−4825−483d−90dd−380a43825c06"collapsed="false"seolevelmigrated="true">RadixSort</h3><ul><li><p>RadixSortprocessesdigitsfromleastsignificanttomostsignificant(LSDfirst).</p><ul><li><p><em>(LSDtheleastsignificantdigitisthenumberfarthesttotheright)</em></p></li><li><p><em>(MSDthemostsignificantdigitisthenumberfarthesttotheleft)</em></p></li></ul></li><li><p>Stablesub−sortingperdigitplace(e.g.,countingsort)isusedtopreservetheorderofdigitsalreadysorted.</p></li><li><p>Doesnotrelyoncomparisonsbetweenkeys;reliesondigitbucketing.</p></li><li><p>Bestforuniform,fixed−lengthkeys(e.g.,largelistsofintegersorstringswithconsistentkeylength).</p></li><li><p>Keycomponents:</p><ul><li><p>RadixGetLength(value):returnsnumberofdigitsinvalue(atleast1for0).</p></li><li><p>RadixGetMaxLength(array):maximumnumberofdigitsamongallelements.</p></li><li><p>RadixSort(array):uses10buckets(forbase−10)anditeratesoverdigitpositionsfromleasttomostsignificant,rebuildingthearrayaftereachpass.Alsohandlesnegativenumbersbyseparatingnegativesandnonnegatives,reversingnegatives,andconcatenating.</p></li></ul></li><li><p>High−levelpseudocode(outline):</p><ul><li><p>Buckets=array[0..9]ofemptylists</p></li><li><p>maxDigits=RadixGetMaxLength(array)</p></li><li><p>pow10=1</p></li><li><p>fordigitIndex=0tomaxDigits−1:</p></li><li><p>fori=0toarray.size−1:</p><ul><li><p>bucketIndex=abs(array[i]/pow10)stack space on average.</p></li><li><p>Stability: Merge Sort is stable; Quicksort is not stable unless modified.</p></li><li><p>In-place: Merge Sort is not in-place by default; Quicksort is in-place (in typical implementations).</p></li><li><p>Use cases: Merge Sort for large datasets and linked lists; Quicksort for in-memory arrays and general-purpose sorting in libraries.</p></li><li><p>Library usage and implementation considerations: often used in parallel/distributed contexts (Merge) versus in-memory, general-purpose sorting (Quicksort).</p></li></ul><h3 id="57c8a428-4825-483d-90dd-380a43825c06" data-toc-id="57c8a428-4825-483d-90dd-380a43825c06" collapsed="false" seolevelmigrated="true">Radix Sort</h3><ul><li><p>Radix Sort processes digits from least significant to most significant (LSD first).</p><ul><li><p><em>(LSD the least significant digit is the number farthest to the right)</em></p></li><li><p><em>(MSD the most significant digit is the number farthest to the left)</em></p></li></ul></li><li><p>Stable sub-sorting per digit place (e.g., counting sort) is used to preserve the order of digits already sorted.</p></li><li><p>Does not rely on comparisons between keys; relies on digit bucketing.</p></li><li><p>Best for uniform, fixed-length keys (e.g., large lists of integers or strings with consistent key length).</p></li><li><p>Key components:</p><ul><li><p>RadixGetLength(value): returns number of digits in value (at least 1 for 0).</p></li><li><p>RadixGetMaxLength(array): maximum number of digits among all elements.</p></li><li><p>RadixSort(array): uses 10 buckets (for base-10) and iterates over digit positions from least to most significant, rebuilding the array after each pass. Also handles negative numbers by separating negatives and nonnegatives, reversing negatives, and concatenating.</p></li></ul></li><li><p>High-level pseudocode (outline):</p><ul><li><p>Buckets = array[0..9] of empty lists</p></li><li><p>maxDigits = RadixGetMaxLength(array)</p></li><li><p>pow10 = 1</p></li><li><p>for digitIndex = 0 to maxDigits - 1:</p></li><li><p>for i = 0 to array.size - 1:</p><ul><li><p>bucketIndex = abs(array[i] / pow10) % 10</p></li><li><p>Append array[i] to buckets[bucketIndex]</p></li></ul></li><li><p>arrayIndex = 0</p></li><li><p>for i = 0 to 9:</p><ul><li><p>for j = 0 to buckets[i].size - 1:</p></li><li><p>array[arrayIndex] = buckets[i][j]</p></li><li><p>arrayIndex = arrayIndex + 1</p></li></ul></li><li><p>pow10 = pow10 * 10</p></li><li><p>clear all buckets</p></li><li><p>Handle negatives: negatives = all negative values; nonnegatives = all nonnegative values; reverse negatives; concatenate negatives + nonnegatives.</p></li></ul></li><li><p>Runtime intuition:</p><ul><li><p>Step 1: number of passes = d, where d is the number of digits in the largest number.</p></li><li><p>Step 2: cost per pass ~ O(n) (scan elements, place into buckets, then flatten buckets).</p></li><li><p>Step 3: total cost ~ O(d × n).</p></li><li><p>For base 10, d = O(log10(k)) where k is the largest value; hence overall complexity ~O(n \log_{10}(k)).</p></li></ul></li><li><p>Practicaltakeaway:RadixSortcanoutperformcomparison−basedsortsonlargedatasetswithfixed−lengthkeysbutrequiresextraspaceandcarefulhandlingofnegatives.</p></li></ul><h3id="5328dd2f−a016−4cc5−a349−ac7a71704d37"data−toc−id="5328dd2f−a016−4cc5−a349−ac7a71704d37"collapsed="false"seolevelmigrated="true">SortingAlgorithmComparisonChart(summarypoints)</h3><ul><li><p>BubbleSort:Best.</p></li></ul></li><li><p>Practical takeaway: Radix Sort can outperform comparison-based sorts on large datasets with fixed-length keys but requires extra space and careful handling of negatives.</p></li></ul><h3 id="5328dd2f-a016-4cc5-a349-ac7a71704d37" data-toc-id="5328dd2f-a016-4cc5-a349-ac7a71704d37" collapsed="false" seolevelmigrated="true">Sorting Algorithm Comparison Chart (summary points)</h3><ul><li><p>Bubble Sort: BestO(n);Average; AverageO(n^2);Worst; WorstO(n^2);Stable:Yes;In−place:Yes.Notes:educational;notusedinpractice.</p></li><li><p>SelectionSort:Best; Stable: Yes; In-place: Yes. Notes: educational; not used in practice.</p></li><li><p>Selection Sort: BestO(n^2);Average; AverageO(n^2);Worst; WorstO(n^2);Stable:No;In−place:Yes.Notes:alwaysdoesn2comparisons.</p></li><li><p>InsertionSort:Best; Stable: No; In-place: Yes. Notes: always does n^2 comparisons.</p></li><li><p>Insertion Sort: BestO(n);Average; AverageO(n^2);Worst; WorstO(n^2);Stable:Yes;In−place:Yes.Notes:greatfornearly−sorteddata;conceptuallyabridgetoMergeSort.</p></li><li><p>ShellSort:Best; Stable: Yes; In-place: Yes. Notes: great for nearly-sorted data; conceptually a bridge to Merge Sort.</p></li><li><p>Shell Sort: BestO(n \, ext{log} n)^* roughly~ roughlyO(n^{1.3});Average; AverageO(n^2);Stable:No;In−place:Yes.Notes:fasterthaninsertionbutnotascommonlyused;notstable.</p></li><li><p>MergeSort:Best/Average/Worst; Stable: No; In-place: Yes. Notes: faster than insertion but not as commonly used; not stable.</p></li><li><p>Merge Sort: Best/Average/WorstO(n \, log n);Stable:Yes;In−place:No.Notes:recursive;requires; Stable: Yes; In-place: No. Notes: recursive; requiresO(n)space;excellentperformance.</p></li><li><p>QuickSort:Best/Averagespace; excellent performance.</p></li><li><p>Quick Sort: Best/AverageO(n \, log n);Worst; WorstO(n^2);Stable:No;In−place:Yes.Notes:veryfastinpractice;pivotstrategyiskey;notstablebydefault.</p></li><li><p>HeapSort:Best/Average/Worst; Stable: No; In-place: Yes. Notes: very fast in practice; pivot strategy is key; not stable by default.</p></li><li><p>Heap Sort: Best/Average/WorstO(n \, log n);Stable:No;In−place:Yes.Notes:basedonheap;goodperformancebutnotstable.</p></li></ul><h3id="ca0d0737−14a4−45d2−b9de−3e9331551db6"data−toc−id="ca0d0737−14a4−45d2−b9de−3e9331551db6"collapsed="false"seolevelmigrated="true">NotesonMasterTheoremandRecurrenceRelations</h3><ul><li><p>Recurrenceformfordivide−and−conqueralgorithms:</p><ul><li><p>T(n)=a⋅T(n/b)+f(n)</p></li><li><p>a:numberofsubproblems</p></li><li><p>b:factorbywhichtheproblemsizeisreduced</p></li><li><p>f(n):timetocombinethesub−solutions(theworkoutsiderecursivecalls)</p></li></ul></li><li><p>MasterTheoremapplicability:</p><ul><li><p>UsedtosolverecurrencesoftheformT(n)=aT(n/b)+f(n).</p></li><li><p>Computenlogbaandcomparewithf(n)todeterminethecase.</p></li></ul></li><li><p>Thethreeclassicalcases(informal):</p><ul><li><p>Case1:f(n)=O(nlogba−ε)forsomeε>0</p></li><li><p>ThenT(n)=Θ(nlogba)</p></li><li><p>Case2:f(n)=Θ(nlogbalogkn)forsomek≥0</p></li><li><p>ThenT(n)=Θ(nlogbalogk+1n)</p></li><li><p>Case3:f(n)=Ω(nlogba+ε)forsomeε>0,andregularityconditionholds(oftenwrittenasaf(n/b)≤cf(n)forsomec<1andsufficientlylargen)</p></li><li><p>ThenT(n)=Θ(f(n))</p></li></ul></li><li><p>HowtoapplyMasterTheorem(workflow):</p><ul><li><p>Identifya,b,f(n)intherecurrence.</p></li><li><p>Computenlogba.</p></li><li><p>Comparef(n)withnlogba(andanylogfactors)toselectthecorrectcase.</p></li><li><p>ConcludeT(n)fromthecase.</p></li></ul></li><li><p>Classicexamples:</p><ul><li><p>MergeSort:T(n)=2T(n/2)+Θ(n)→a=2,b=2,f(n)=Θ(n)→Case2→T(n)=Θ(nlogn).</p></li><li><p>BinarySearch:T(n)=T(n/2)+Θ(1)→a=1,b=2,f(n)=Θ(1)→Case2→T(n)=Θ(logn).</p></li></ul></li></ul><h3id="d9b1fe6a−652c−4bf6−b7ec−d9f4ac71301e"data−toc−id="d9b1fe6a−652c−4bf6−b7ec−d9f4ac71301e"collapsed="false"seolevelmigrated="true">RecurrenceExamplesandMasterTheoremFlow</h3><ul><li><p>MasterTheoremFlowchart(conceptual):</p><ul><li><p>Iff(n)=O(nlog<em>ba−ε)forsomeε>0→Case1→T(n)=Θ(nlog</em>ba)</p></li><li><p>Elseiff(n)=Θ(nlog<em>balogkn)→Case2→T(n)=Θ(nlog</em>balogk+1n)</p></li><li><p>Elseiff(n)=Ω(nlogba+ε)andregularityconditionholds→Case3→T(n)=Θ(f(n))</p></li><li><p>ElseMasterTheoremdoesnotapply</p></li></ul></li></ul><h3id="313e3b22−8c38−46be−98c2−2a16054d47ee"data−toc−id="313e3b22−8c38−46be−98c2−2a16054d47ee"collapsed="false"seolevelmigrated="true">MasterTheorem:WorkedCaseExamples(conceptual)</h3><ul><li><p>Case1exampleoutline:T(n)=2T(n/2)+Θ(1)</p><ul><li><p>a=2,b=2,f(n)=Θ(1)→nlog<em>ba=nlog</em>22=n1=n</p></li><li><p>Sincef(n)=O(n1−ε)withε=1,Case1applies?(Note:thestandardinterpretationyieldsT(n)=Θ(n),buttheslideframingiscontext−dependent;thekeytakeawayisusingthecomparisonwithnlogba.)</p></li></ul></li><li><p>Case2exampleoutline:T(n)=4T(n/2)+Θ(n2)</p><ul><li><p>a=4,b=2,f(n)=Θ(n2)→nlog<em>ba=nlog</em>24=n2</p></li><li><p>f(n)=Θ(nlog<em>ba)→Case2withk=0→T(n)=Θ(nlog</em>balogn)=Θ(n2logn)(asperthegeneralform).(Theslideusesavariant;theessentialpointisthatequalitywithalogfactorescalatestoCase2intheMasterTheoremframework.)</p></li></ul></li><li><p>Case3exampleoutline:T(n)=2T(n/2)+Θ(n2)</p><ul><li><p>a=2,b=2,f(n)=Θ(n2)→nlogba=n</p></li><li><p>Sincef(n)growsfasterthannlogba,Case3appliesgiventheregularitycondition,yieldingT(n)=Θ(f(n))=Θ(n2).</p></li></ul></li><li><p>Finalnotes:TheMasterTheoremprovidesastructuredwaytodeduceasymptoticgrowthfordivide−and−conquerrecurrences,withMergeSortandBinarySearchascanonicalexamples.</p></li></ul><h3id="08bcfd81−aae9−473c−bdd0−a695d2ff37f8"data−toc−id="08bcfd81−aae9−473c−bdd0−a695d2ff37f8"collapsed="false"seolevelmigrated="true">PracticalTakeaways</h3><ul><li><p>Whenchoosingasortingalgorithm,consider:</p><ul><li><p>Inputsizeandcharacteristics(random,nearlysorted,linkedlists,in−memoryarrays).</p></li><li><p>Stabilityrequirements.</p></li><li><p>Memoryconstraints(in−placevsextraspace).</p></li><li><p>Worst−casevsaverage−caseperformance.</p></li></ul></li><li><p>RadixSortoffersanon−comparison−basedalternativewithpredictablelinearpassesincertaincontexts,particularlywithfixed−lengthkeys,butrequiresextraspaceandhandlingofsigns.Overallcomplexityscaleswiththenumberofdigitsofthelargestkey:; Stable: No; In-place: Yes. Notes: based on heap; good performance but not stable.</p></li></ul><h3 id="ca0d0737-14a4-45d2-b9de-3e9331551db6" data-toc-id="ca0d0737-14a4-45d2-b9de-3e9331551db6" collapsed="false" seolevelmigrated="true">Notes on Master Theorem and Recurrence Relations</h3><ul><li><p>Recurrence form for divide-and-conquer algorithms:</p><ul><li><p>T(n) = a · T(n/b) + f(n)</p></li><li><p>a: number of subproblems</p></li><li><p>b: factor by which the problem size is reduced</p></li><li><p>f(n): time to combine the sub-solutions (the work outside recursive calls)</p></li></ul></li><li><p>Master Theorem applicability:</p><ul><li><p>Used to solve recurrences of the form T(n) = a T(n/b) + f(n).</p></li><li><p>Compute n^{log_b a} and compare with f(n) to determine the case.</p></li></ul></li><li><p>The three classical cases (informal):</p><ul><li><p>Case 1: f(n) = O(n^{log_b a - ε}) for some ε > 0</p></li><li><p>Then T(n) = Θ(n^{log_b a})</p></li><li><p>Case 2: f(n) = Θ(n^{log_b a} log^k n) for some k ≥ 0</p></li><li><p>Then T(n) = Θ(n^{log_b a} log^{k+1} n)</p></li><li><p>Case 3: f(n) = Ω(n^{log_b a + ε}) for some ε > 0, and regularity condition holds (often written as a f(n/b) ≤ c f(n) for some c < 1 and sufficiently large n)</p></li><li><p>Then T(n) = Θ(f(n))</p></li></ul></li><li><p>How to apply Master Theorem (workflow):</p><ul><li><p>Identify a, b, f(n) in the recurrence.</p></li><li><p>Compute n^{log_b a}.</p></li><li><p>Compare f(n) with n^{log_b a} (and any log factors) to select the correct case.</p></li><li><p>Conclude T(n) from the case.</p></li></ul></li><li><p>Classic examples:</p><ul><li><p>Merge Sort: T(n) = 2 T(n/2) + Θ(n) → a = 2, b = 2, f(n) = Θ(n) → Case 2 → T(n) = Θ(n log n).</p></li><li><p>Binary Search: T(n) = T(n/2) + Θ(1) → a = 1, b = 2, f(n) = Θ(1) → Case 2 → T(n) = Θ(log n).</p></li></ul></li></ul><h3 id="d9b1fe6a-652c-4bf6-b7ec-d9f4ac71301e" data-toc-id="d9b1fe6a-652c-4bf6-b7ec-d9f4ac71301e" collapsed="false" seolevelmigrated="true">Recurrence Examples and Master Theorem Flow</h3><ul><li><p>Master Theorem Flowchart (conceptual):</p><ul><li><p>If f(n) = O(n^{log<em>b a - ε}) for some ε > 0 → Case 1 → T(n) = Θ(n^{log</em>b a})</p></li><li><p>Else if f(n) = Θ(n^{log<em>b a} log^k n) → Case 2 → T(n) = Θ(n^{log</em>b a} log^{k+1} n)</p></li><li><p>Else if f(n) = Ω(n^{log_b a + ε}) and regularity condition holds → Case 3 → T(n) = Θ(f(n))</p></li><li><p>Else Master Theorem does not apply</p></li></ul></li></ul><h3 id="313e3b22-8c38-46be-98c2-2a16054d47ee" data-toc-id="313e3b22-8c38-46be-98c2-2a16054d47ee" collapsed="false" seolevelmigrated="true">Master Theorem: Worked Case Examples (conceptual)</h3><ul><li><p>Case 1 example outline: T(n) = 2 T(n/2) + Θ(1)</p><ul><li><p>a = 2, b = 2, f(n) = Θ(1) → n^{log<em>b a} = n^{log</em>2 2} = n^1 = n</p></li><li><p>Since f(n) = O(n^{1 - ε}) with ε = 1, Case 1 applies? (Note: the standard interpretation yields T(n) = Θ(n), but the slide framing is context-dependent; the key takeaway is using the comparison with n^{log_b a}.)</p></li></ul></li><li><p>Case 2 example outline: T(n) = 4 T(n/2) + Θ(n^2)</p><ul><li><p>a = 4, b = 2, f(n) = Θ(n^2) → n^{log<em>b a} = n^{log</em>2 4} = n^{2}</p></li><li><p>f(n) = Θ(n^{log<em>b a}) → Case 2 with k = 0 → T(n) = Θ(n^{log</em>b a} log n) = Θ(n^2 log n) (as per the general form). (The slide uses a variant; the essential point is that equality with a log factor escalates to Case 2 in the Master Theorem framework.)</p></li></ul></li><li><p>Case 3 example outline: T(n) = 2 T(n/2) + Θ(n^2)</p><ul><li><p>a = 2, b = 2, f(n) = Θ(n^2) → n^{log_b a} = n</p></li><li><p>Since f(n) grows faster than n^{log_b a}, Case 3 applies given the regularity condition, yielding T(n) = Θ(f(n)) = Θ(n^2).</p></li></ul></li><li><p>Final notes: The Master Theorem provides a structured way to deduce asymptotic growth for divide-and-conquer recurrences, with Merge Sort and Binary Search as canonical examples.</p></li></ul><h3 id="08bcfd81-aae9-473c-bdd0-a695d2ff37f8" data-toc-id="08bcfd81-aae9-473c-bdd0-a695d2ff37f8" collapsed="false" seolevelmigrated="true">Practical Takeaways</h3><ul><li><p>When choosing a sorting algorithm, consider:</p><ul><li><p>Input size and characteristics (random, nearly sorted, linked lists, in-memory arrays).</p></li><li><p>Stability requirements.</p></li><li><p>Memory constraints (in-place vs extra space).</p></li><li><p>Worst-case vs average-case performance.</p></li></ul></li><li><p>Radix Sort offers a non-comparison-based alternative with predictable linear passes in certain contexts, particularly with fixed-length keys, but requires extra space and handling of signs. Overall complexity scales with the number of digits of the largest key:O(n \, d) = O(n \, ext{log}_{b}(k))forbasefor basebandlargestkeyand largest keyk.</p></li></ul><h3id="d68b5da7−e0e0−43fa−9496−aea8e3c6e566"data−toc−id="d68b5da7−e0e0−43fa−9496−aea8e3c6e566"collapsed="false"seolevelmigrated="true">QuickReference:KeyNotation</h3><ul><li><p>Timecomplexities:.</p></li></ul><h3 id="d68b5da7-e0e0-43fa-9496-aea8e3c6e566" data-toc-id="d68b5da7-e0e0-43fa-9496-aea8e3c6e566" collapsed="false" seolevelmigrated="true">Quick Reference: Key Notation</h3><ul><li><p>Time complexities:O(\,),,
      abla $$ (log base 2) denotation in context, and variations by base as needed.

    • Recurrences: T(n) = a T(n/b) + f(n)

    • Master Theorem cases summarized above.