# Table of Contents - [Fall 2025 Textbook | CS61B Textbook Fall 2025](#fall-2025-textbook-cs61b-textbook-fall-2025) - [Contributors | CS61B Textbook](#contributors-cs61b-textbook) - [DISCLAIMER | CS61B Textbook](#disclaimer-cs61b-textbook) - [1. Introduction | CS61B Textbook](#1-introduction-cs61b-textbook) - [5. DLLists | CS61B Textbook](#5-dllists-cs61b-textbook) - [8. ArrayList | CS61B Textbook](#8-arraylist-cs61b-textbook) - [6. Arrays | CS61B Textbook](#6-arrays-cs61b-textbook) - [4. SLLists | CS61B Textbook](#4-sllists-cs61b-textbook) - [10. Inheritance II: Extends, Casting, Higher Order Functions | CS61B Textbook](#10-inheritance-ii-extends-casting-higher-order-functions-cs61b-textbook) - [2. Defining and Using Classes | CS61B Textbook](#2-defining-and-using-classes-cs61b-textbook) - [3. References, Recursion, and Lists | CS61B Textbook](#3-references-recursion-and-lists-cs61b-textbook) - [9. Inheritance I: Interface and Implementation Inheritance | CS61B Textbook](#9-inheritance-i-interface-and-implementation-inheritance-cs61b-textbook) - [12. Inheritance IV: Iterators, Object Methods | CS61B Textbook](#12-inheritance-iv-iterators-object-methods-cs61b-textbook) - [11. Inheritance III: Subtype Polymorphism, Comparators, Comparable | CS61B Textbook](#11-inheritance-iii-subtype-polymorphism-comparators-comparable-cs61b-textbook) - [1.4 Exercises | CS61B Textbook](#1-4-exercises-cs61b-textbook) - [7. Testing | CS61B Textbook](#7-testing-cs61b-textbook) - [1.2 Java Workflow | CS61B Textbook](#1-2-java-workflow-cs61b-textbook) - [14. Disjoint Sets | CS61B Textbook](#14-disjoint-sets-cs61b-textbook) - [13. Asymptotics I | CS61B Textbook](#13-asymptotics-i-cs61b-textbook) - [1.1 Your First Java Program | CS61B Textbook](#1-1-your-first-java-program-cs61b-textbook) - [15. Asymptotics II | CS61B Textbook](#15-asymptotics-ii-cs61b-textbook) - [16. ADTs and BSTs | CS61B Textbook](#16-adts-and-bsts-cs61b-textbook) - [1.3 Basic Java Features | CS61B Textbook](#1-3-basic-java-features-cs61b-textbook) - [17. B-Trees | CS61B Textbook](#17-b-trees-cs61b-textbook) - [18. Red Black Trees | CS61B Textbook](#18-red-black-trees-cs61b-textbook) - [10.3 Casting | CS61B Textbook](#10-3-casting-cs61b-textbook) - [11.1 A Review of Dynamic Method Selection | CS61B Textbook](#11-1-a-review-of-dynamic-method-selection-cs61b-textbook) - [19.3 "Valid" & "Good" Hashcodes | CS61B Textbook](#19-3-valid-good-hashcodes-cs61b-textbook) - [11.6 Exercises | CS61B Textbook](#11-6-exercises-cs61b-textbook) - [19. Hashing I | CS61B Textbook](#19-hashing-i-cs61b-textbook) - [21. Heaps and Priority Queues | CS61B Textbook](#21-heaps-and-priority-queues-cs61b-textbook) - [11.2 Subtype Polymorphism vs Explicit Higher Order Functions | CS61B Textbook](#11-2-subtype-polymorphism-vs-explicit-higher-order-functions-cs61b-textbook) - [12.2 Exceptions | CS61B Textbook](#12-2-exceptions-cs61b-textbook) - [11.5 Chapter Summary | CS61B Textbook](#11-5-chapter-summary-cs61b-textbook) - [13.9 Summary | CS61B Textbook](#13-9-summary-cs61b-textbook) - [12.5 Chapter Summary | CS61B Textbook](#12-5-chapter-summary-cs61b-textbook) - [20. Hashing II | CS61B Textbook](#20-hashing-ii-cs61b-textbook) - [28. Reductions and Decomposition | CS61B Textbook](#28-reductions-and-decomposition-cs61b-textbook) - [30. Quicksort | CS61B Textbook](#30-quicksort-cs61b-textbook) - [29.6 Exercises | CS61B Textbook](#29-6-exercises-cs61b-textbook) - [31. Software Engineering II | CS61B Textbook](#31-software-engineering-ii-cs61b-textbook) - [32. More Quick Sort, Sorting Summary | CS61B Textbook](#32-more-quick-sort-sorting-summary-cs61b-textbook) - [33. Software Engineering III | CS61B Textbook](#33-software-engineering-iii-cs61b-textbook) - [DISCLAIMER | CS61B Textbook Fall 2025](#disclaimer-cs61b-textbook-fall-2025) - [Contributors | CS61B Textbook Fall 2025](#contributors-cs61b-textbook-fall-2025) - [31.3 Modular Design | CS61B Textbook](#31-3-modular-design-cs61b-textbook) - [37. Software Engineering IV | CS61B Textbook](#37-software-engineering-iv-cs61b-textbook) - [32.3 Stability, Adaptiveness, and Optimization | CS61B Textbook](#32-3-stability-adaptiveness-and-optimization-cs61b-textbook) - [34. Sorting and Algorithmic Bounds | CS61B Textbook](#34-sorting-and-algorithmic-bounds-cs61b-textbook) - [1. Introduction | CS61B Textbook Fall 2025](#1-introduction-cs61b-textbook-fall-2025) - [11. There is no chapter 11. | CS61B Textbook Fall 2025](#11-there-is-no-chapter-11-cs61b-textbook-fall-2025) - [33.5 Exercises | CS61B Textbook](#33-5-exercises-cs61b-textbook) - [33.4 Summary | CS61B Textbook](#33-4-summary-cs61b-textbook) - [37.1 The end is near | CS61B Textbook](#37-1-the-end-is-near-cs61b-textbook) - [36. Sorting and Data Structures Conclusion | CS61B Textbook](#36-sorting-and-data-structures-conclusion-cs61b-textbook) - [35.2 LSD Radix Sort | CS61B Textbook](#35-2-lsd-radix-sort-cs61b-textbook) - [36.5 Exercises | CS61B Textbook](#36-5-exercises-cs61b-textbook) - [35.4 Summary | CS61B Textbook](#35-4-summary-cs61b-textbook) - [8. ArrayList | CS61B Textbook Fall 2025](#8-arraylist-cs61b-textbook-fall-2025) - [5. DLLists | CS61B Textbook Fall 2025](#5-dllists-cs61b-textbook-fall-2025) - [35.3 MSD Radix Sort | CS61B Textbook](#35-3-msd-radix-sort-cs61b-textbook) - [39.5 Exercises | CS61B Textbook](#39-5-exercises-cs61b-textbook) - [10. Inheritance II: Subtype Polymorphism, Comparators, Comparables, Generic Functions | CS61B Textbook Fall 2025](#10-inheritance-ii-subtype-polymorphism-comparators-comparables-generic-functions-cs61b-textbook-fall-2025) - [35.5 Exercises | CS61B Textbook](#35-5-exercises-cs61b-textbook) - [39.1 Models of Compression | CS61B Textbook](#39-1-models-of-compression-cs61b-textbook) - [12. Inheritance III: Iterators, Object Methods | CS61B Textbook Fall 2025](#12-inheritance-iii-iterators-object-methods-cs61b-textbook-fall-2025) - [35.1 Counting Sort | CS61B Textbook](#35-1-counting-sort-cs61b-textbook) - [35. Radix Sorts | CS61B Textbook](#35-radix-sorts-cs61b-textbook) - [39.2 Optimal Compression, Kolmogorov Complexity | CS61B Textbook](#39-2-optimal-compression-kolmogorov-complexity-cs61b-textbook) - [39.4 P = NP | CS61B Textbook](#39-4-p-np-cs61b-textbook) - [36.3 Radix Sorting Integers | CS61B Textbook](#36-3-radix-sorting-integers-cs61b-textbook) - [6. Arrays | CS61B Textbook Fall 2025](#6-arrays-cs61b-textbook-fall-2025) - [9. Inheritance I: Interface and Implementation Inheritance | CS61B Textbook Fall 2025](#9-inheritance-i-interface-and-implementation-inheritance-cs61b-textbook-fall-2025) - [17. Asymptotics III | CS61B Textbook Fall 2025](#17-asymptotics-iii-cs61b-textbook-fall-2025) - [38.8 Exercises | CS61B Textbook](#38-8-exercises-cs61b-textbook) - [36.2 The Just-In-Time Compiler | CS61B Textbook](#36-2-the-just-in-time-compiler-cs61b-textbook) - [38.6 LZW Compression | CS61B Textbook](#38-6-lzw-compression-cs61b-textbook) - [36.4 Summary | CS61B Textbook](#36-4-summary-cs61b-textbook) - [14. Disjoint Sets | CS61B Textbook Fall 2025](#14-disjoint-sets-cs61b-textbook-fall-2025) - [38. Compression and Complexity | CS61B Textbook](#38-compression-and-complexity-cs61b-textbook) - [38.7 Summary | CS61B Textbook](#38-7-summary-cs61b-textbook) - [2. Defining and Using Classes | CS61B Textbook Fall 2025](#2-defining-and-using-classes-cs61b-textbook-fall-2025) - [15. Asymptotics II | CS61B Textbook Fall 2025](#15-asymptotics-ii-cs61b-textbook-fall-2025) - [38.5 Compression Theory | CS61B Textbook](#38-5-compression-theory-cs61b-textbook) - [38.2 Prefix-free Codes | CS61B Textbook](#38-2-prefix-free-codes-cs61b-textbook) - [13. Asymptotics I | CS61B Textbook Fall 2025](#13-asymptotics-i-cs61b-textbook-fall-2025) - [1.2 Java Workflow | CS61B Textbook Fall 2025](#1-2-java-workflow-cs61b-textbook-fall-2025) - [16. ADTs and BSTs | CS61B Textbook Fall 2025](#16-adts-and-bsts-cs61b-textbook-fall-2025) - [9.5 Implementation vs. Interface Inheritance | CS61B Textbook Fall 2025](#9-5-implementation-vs-interface-inheritance-cs61b-textbook-fall-2025) - [21. Hashing II | CS61B Textbook Fall 2025](#21-hashing-ii-cs61b-textbook-fall-2025) - [18. B-Trees | CS61B Textbook Fall 2025](#18-b-trees-cs61b-textbook-fall-2025) - [19. Red Black Trees | CS61B Textbook Fall 2025](#19-red-black-trees-cs61b-textbook-fall-2025) - [38.3 Shannon-Fano Codes | CS61B Textbook](#38-3-shannon-fano-codes-cs61b-textbook) - [24. Graph Traversals and Implementations | CS61B Textbook Fall 2025](#24-graph-traversals-and-implementations-cs61b-textbook-fall-2025) - [38.1 Introduction to Compression | CS61B Textbook](#38-1-introduction-to-compression-cs61b-textbook) - [23. Tree Traversals and Graphs | CS61B Textbook Fall 2025](#23-tree-traversals-and-graphs-cs61b-textbook-fall-2025) - [9.4 Implementation Inheritance, default | CS61B Textbook Fall 2025](#9-4-implementation-inheritance-default-cs61b-textbook-fall-2025) - [10.4 Summary | CS61B Textbook Fall 2025](#10-4-summary-cs61b-textbook-fall-2025) - [9.6 Abstract Data Types | CS61B Textbook Fall 2025](#9-6-abstract-data-types-cs61b-textbook-fall-2025) - [16.5 Summary | CS61B Textbook Fall 2025](#16-5-summary-cs61b-textbook-fall-2025) - [12.2 Exceptions | CS61B Textbook Fall 2025](#12-2-exceptions-cs61b-textbook-fall-2025) - [38.4 Huffman Coding Conceptuals | CS61B Textbook](#38-4-huffman-coding-conceptuals-cs61b-textbook) - [10.1 Polymorphism vs. Function Passing | CS61B Textbook Fall 2025](#10-1-polymorphism-vs-function-passing-cs61b-textbook-fall-2025) - [17.4 B-trees Big O | CS61B Textbook Fall 2025](#17-4-b-trees-big-o-cs61b-textbook-fall-2025) - [9.2 Hypernyms, Hyponyms, and the Implements Keyword | CS61B Textbook Fall 2025](#9-2-hypernyms-hyponyms-and-the-implements-keyword-cs61b-textbook-fall-2025) - [9.3 Overriding, Interface Inheritance | CS61B Textbook Fall 2025](#9-3-overriding-interface-inheritance-cs61b-textbook-fall-2025) - [22. Heaps and Priority Queues | CS61B Textbook Fall 2025](#22-heaps-and-priority-queues-cs61b-textbook-fall-2025) - [29. Reductions and Decomposition | CS61B Textbook Fall 2025](#29-reductions-and-decomposition-cs61b-textbook-fall-2025) - [22.4 Summary | CS61B Textbook Fall 2025](#22-4-summary-cs61b-textbook-fall-2025) - [24.3 Summary | CS61B Textbook Fall 2025](#24-3-summary-cs61b-textbook-fall-2025) - [9.1 The Problem of Generality | CS61B Textbook Fall 2025](#9-1-the-problem-of-generality-cs61b-textbook-fall-2025) - [12.5 Chapter Summary | CS61B Textbook Fall 2025](#12-5-chapter-summary-cs61b-textbook-fall-2025) - [25.1 Introduction | CS61B Textbook Fall 2025](#25-1-introduction-cs61b-textbook-fall-2025) - [1.1 Your First Java Program | CS61B Textbook Fall 2025](#1-1-your-first-java-program-cs61b-textbook-fall-2025) - [16.4 BSTs as Sets and Maps | CS61B Textbook Fall 2025](#16-4-bsts-as-sets-and-maps-cs61b-textbook-fall-2025) - [15.5 Summary | CS61B Textbook Fall 2025](#15-5-summary-cs61b-textbook-fall-2025) - [21.1 Hash Table Recap, Default Hash Function | CS61B Textbook Fall 2025](#21-1-hash-table-recap-default-hash-function-cs61b-textbook-fall-2025) - [21.4 Mutable vs. Immutable Types | CS61B Textbook Fall 2025](#21-4-mutable-vs-immutable-types-cs61b-textbook-fall-2025) - [12.1 Lists and Sets in Java | CS61B Textbook Fall 2025](#12-1-lists-and-sets-in-java-cs61b-textbook-fall-2025) - [13.6 Summary | CS61B Textbook Fall 2025](#13-6-summary-cs61b-textbook-fall-2025) - [14.5 Weighted Quick Union with Path Compression | CS61B Textbook Fall 2025](#14-5-weighted-quick-union-with-path-compression-cs61b-textbook-fall-2025) - [36.1 Radix vs. Comparison Sorting | CS61B Textbook](#36-1-radix-vs-comparison-sorting-cs61b-textbook) - [18.4 B-Tree Invariants | CS61B Textbook Fall 2025](#18-4-b-tree-invariants-cs61b-textbook-fall-2025) - [16.2 BST Definitions | CS61B Textbook Fall 2025](#16-2-bst-definitions-cs61b-textbook-fall-2025) - [16.1 Binary Search Trees | CS61B Textbook Fall 2025](#16-1-binary-search-trees-cs61b-textbook-fall-2025) - [23.1 Tree Recap | CS61B Textbook Fall 2025](#23-1-tree-recap-cs61b-textbook-fall-2025) - [3. References, Recursion, and Lists | CS61B Textbook Fall 2025](#3-references-recursion-and-lists-cs61b-textbook-fall-2025) - [20. Hashing I | CS61B Textbook Fall 2025](#20-hashing-i-cs61b-textbook-fall-2025) - [13.1 An Introduction to Asymptotic Analysis | CS61B Textbook Fall 2025](#13-1-an-introduction-to-asymptotic-analysis-cs61b-textbook-fall-2025) - [14.4 Weighted Quick Union (WQU) | CS61B Textbook Fall 2025](#14-4-weighted-quick-union-wqu-cs61b-textbook-fall-2025) - [27. Prefix Operations and Tries | CS61B Textbook Fall 2025](#27-prefix-operations-and-tries-cs61b-textbook-fall-2025) - [25.5 Exercises | CS61B Textbook Fall 2025](#25-5-exercises-cs61b-textbook-fall-2025) - [4. SLLists | CS61B Textbook Fall 2025](#4-sllists-cs61b-textbook-fall-2025) - [30.5 Summary | CS61B Textbook Fall 2025](#30-5-summary-cs61b-textbook-fall-2025) - [19.2 Creating LLRB Trees | CS61B Textbook Fall 2025](#19-2-creating-llrb-trees-cs61b-textbook-fall-2025) - [33. More Quick Sort, Sorting Summary | CS61B Textbook Fall 2025](#33-more-quick-sort-sorting-summary-cs61b-textbook-fall-2025) - [24.1 BFS & DFS | CS61B Textbook Fall 2025](#24-1-bfs-dfs-cs61b-textbook-fall-2025) - [10.3 Writing a Max Function | CS61B Textbook Fall 2025](#10-3-writing-a-max-function-cs61b-textbook-fall-2025) - [20.3 "Valid" & "Good" Hashcodes | CS61B Textbook Fall 2025](#20-3-valid-good-hashcodes-cs61b-textbook-fall-2025) - [15.2 Big O | CS61B Textbook Fall 2025](#15-2-big-o-cs61b-textbook-fall-2025) - [25. Shortest Paths | CS61B Textbook Fall 2025](#25-shortest-paths-cs61b-textbook-fall-2025) - [28.1 Introduction to Software Engineering | CS61B Textbook Fall 2025](#28-1-introduction-to-software-engineering-cs61b-textbook-fall-2025) - [22.5 Exercises | CS61B Textbook Fall 2025](#22-5-exercises-cs61b-textbook-fall-2025) - [28.5 Summary, Exercises | CS61B Textbook Fall 2025](#28-5-summary-exercises-cs61b-textbook-fall-2025) - [16.6 Exercises | CS61B Textbook Fall 2025](#16-6-exercises-cs61b-textbook-fall-2025) - [27.4 Summary | CS61B Textbook Fall 2025](#27-4-summary-cs61b-textbook-fall-2025) - [31. Quicksort | CS61B Textbook Fall 2025](#31-quicksort-cs61b-textbook-fall-2025) - [25.3 A* Algorithm | CS61B Textbook Fall 2025](#25-3-a-algorithm-cs61b-textbook-fall-2025) - [22.3 PQ Implementation | CS61B Textbook Fall 2025](#22-3-pq-implementation-cs61b-textbook-fall-2025) - [31.2 Quicksort Algorithm | CS61B Textbook Fall 2025](#31-2-quicksort-algorithm-cs61b-textbook-fall-2025) - [23.4 Graph Problems | CS61B Textbook Fall 2025](#23-4-graph-problems-cs61b-textbook-fall-2025) - [24.4 Exercises | CS61B Textbook Fall 2025](#24-4-exercises-cs61b-textbook-fall-2025) - [18.7 Exercises | CS61B Textbook Fall 2025](#18-7-exercises-cs61b-textbook-fall-2025) - [18.2 Big O vs. Worst Case | CS61B Textbook Fall 2025](#18-2-big-o-vs-worst-case-cs61b-textbook-fall-2025) - [32. Software Engineering II | CS61B Textbook Fall 2025](#32-software-engineering-ii-cs61b-textbook-fall-2025) - [16.3 BST Operations | CS61B Textbook Fall 2025](#16-3-bst-operations-cs61b-textbook-fall-2025) - [19.5 Summary | CS61B Textbook Fall 2025](#19-5-summary-cs61b-textbook-fall-2025) - [26. Minimum Spanning Trees | CS61B Textbook Fall 2025](#26-minimum-spanning-trees-cs61b-textbook-fall-2025) - [25.4 Summary | CS61B Textbook Fall 2025](#25-4-summary-cs61b-textbook-fall-2025) - [23.3 Graphs | CS61B Textbook Fall 2025](#23-3-graphs-cs61b-textbook-fall-2025) - [38.1 The end is near | CS61B Textbook Fall 2025](#38-1-the-end-is-near-cs61b-textbook-fall-2025) - [12.6 Exercises | CS61B Textbook Fall 2025](#12-6-exercises-cs61b-textbook-fall-2025) - [38. Software Engineering IV | CS61B Textbook Fall 2025](#38-software-engineering-iv-cs61b-textbook-fall-2025) - [20.2 Hash Code | CS61B Textbook Fall 2025](#20-2-hash-code-cs61b-textbook-fall-2025) - [20.5 Resizing & Hash Table Performance | CS61B Textbook Fall 2025](#20-5-resizing-hash-table-performance-cs61b-textbook-fall-2025) - [24.2 Representing Graphs | CS61B Textbook Fall 2025](#24-2-representing-graphs-cs61b-textbook-fall-2025) - [39. Compression, Complexity, P = NP | CS61B Textbook](#39-compression-complexity-p-np-cs61b-textbook) - [14.1 Introduction | CS61B Textbook Fall 2025](#14-1-introduction-cs61b-textbook-fall-2025) - [15.1 Big Theta | CS61B Textbook Fall 2025](#15-1-big-theta-cs61b-textbook-fall-2025) - [14.2 Quick Find | CS61B Textbook Fall 2025](#14-2-quick-find-cs61b-textbook-fall-2025) - [1.3 Basic Java Features | CS61B Textbook Fall 2025](#1-3-basic-java-features-cs61b-textbook-fall-2025) - [39.3 Space/Time-Bounded Compression | CS61B Textbook](#39-3-space-time-bounded-compression-cs61b-textbook) - [33.3 Stability, Adaptiveness, and Optimization | CS61B Textbook Fall 2025](#33-3-stability-adaptiveness-and-optimization-cs61b-textbook-fall-2025) - [20.1.1 A first attempt: DataIndexedIntegerSet | CS61B Textbook Fall 2025](#20-1-1-a-first-attempt-dataindexedintegerset-cs61b-textbook-fall-2025) - [20.1.3 A third attempt: DataIndexedStringSet | CS61B Textbook Fall 2025](#20-1-3-a-third-attempt-dataindexedstringset-cs61b-textbook-fall-2025) - [20.6 Summary | CS61B Textbook Fall 2025](#20-6-summary-cs61b-textbook-fall-2025) - [13.5 Simplified Analysis Process | CS61B Textbook Fall 2025](#13-5-simplified-analysis-process-cs61b-textbook-fall-2025) - [13.2 Runtime Characterization | CS61B Textbook Fall 2025](#13-2-runtime-characterization-cs61b-textbook-fall-2025) - [19.4 Runtime Analysis | CS61B Textbook Fall 2025](#19-4-runtime-analysis-cs61b-textbook-fall-2025) - [19.6 Exercises | CS61B Textbook Fall 2025](#19-6-exercises-cs61b-textbook-fall-2025) - [14.3 Quick Union | CS61B Textbook Fall 2025](#14-3-quick-union-cs61b-textbook-fall-2025) - [19.1 Rotating Trees | CS61B Textbook Fall 2025](#19-1-rotating-trees-cs61b-textbook-fall-2025) - [26.4 Chapter Summary | CS61B Textbook Fall 2025](#26-4-chapter-summary-cs61b-textbook-fall-2025) - [36.1 Counting Sort | CS61B Textbook Fall 2025](#36-1-counting-sort-cs61b-textbook-fall-2025) - [34.4 Summary | CS61B Textbook Fall 2025](#34-4-summary-cs61b-textbook-fall-2025) - [28. Software Engineering I | CS61B Textbook Fall 2025](#28-software-engineering-i-cs61b-textbook-fall-2025) - [26.2 Prim's Algorithm | CS61B Textbook Fall 2025](#26-2-prim-s-algorithm-cs61b-textbook-fall-2025) - [28.3 Strategic vs Tactical Programming | CS61B Textbook Fall 2025](#28-3-strategic-vs-tactical-programming-cs61b-textbook-fall-2025) - [20.1 Introduction to Hashing: Data Indexed Arrays | CS61B Textbook Fall 2025](#20-1-introduction-to-hashing-data-indexed-arrays-cs61b-textbook-fall-2025) - [13.7 Exercises | CS61B Textbook Fall 2025](#13-7-exercises-cs61b-textbook-fall-2025) - [27.5 Exercises | CS61B Textbook Fall 2025](#27-5-exercises-cs61b-textbook-fall-2025) - [39. Compression and Complexity | CS61B Textbook Fall 2025](#39-compression-and-complexity-cs61b-textbook-fall-2025) - [18.5 B-Tree Performance | CS61B Textbook Fall 2025](#18-5-b-tree-performance-cs61b-textbook-fall-2025) - [12.3 Iteration | CS61B Textbook Fall 2025](#12-3-iteration-cs61b-textbook-fall-2025) - [36. Radix Sorts | CS61B Textbook Fall 2025](#36-radix-sorts-cs61b-textbook-fall-2025) - [27.3 Trie String Operations | CS61B Textbook Fall 2025](#27-3-trie-string-operations-cs61b-textbook-fall-2025) - [28.4 Real World Examples | CS61B Textbook Fall 2025](#28-4-real-world-examples-cs61b-textbook-fall-2025) - [30.2 Selection Sort & Heapsort | CS61B Textbook Fall 2025](#30-2-selection-sort-heapsort-cs61b-textbook-fall-2025) - [7. Testing | CS61B Textbook Fall 2025](#7-testing-cs61b-textbook-fall-2025) - [15.3 For Loops | CS61B Textbook Fall 2025](#15-3-for-loops-cs61b-textbook-fall-2025) - [30.1 The Sorting Problem | CS61B Textbook Fall 2025](#30-1-the-sorting-problem-cs61b-textbook-fall-2025) - [40.2 Optimal Compression, Kolmogorov Complexity | CS61B Textbook Fall 2025](#40-2-optimal-compression-kolmogorov-complexity-cs61b-textbook-fall-2025) - [26.1 MSTs and Cut Property | CS61B Textbook Fall 2025](#26-1-msts-and-cut-property-cs61b-textbook-fall-2025) - [10.2 Comparables and Comparators | CS61B Textbook Fall 2025](#10-2-comparables-and-comparators-cs61b-textbook-fall-2025) - [21.2 Distribution By Other Hash Functions | CS61B Textbook Fall 2025](#21-2-distribution-by-other-hash-functions-cs61b-textbook-fall-2025) - [13.3 Checkpoint: An Exercise | CS61B Textbook Fall 2025](#13-3-checkpoint-an-exercise-cs61b-textbook-fall-2025) - [27.2 Trie Implementation | CS61B Textbook Fall 2025](#27-2-trie-implementation-cs61b-textbook-fall-2025) - [33.4 Summary | CS61B Textbook Fall 2025](#33-4-summary-cs61b-textbook-fall-2025) - [32.3 Modular Design | CS61B Textbook Fall 2025](#32-3-modular-design-cs61b-textbook-fall-2025) - [28.2 Complexity | CS61B Textbook Fall 2025](#28-2-complexity-cs61b-textbook-fall-2025) - [37.2 The Just-In-Time Compiler | CS61B Textbook Fall 2025](#37-2-the-just-in-time-compiler-cs61b-textbook-fall-2025) - [17.3 Mergesort | CS61B Textbook Fall 2025](#17-3-mergesort-cs61b-textbook-fall-2025) - [20.1.2 A second attempt: DataIndexedWordSet | CS61B Textbook Fall 2025](#20-1-2-a-second-attempt-dataindexedwordset-cs61b-textbook-fall-2025) - [18.6 Summary | CS61B Textbook Fall 2025](#18-6-summary-cs61b-textbook-fall-2025) - [29.4 Reductions and Decomposition | CS61B Textbook Fall 2025](#29-4-reductions-and-decomposition-cs61b-textbook-fall-2025) - [37.4 Summary | CS61B Textbook Fall 2025](#37-4-summary-cs61b-textbook-fall-2025) - [20.7 Exercises | CS61B Textbook Fall 2025](#20-7-exercises-cs61b-textbook-fall-2025) - [40.3 Space/Time-Bounded Compression | CS61B Textbook Fall 2025](#40-3-space-time-bounded-compression-cs61b-textbook-fall-2025) - [18.3 B-Tree Operations | CS61B Textbook Fall 2025](#18-3-b-tree-operations-cs61b-textbook-fall-2025) - [13.4 Asymptotic Behavior | CS61B Textbook Fall 2025](#13-4-asymptotic-behavior-cs61b-textbook-fall-2025) - [18.1 BST Performance | CS61B Textbook Fall 2025](#18-1-bst-performance-cs61b-textbook-fall-2025) - [25.2 Dijkstra's Algorithm | CS61B Textbook Fall 2025](#25-2-dijkstra-s-algorithm-cs61b-textbook-fall-2025) - [22.2 Heaps | CS61B Textbook Fall 2025](#22-2-heaps-cs61b-textbook-fall-2025) - [40.4 P = NP | CS61B Textbook Fall 2025](#40-4-p-np-cs61b-textbook-fall-2025) - [30. Basic Sorts | CS61B Textbook Fall 2025](#30-basic-sorts-cs61b-textbook-fall-2025) - [29.2 Shortest Paths on DAGs | CS61B Textbook Fall 2025](#29-2-shortest-paths-on-dags-cs61b-textbook-fall-2025) - [26.5 MST Exercises | CS61B Textbook Fall 2025](#26-5-mst-exercises-cs61b-textbook-fall-2025) - [36.4 Summary | CS61B Textbook Fall 2025](#36-4-summary-cs61b-textbook-fall-2025) - [17.2 Binary Search | CS61B Textbook Fall 2025](#17-2-binary-search-cs61b-textbook-fall-2025) - [30.4 Insertion Sort | CS61B Textbook Fall 2025](#30-4-insertion-sort-cs61b-textbook-fall-2025) - [37.5 Exercises | CS61B Textbook Fall 2025](#37-5-exercises-cs61b-textbook-fall-2025) --- # Fall 2025 Textbook | CS61B Textbook Fall 2025 This is the Fall 2025 edition of the CS61B textbook. This book is intended as a written accompaniment for the lectures, as opposed to being a full data structures textbook. If you are interested in a serious data structures textbook that covers the topics of this class, we recommend: * Algorithms, 4th edition by Robert Sedgwick, Kevin Wayne * Algorithms Illuminated by Tim Roughgarden * The Art of Computer Programming (Volume 3) by Donald Knuth [NextContributorschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/readme-1) Last updated 6 months ago --- # Contributors | CS61B Textbook * Professor Joshua Hug * Vanessa Teo * Angel Aldaco * Dhruti Pandya * Thomas Lee * Teresa Luo * Mihir Mirchandani * William Lee * Vidya Ganga * Ergun Acikoz * Kenneth Wang * Carl Ji * Nathalys Pham * Stella Kaval * Aniruth Narayanan * Circle Chen [NextDISCLAIMERchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/disclaimer) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # DISCLAIMER | CS61B Textbook This textbook is under construction. Sections may be missing and/or erroneous. Please contact circlecly@berkeley.edu with any questions or comments. [PreviousContributorschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook) [Next1\. Introductionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/1.-introduction) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 1. Introduction | CS61B Textbook This section covers basic features of Java and how to run programs on the command line. [PreviousDISCLAIMERchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/disclaimer) [Next1.1 Your First Java Programchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/1.-introduction/1.1-your-first-java-program) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 5. DLLists | CS61B Textbook In Chapter 2.2, we built the `SLList` class, which was better than our earlier naked recursive `IntList` data structure. In this section, we'll wrap up our discussion of linked lists, and also start learning the foundations of arrays that we'll need for an array based list we'll call an `AList`. Along the way, we'll also reveal the secret of why we used the awkward name `SLList` in the previous chapter. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/5.-dllists#addlast) addLast Consider the `addLast(int x)` method from the previous chapter. Copy public void addLast(int x) { size += 1; IntNode p = sentinel; while (p.next != null) { p = p.next; } p.next = new IntNode(x, null); } The issue with this method is that it is slow. For a long list, the `addLast` method has to walk through the entire list, much like we saw with the `size` method in chapter 2.2. Similarly, we can attempt to speed things up by adding a `last` variable, to speed up our code, as shown below: Copy public class SLList { private IntNode sentinel; private IntNode last; private int size; public void addLast(int x) { last.next = new IntNode(x, null); last = last.next; size += 1; } ... } **Exercise 2.3.1:** Consider the box and pointer diagram representing the `SLList` implementation above, which includes the last pointer. Suppose that we'd like to support `addLast`, `getLast`, and `removeLast` operations. Will the structure shown support rapid `addLast`, `getLast`, and `removeLast` operations? If not, which operations are slow? ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig23%2Fsllist_last_pointer.png&width=768&dpr=3&quality=100&sign=fd386d6e&sv=2) sllist\_last\_pointer.png **Answer 2.3.1:** `addLast` and `getLast` will be fast, but `removeLast` will be slow. That's because we have no easy way to get the second-to-last node, to update the `last` pointer, after removing the last node. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/5.-dllists#secondtolast) SecondToLast The issue with the structure from exercise 2.3.1 is that a method that removes the last item in the list will be inherently slow. This is because we need to first find the second to last item, and then set its next pointer to be null. Adding a `secondToLast` pointer will not help either, because then we'd need to find the third to last item in the list in order to make sure that `secondToLast` and `last` obey the appropriate invariants after removing the last item. **Exercise 2.3.2:** Try to devise a scheme for speeding up the `removeLast` operation so that it always runs in constant time, no matter how long the list. Don't worry about actually coding up a solution, we'll leave that to project 1. Just come up with an idea about how you'd modify the structure of the list (i.e. the instance variables). We'll describe the solution in Improvement #7. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/5.-dllists#improvement-7-looking-back) Improvement #7: Looking Back The most natural way to tackle this issue is to add a previous pointer to each `IntNode`, i.e. In other words, our list now has two links for every node. One common term for such lists is the "Doubly Linked List", which we'll call a `DLList` for short. This is in contrast to a single linked list from chapter 2.2, a.k.a. an `SLList`. The addition of these extra pointers will lead to extra code complexity. Rather than walk you through it, you'll build a doubly linked list on your own in project 1. The box and pointer diagram below shows more precisely what a doubly linked list looks like for lists of size 0 and size 2, respectively. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig23%2Fdllist_basic_size_0.png&width=768&dpr=3&quality=100&sign=e33679d8&sv=2) dllist\_basic\_size\_0.png ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig23%2Fdllist_basic_size_2.png&width=768&dpr=3&quality=100&sign=c1ba7ec2&sv=2) dllist\_basic\_size\_2.png #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/5.-dllists#improvement-8-sentinel-upgrade) Improvement #8: Sentinel Upgrade Back pointers allow a list to support adding, getting, and removing the front and back of a list in constant time. There is a subtle issue with this design where the `last` pointer sometimes points at the sentinel node, and sometimes at a real node. Just like the non-sentinel version of the `SLList`, this results in code with special cases that is much uglier than what we'll get after our 8th and final improvement. (Can you think of what `DLList` methods would have these special cases?) One fix is to add a second sentinel node to the back of the list. This results in the topology shown below as a box and pointer diagram. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig23%2Fdllist_double_sentinel_size_0.png&width=768&dpr=3&quality=100&sign=3bb5a2f8&sv=2) dllist\_double\_sentinel\_size\_0.png ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig23%2Fdllist_double_sentinel_size_2.png&width=768&dpr=3&quality=100&sign=768411e2&sv=2) dllist\_double\_sentinel\_size\_2.png An alternate approach is to implement the list so that it is circular, with the front and back pointers sharing the same sentinel node. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig23%2Fdllist_circular_sentinel_size_0.png&width=768&dpr=3&quality=100&sign=f45b5e10&sv=2) dllist\_circular\_sentinel\_size\_0.png ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig23%2Fdllist_circular_sentinel_size_2.png&width=768&dpr=3&quality=100&sign=f887c83a&sv=2) dllist\_circular\_sentinel\_size\_2.png Both the two-sentinel and circular sentinel approaches work and result in code that is free of ugly special cases, though I personally find the circular approach to be cleaner and more aesthetically beautiful. We will not discuss the details of these implementations, as you'll have a chance to explore one or both in project 1. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/5.-dllists#generic-dllists) Generic DLLists Our DLLists suffer from a major limitation: they can only hold integer values. For example, suppose we wanted to create a list of Strings: The code above would crash, since our `DLList` constructor and `addLast` methods only take an integer argument. Luckily, in 2004, the creators of Java added **generics** to the language, which will allow you to, among other things, create data structures that hold any reference type. The syntax is a little strange to grasp at first. The basic idea is that right after the name of the class in your class declaration, you use an arbitrary placeholder inside angle brackets: `<>`. Then anywhere you want to use the arbitrary type, you use that placeholder instead. For example, our `DLList` declaration before was: A generic `DLList` that can hold any type would look as below: Here, `BleepBlorp` is just a name I made up, and you could use most any other name you might care to use instead, like `GloopGlop`, `Horse`, `TelbudorphMulticulus` or whatever. Now that we've defined a generic version of the `DLList` class, we must also use a special syntax to instantiate this class. To do so, we put the desired type inside of angle brackets during declaration, and also use empty angle brackets during instantiation. For example: Since generics only work with reference types, we cannot put primitives like `int` or `double` inside of angle brackets, e.g. ``. Instead, we use the reference version of the primitive type, which in the case of `int` case is `Integer`, e.g. There are additional nuances about working with generic types, but we will defer them to a later chapter of this book, when you've had more of a chance to experiment with them on your own. For now, use the following rules of thumb: * In the .java file **implementing** a data structure, specify your generic type name only once at the very top of the file after the class name. * In other .java files, which use your data structure, specify the specific desired type during declaration, and use the empty diamond operator during instantiation. * If you need to instantiate a generic over a primitive type, use `Integer`, `Double`, `Character`, `Boolean`, `Long`, `Short`, `Byte`, or `Float` instead of their primitive equivalents. Minor detail: You may also declare the type inside of angle brackets when instantiating, though this is not necessary, so long as you are also declaring a variable on the same line. In other words, the following line of code is perfectly valid, even though the `Integer` on the right hand side is redundant. At this point, you know everything you need to know to implement the `LinkedListDeque` project on project 1, where you'll refine all of the knowledge you've gained in chapters 2.1, 2.2, and 2.3. [Previous4\. SLListschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/4.-sllists) [Next6\. Arrayschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/6.-arrays) Last updated 2 years ago sun-brightdesktopmoon Copy public class IntNode { public IntNode prev; public int item; public IntNode next; } Copy DLList d2 = new DLList("hello"); d2.addLast("world"); Copy public class DLList { private IntNode sentinel; private int size; public class IntNode { public IntNode prev; public int item; public IntNode next; ... } ... } Copy public class DLList { private IntNode sentinel; private int size; public class IntNode { public IntNode prev; public BleepBlorp item; public IntNode next; ... } ... } Copy DLList d2 = new DLList<>("hello"); d2.addLast("world"); Copy DLList d1 = new DLList<>(5); d1.insertFront(10); Copy DLList d1 = new DLList(5); sun-brightdesktopmoon --- # 8. ArrayList | CS61B Textbook In this section, we'll build a new class called `AList` that can be used to store arbitrarily long lists of data, similar to our `DLList`. Unlike the `DLList`, the `AList` will use arrays to store data instead of a linked list. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/8.-arraylist#linked-list-performance-puzzle) Linked List Performance Puzzle Suppose we wanted to write a new method for `DLList` called `int get(int i)`. Why would `get` be slow for long lists compared to `getLast`? For what inputs would it be especially slow? You may find the figure below useful for thinking about your answer. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig23%2Fdllist_circular_sentinel_size_2.png&width=768&dpr=3&quality=100&sign=f887c83a&sv=2) dllist\_circular\_sentinel\_size\_2.png #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/8.-arraylist#linked-list-performance-puzzle-solution) Linked List Performance Puzzle Solution It turns out that no matter how clever you are, the `get` method will usually be slower than `getBack` if we're using the doubly linked list structure described in section 2.3. This is because, since we only have references to the first and last items of the list, we'll always need to walk through the list from the front or back to get to the item that we're trying to retrieve. For example, if we want to get item #417 in a list of length 10,000, we'll have to walk across 417 forward links to get to the item we want. In the very worst case, the item is in the very middle and we'll need to walk through a number of items proportional to the length of the list (specifically, the number of items divided by two). In other words, our worst case execution time for `get` is linear in the size of the entire list. This in contrast to the runtime for `getBack`, which is constant, no matter the size of the list. Later in the course, we'll formally define runtimes in terms of big O and big Theta notation. For now, we'll stick to an informal understanding. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/8.-arraylist#our-first-attempt-the-naive-array-based-list) Our First Attempt: The Naive Array Based List Accessing the `i`th element of an array takes constant time on a modern computer. This suggests that an array-based list would be capable of much better performance for `get` than a linked-list based solution, since it can simply use bracket notation to get the item of interest. If you'd like to know **why** arrays have constant time access, check out this [Quora postarrow-up-right](https://www.quora.com/Why-does-accessing-an-array-element-take-constant-time) . **Optional Exercise 2.5.1:** Try to build an AList class that supports `addLast`, `getLast`, `get`, and `size` operations. Your AList should work for any size array up to 100. For starter code, see [https://github.com/Berkeley-CS61B/lectureCode/tree/master/lists4/DIYarrow-up-right](https://github.com/Berkeley-CS61B/lectureCode/tree/master/lists4/DIY) . [My solutionarrow-up-right](https://github.com/Berkeley-CS61B/lectureCode/tree/master/lists4/naive) has the following handy invariants. * The position of the next item to be inserted (using `addLast`) is always `size`. * The number of items in the AList is always `size`. * The position of the last item in the list is always `size - 1`. Other solutions might be slightly different. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/8.-arraylist#removelast) removeLast The last operation we need to support is `removeLast`. Before we start, we make the following key observation: Any change to our list must be reflected in a change in one or more memory boxes in our implementation. This might seem obvious, but there is some profundity to it. The list is an abstract idea, and the `size`, `items`, and `items[i]` memory boxes are the concrete representation of that idea. Any change the user tries to make to the list using the abstractions we provide (`addLast`, `removeLast`) must be reflected in some changes to these memory boxes in a way that matches the user's expectations. Our invariants provide us with a guide for what those changes should look like. **Optional Exercise 2.5.2:** Try to write `removeLast`. Before starting, decide which of `size`, `items`, and `items[i]` needs to change so that our invariants are preserved after the operation, i.e. so that future calls to our methods provide the user of the list class with the behavior they expect. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/8.-arraylist#naive-resizing-arrays) Naive Resizing Arrays **Optional Exercise 2.5.3:** Suppose we have an AList in the state shown in the figure below. What will happen if we call `addLast(11)`? What should we do about this problem? ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig25%2Ffull_naive_alist.png&width=768&dpr=3&quality=100&sign=8dd233ad&sv=2) dllist\_circular\_sentinel\_size\_2.png The answer, in Java, is that we simply build a new array that is big enough to accomodate the new data. For example, we can imagine adding the new item as follows: The process of creating a new array and copying items over is often referred to as "resizing". It's a bit of a misnomer since the array doesn't actually change size, we are just making a **new** one that has a bigger size. **Exercise 2.5.4:** Try to implement the `addLast(int i)` method to work with resizing arrays. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/8.-arraylist#analyzing-the-naive-resizing-array) Analyzing the Naive Resizing Array The approach that we attempted in the previous section has terrible performance. By running a simple computational experiment where we call `addLast` 100,000 times, we see that the `SLList` completes so fast that we can't even time it. By contrast our array based list takes several seconds. To understand why, consider the following exercise: **Exercise 2.5.5:** Suppose we have an array of size 100. If we call insertBack two times, how many total boxes will we need to create and fill throughout this entire process? How many total boxes will we have at any one time, assuming that garbage collection happens as soon as the last reference to an array is lost? **Exercise 2.5.6:** Starting from an array of size 100, approximately how many memory boxes get created and filled if we call `addLast` 1,000 times? Creating all those memory boxes and recopying their contents takes time. In the graph below, we plot total time vs. number of operations for an SLList on the top, and for a naive array based list on the bottom. The SLList shows a straight line, which means for each `add` operation, the list takes the same additional amount of time. This means each single operation takes constant time! You can also think of it this way: the graph is linear, indicating that each operation takes constant time, since the integral of a constant is a line. By contrast, the naive array list shows a parabola, indicating that each operation takes linear time, since the integral of a line is a parabola. This has significant real world implications. For inserting 100,000 items, we can roughly compute how much longer by computing the ratio of N^2/N. Inserting 100,000 items into our array based list takes (100,000^2)/100,000 or 100,000 times as long. This is obviously unacceptable. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig25%2Finsert_experiment.png&width=768&dpr=3&quality=100&sign=54679adb&sv=2) fig25/insert\_experiment.png #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/8.-arraylist#geometric-resizing) Geometric Resizing We can fix our performance problems by growing the size of our array by a multiplicative amount, rather than an additive amount. That is, rather than **adding** a number of memory boxes equal to some resizing factor `RFACTOR`: We instead resize by **multiplying** the number of boxes by `RFACTOR`. Repeating our computational experiment from before, we see that our new `AList` completes 100,000 inserts in so little time that we don't even notice. We'll defer a full analysis of why this happens until the final chapter of this book. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/8.-arraylist#memory-performance) Memory Performance Our `AList` is almost done, but we have one major issue. Suppose we insert 1,000,000,000 items, then later remove 990,000,000 items. In this case, we'll be using only 10,000,000 of our memory boxes, leaving 99% completely unused. To fix this issue, we can also downsize our array when it starts looking empty. Specifically, we define a "usage ratio" R which is equal to the size of the list divided by the length of the `items` array. For example, in the figure below, the usage ratio is 0.04. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig25%2Fusage_ratio.png&width=768&dpr=3&quality=100&sign=e3f3cef5&sv=2) fig25/usage\_ratio.png In a typical implementation, we halve the size of the array when R falls to less than 0.25. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/8.-arraylist#generic-alists) Generic ALists Just as we did before, we can modify our `AList` so that it can hold any data type, not just integers. To do this, we again use the special angle braces notation in our class and substitute our arbitrary type parameter for integer wherever appropriate. For example, below, we use `Glorp` as our type parameter. There is one significant syntactical difference: Java does not allow us to create an array of generic objects due to an obscure issue with the way generics are implemented. That is, we cannot do something like: Instead, we have to use the awkward syntax shown below: This will yield a compilation warning, but it's just something we'll have to live with. We'll discuss this in more details in a later chapter. The other change we make is that we null out any items that we "delete". Whereas before, we had no reason to zero out elements that were deleted, with generic objects, we do want to null out references to the objects that we're storing. This is to avoid "loitering". Recall that Java only destroys objects when the last reference has been lost. If we fail to null out the reference, then Java will not garbage collect the objects that have been added to the list. This is a subtle performance bug that you're unlikely to observe unless you're looking for it, but in certain cases could result in a significant wastage of memory. [Previous7\. Testingchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/7.-testing) [Next9\. Inheritance I: Interface and Implementation Inheritancechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/9.-inheritance-i-interface-and-implementation-inheritance) Last updated 2 years ago sun-brightdesktopmoon Copy int[] a = new int[size + 1]; System.arraycopy(items, 0, a, 0, size); a[size] = 11; items = a; size = size + 1; Copy public void insertBack(int x) { if (size == items.length) { resize(size + RFACTOR); } items[size] = x; size += 1; } Copy public void insertBack(int x) { if (size == items.length) { resize(size * RFACTOR); } items[size] = x; size += 1; } Copy Glorp[] items = new Glorp[8]; Copy Glorp[] items = (Glorp []) new Object[8]; sun-brightdesktopmoon --- # 6. Arrays | CS61B Textbook So far, we've seen how to harness recursive class definitions to create an expandable list class, including the `IntList`, `SLList`, and `DLList`. In the next two sections of this book, we'll discuss how to build a list class using arrays. This section of this book assumes you've already worked with arrays and is not intended to be a comprehensive guide to their syntax. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/6.-arrays#array-basics) Array Basics To ultimately build a list that can hold information, we need some way to get memory boxes. Prevously, we saw how we could get memory boxes with variable declarations and class instantiations. For example: * `int x;` gives us a 32 bit memory box that stores ints. * `Walrus w1;` gives us a 64 bit memory box that stores Walrus references. * `Walrus w2 = new Walrus(30, 5.6);` gets us 3 total memory boxes. One 64 bit box that stores Walrus references, one 32 bit box that stores the int size of the Walrus, and a 64 bit box that stores the double tuskSize of the Walrus. Arrays are a special type of object that consists of a numbered sequence of memory boxes. This is unlike class instances, which have named memory boxes. To get the ith item of an array, we use bracket notation as we saw in HW0 and Project 0, e.g. `A[i]` to get the `i`th element of A. Arrays consist of: * A fixed integer length, N * A sequence of N memory boxes (N = length) where all boxes are of the same type, and are numbered 0 through N - 1. Unlike classes, arrays do not have methods. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/6.-arrays#array-creation) Array Creation There are three valid notations for array creation. Try running the code below and see what happens. Click [herearrow-up-right](http://pythontutor.com/iframe-embed.html#code=public%20class%20ArrayCreationDemo%20%7B%0A%20%20public%20static%20void%20main(String%5B%5D%20args%29%20%7B%0A%20%20%20%20int%5B%5D%20x%3B%0A%20%20%20%20int%5B%5D%20y%3B%0A%20%20%20%20x%20%3D%20new%20int%5B3%5D%3B%0A%20%20%20%20y%20%3D%20new%20int%5B%5D%7B1,%202,%203,%204,%205%7D%3B%0A%20%20%20%20int%5B%5D%20z%20%3D%20%7B9,%2010,%2011,%2012,%2013%7D%3B%0A%09%7D%0A%7D&codeDivHeight=400&codeDivWidth=350&cumulative=false&curInstr=0&heapPrimitives=false&origin=opt-frontend.js&py=java&rawInputLstJSON=%5B%5D&textReferences=false) for an interactive visualization. * `x = new int[3];` * `y = new int[]{1, 2, 3, 4, 5};` * `int[] z = {9, 10, 11, 12, 13};` All three notations create an array. The first notation, used to create `x`, will create an array of the specified length and fill in each memory box with a default value. In this case, it will create an array of length 3, and fill each of the 3 boxes with the default `int` value `0`. The second notation, used to create `y`, creates an array with the exact size needed to accommodate the specified starting values. In this case, it creates an array of length 5, with those five specific elements. The third notation, used to declare **and** create `z`, has the same behavior as the second notation. The only difference is that it omits the usage of `new`, and can only be used when combined with a variable declaration. None of these notations is better than any other. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/6.-arrays#array-access-and-modification) Array Access and Modification The following code showcases all of the key syntax we'll use to work with arrays. Try stepping through the code below and making sure you understand what happens when each line executes. To do so, click [herearrow-up-right](https://goo.gl/bertuh) for an interactive visualization. With the exception of the final line of code, we've seen all of this syntax before. The final line demonstrates one way to copy information from one array to another. `System.arraycopy` takes five parameters: * The array to use as a source * Where to start in the source array * The array to use as a destination * Where to start in the destination array * How many items to copy For Python veterans, `System.arraycopy(b, 0,x, 3, 2)` is the equivalent of `x[3:5] = b[0:2]` in Python. An alternate approach to copying arrays would be to use a loop. `arraycopy` is usually faster than a loop, and results in more compact code. The only downside is that `arraycopy` is (arguably) harder to read. Note that Java arrays only perform bounds checking at runtime. That is, the following code compiles just fine, but will crash at runtime. Try running this code locally in a java file or in the [visualizerarrow-up-right](https://goo.gl/YHufJ6) . What is the name of the error that you encounter when it crashes? Does the name of the error make sense? #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/6.-arrays#id-2d-arrays-in-java) 2D Arrays in Java What one might call a 2D array in Java is actually just an array of arrays. They follow the same rules for objects that we've already learned, but let's review them to make sure we understand how they work. Syntax for arrays of arrays can be a bit confusing. Consider the code `int[][] bamboozle = new int[4][]`. This creates an array of integer arrays called `bamboozle`. Specifically, this creates exactly four memory boxes, each of which can point to an array of integers (of unspecified length). Try running the code below line-by-lines, and see if the results match your intuition. For an interactive visualization, click [herearrow-up-right](http://goo.gl/VS4cOK) . **Exercise 2.4.1:** After running the code below, what will be the values of x\[0\]\[0\] and w\[0\]\[0\]? Check your work by clicking [herearrow-up-right](http://goo.gl/fCZ9Dr) . #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/6.-arrays#arrays-vs-classes) Arrays vs. Classes Both arrays and classes can be used to organize a bunch of memory boxes. In both cases, the number of memory boxes is fixed, i.e. the length of an array cannot be changed, just as class fields cannot be added or removed. The key differences between memory boxes in arrays and classes: * Array boxes are numbered and accessed using `[]` notation, and class boxes are named and accessed using dot notation. * Array boxes must all be the same type. Class boxes can be different types. One particularly notable impact of these difference is that `[]` notation allows us to specify which index we'd like at runtime. For example, consider the code below: If we run this code, we might get something like: By contrast, specifying fields in a class is not something we do at runtime. For example, consider the code below: If we tried compiling this, we'd get a syntax error. The same problem occurs if we try to use dot notation: Compiling, we'd get: This isn't a limitation you'll face often, but it's worth pointing out, just for the sake of good scholarship. For what it's worth, there is a way to specify desired fields at runtime called _reflection_, but it is considered very bad coding style for typical programs. You can read more about reflection [herearrow-up-right](https://docs.oracle.com/javase/tutorial/reflect/member/fieldValues.html) . **You should never use reflection in any 61B program**, and we won't discuss it in our course. In general, programming languages are partially designed to limit the choices of programmers to make code simpler to reason about. By restricting these sorts of features to the special Reflections API, we make typical Java programs easier to read and interpret. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/6.-arrays#appendix-java-arrays-vs-other-languages) Appendix: Java Arrays vs. Other Languages Compared to arrays in other languages, Java arrays: * Have no special syntax for "slicing" (such as in Python). * Cannot be shrunk or expanded (such as in Ruby). * Do not have member methods (such as in Javascript). * Must contain values only of the same type (unlike Python). [Previous5\. DLListschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/5.-dllists) [Next7\. Testingchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/7.-testing) Last updated 2 years ago sun-brightdesktopmoon Copy int[] z = null; int[] x, y; x = new int[]{1, 2, 3, 4, 5}; y = x; x = new int[]{-1, 2, 5, 4, 99}; y = new int[3]; z = new int[0]; int xL = x.length; String[] s = new String[6]; s[4] = "ketchup"; s[x[3] - x[1]] = "muffins"; int[] b = {9, 10, 11}; System.arraycopy(b, 0, x, 3, 2); Copy int[] x = {9, 10, 11, 12, 13}; int[] y = new int[2]; int i = 0; while (i < x.length) { y[i] = x[i]; i += 1; } Copy int[][] pascalsTriangle; pascalsTriangle = new int[4][]; int[] rowZero = pascalsTriangle[0]; pascalsTriangle[0] = new int[]{1}; pascalsTriangle[1] = new int[]{1, 1}; pascalsTriangle[2] = new int[]{1, 2, 1}; pascalsTriangle[3] = new int[]{1, 3, 3, 1}; int[] rowTwo = pascalsTriangle[2]; rowTwo[1] = -5; int[][] matrix; matrix = new int[4][]; matrix = new int[4][4]; int[][] pascalAgain = new int[][]{{1}, {1, 1}, {1, 2, 1}, {1, 3, 3, 1}}; Copy int[][] x = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; int[][] z = new int[3][]; z[0] = x[0]; z[1] = x[1]; z[2] = x[2]; z[0][0] = -z[0][0]; int[][] w = new int[3][3]; System.arraycopy(x[0], 0, w[0], 0, 3); System.arraycopy(x[1], 0, w[1], 0, 3); System.arraycopy(x[2], 0, w[2], 0, 3); w[0][0] = -w[0][0]; Copy int indexOfInterest = askUserForInteger(); int[] x = {100, 101, 102, 103}; int k = x[indexOfInterest]; System.out.println(k); Copy $ javac arrayDemo $ java arrayDemo What index do you want? 2 102 Copy String fieldOfInterest = "mass"; Planet p = new Planet(6e24, "earth"); double mass = p[fieldOfInterest]; Copy $ javac classDemo FieldDemo.java:5: error: array required, but Planet found double mass = earth[fieldOfInterest]; ^ Copy String fieldOfInterest = "mass"; Planet p = new Planet(6e24, "earth"); double mass = p.fieldOfInterest; Copy $ javac classDemo FieldDemo.java:5: error: cannot find symbol double mass = earth.fieldOfInterest; ^ symbol: variable fieldOfInterest location: variable earth of type Planet sun-brightdesktopmoon --- # 4. SLLists | CS61B Textbook In Chapter 3, we built the `IntList` class, a list data structure that can technically do all the things a list can do. However, in practice, the `IntList` suffers from the fact that it is fairly awkward to use, resulting in code that is hard to read and maintain. Fundamentally, the issue is that the `IntList` is what I call a **naked recursive** data structure. In order to use an `IntList` correctly, the programmer must understand and utilize recursion even for simple list related tasks. This limits its usefulness to novice programmers, and potentially introduces a whole new class of tricky errors that programmers might run into, depending on what sort of helper methods are provided by the `IntList` class. Inspired by our experience with the `IntList`, we'll now build a new class `SLList`, which much more closely resembles the list implementations that programmers use in modern languages. We'll do so by iteratively adding a sequence of improvements. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/4.-sllists#improvement-1-rebranding) Improvement #1: Rebranding Our `IntList` class from last time was as follows, with helper methods omitted: Copy public class IntList { public int first; public IntList rest; public IntList(int f, IntList r) { first = f; rest = r; } ... Our first step will be to simply rename everything and throw away the helper methods. This probably doesn't seem like progress, but trust me, I'm a professional. Copy public class IntNode { public int item; public IntNode next; public IntNode(int i, IntNode n) { item = i; next = n; } } #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/4.-sllists#improvement-2-bureaucracy) Improvement #2: Bureaucracy Knowing that `IntNodes` are hard to work with, we're going to create a separate class called `SLList` that the user will interact with. The basic class is simply: Already, we can get a vague sense of why a `SLList` is better. Compare the creation of an `IntList` of one item to the creation of a `SLList` of one item. The `SLList` hides the detail that there exists a null link from the user. The `SLList` class isn't very useful yet, so let's add an `addFirst` and `getFirst` method as simple warmup methods. Consider trying to write them yourself before reading on. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/4.-sllists#addfirst-and-getfirst) addFirst and getFirst `addFirst` is relatively straightforward if you understood chapter 2.1. With `IntLists`, we added to the front with the line of code `L = new IntList(5, L)`. Thus, we end up with: `getFirst` is even easier. We simply return `first.item`: The resulting `SLList` class is much easier to use. Compare: to the `IntList` equivalent: Comparing the two data structures visually, we have: (with the `IntList` version on top and `SLList` version below it) ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig22%2FIntList_vs_SLList.png&width=768&dpr=3&quality=100&sign=7ecc82c6&sv=2) IntList\_vs\_SLList.png Essentially, the `SLList` class acts as a middleman between the list user and the naked recursive data structure. As suggested above in the `IntList` version, there is a potentially undesireable possibility for the `IntList` user to have variables that point to the middle of the `IntList`. As Ovid said: [Mortals cannot look upon a god without dyingarrow-up-right](https://en.wikipedia.org/wiki/Semele) , so perhaps it is best that the `SLList` is there to act as our intermediary. **Exercise 2.2.1**: The curious reader might object and say that the `IntList` would be just as easy to use if we simply wrote an `addFirst` method. Try to write an `addFirst` method to the `IntList` class. You'll find that the resulting method is tricky as well as inefficient. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/4.-sllists#improvement-3-public-vs-private) Improvement #3: Public vs. Private Unfortunately, our `SLList` can be bypassed and the raw power of our naked data structure (with all its dangers) can be accessed. A programmer can easily modify the list directly, without going through the kid-tested, mother-approved `addFirst` method, for example: ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig22%2Fbad_SLList.png&width=768&dpr=3&quality=100&sign=f018c1c6&sv=2) bad\_SLList.png This results in a malformed list with an infinite loop. To deal with this problem, we can modify the `SLList` class so that the `first` variable is declared with the `private` keyword. Private variables and methods can only be accessed by code inside the same `.java` file, e.g. in this case `SLList.java`. That means that a class like `SLLTroubleMaker` below will fail to compile, yielding a `first has private access in SLList` error. By contrast, any code inside the `SLList.java` file will be able to access the `first` variable. It may seem a little silly to restrict access. After all, the only thing that the `private` keyword does is break programs that otherwise compile. However, in large software engineering projects, the `private` keyword is an invaluable signal that certain pieces of code should be ignored (and thus need not be understood) by the end user. Likewise, the `public` keyword should be thought of as a declaration that a method is available and will work **forever** exactly as it does now. As an analogy, a car has certain `public` features, e.g. the accelerator and brake pedals. Under the hood, there are `private` details about how these operate. In a gas powered car, the accelerator pedal might control some sort of fuel injection system, and in a battery powered car, it may adjust the amount of battery power being delivered to the motor. While the private details may vary from car to car, we expect the same behavior from all accelerator pedals. Changing these would cause great consternation from users, and quite possibly terrible accidents. **When you create a** `**public**` **member (i.e. method or variable), be careful, because you're effectively committing to supporting that member's behavior exactly as it is now, forever.** #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/4.-sllists#improvement-4-nested-classes) Improvement #4: Nested Classes At the moment, we have two `.java` files: `IntNode` and `SLList`. However, the `IntNode` is really just a supporting character in the story of `SLList`. Java provides us with the ability to embed a class declaration inside of another for just this situation. The syntax is straightforward and intuitive: Having a nested class has no meaningful effect on code performance, and is simply a tool for keeping code organized. For more on nested classes, see [Oracle's official documentationarrow-up-right](https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html) . If the nested class has no need to use any of the instance methods or variables of `SLList`, you may declare the nested class `static`, as follows. Declaring a nested class as `static` means that methods inside the static class can not access any of the members of the enclosing class. In this case, it means that no method in `IntNode` would be able to access `first`, `addFirst`, or `getFirst`. This saves a bit of memory, because each `IntNode` no longer needs to keep track of how to access its enclosing `SLList`. Put another way, if you examine the code above, you'll see that the `IntNode` class never uses the `first` variable of `SLList`, nor any of `SLList`'s methods. As a result, we can use the static keyword, which means the `IntNode` class doesn't get a reference to its boss, saving us a small amount of memory. If this seems a bit technical and hard to follow, try Exercise 2.2.2. A simple rule of thumb is that _if you don't use any instance members of the outer class, make the nested class static_. **Exercise 2.2.2** Delete the word `static` as few times as possible so that [this programarrow-up-right](https://joshhug.gitbooks.io/hug61b/content/chap2/exercises/Government.java) compiles (Refresh the page after clicking the link and making sure the url changed). Make sure to read the comments at the top before doing the exercise. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/4.-sllists#addlast-and-size) addLast() and size() To motivate our remaining improvements and also demonstrate some common patterns in data structure implementation, we'll add `addLast(int x)` and `size()` methods. You're encouraged to take the [starter codearrow-up-right](https://github.com/Berkeley-CS61B/lectureCode/blob/master/lists2/DIY/addLastAndSize/SLList.java) and try it yourself before reading on. I especially encourage you to try to write a recursive implementation of `size`, which will yield an interesting challenge. I'll implement the `addLast` method iteratively, though you could also do it recursively. The idea is fairly straightforward, we create a pointer variable `p` and have it iterate through the list to the end. By contrast, I'll implement `size` recursively. This method will be somewhat similar to the `size` method we implemented in section [2.1arrow-up-right](https://joshhug.gitbooks.io/hug61b/content/chap2/chap21.html) for `IntList`. The recursive call for `size` in `IntList` was straightforward: `return 1 + this.rest.size()`. For a `SLList`, this approach does not make sense. A `SLList` has no `rest` variable. Instead, we'll use a common pattern that is used with middleman classes like `SLList` -- we'll create a private helper method that interacts with the underlying naked recursive data structure. This yields a method like the following: Using this method, we can easily compute the size of the entire list: Here, we have two methods, both named `size`. This is allowed in Java, since they have different parameters. We say that two methods with the same name but different signatures are **overloaded**. For more on overloaded methods, see Java's [official documentationarrow-up-right](https://docs.oracle.com/javase/tutorial/java/javaOO/methods.html) . An alternate approach is to create a non-static helper method in the `IntNode` class itself. Either approach is fine, though I personally prefer not having any methods in the `IntNode` class. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/4.-sllists#improvement-5-caching) Improvement #5: Caching ------------------------------------------------------------------------------------------------------------------ Consider the `size` method we wrote above. Suppose `size` takes 2 seconds on a list of size 1,000. We expect that on a list of size 1,000,000, the `size` method will take 2,000 seconds, since the computer has to step through 1,000 times as many items in the list to reach the end. Having a `size` method that is very slow for large lists is unacceptable, since we can do better. It is possible to rewrite `size` so that it takes the same amount of time, no matter how large the list. To do so, we can simply add a `size` variable to the `SLList` class that tracks the current size, yielding the code below. This practice of saving important data to speed up retrieval is sometimes known as **caching**. This modification makes our `size` method incredibly fast, no matter how large the list. Of course, it will also slow down our `addFirst` and `addLast` methods, and also increase the memory of usage of our class, but only by a trivial amount. In this case, the tradeoff is clearly in favor of creating a cache for size. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/4.-sllists#improvement-6-the-empty-list) Improvement #6: The Empty List Our `SLList` has a number of benefits over the simple `IntList` from chapter 2.1: * Users of a `SLList` never see the `IntList` class. * Simpler to use. * More efficient `addFirst` method (exercise 2.2.1). * Avoids errors or malfeasance by `IntList` users. * Faster `size` method than possible with `IntList`. Another natural advantage is that we will be able to easily implement a constructor that creates an empty list. The most natural way is to set `first` to `null` if the list is empty. This yields the constructor below: Unfortunately, this causes our `addLast` method to crash if we insert into an empty list. Since `first` is `null`, the attempt to access `p.next` in `while (p.next != null)` below causes a null pointer exception. **Exercise 2.2.3** Fix the `addLast` method. Starter code [herearrow-up-right](https://github.com/Berkeley-CS61B/lectureCode/blob/master/lists2/DIY/fixAddLast/SLList.java) . #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/4.-sllists#improvement-6b-sentinel-nodes) Improvement #6b: Sentinel Nodes One solution to fix `addLast` is to create a special case for the empty list, as shown below: This solution works, but special case code like that shown above should be avoided when necessary. Human beings only have so much working memory, and thus we want to keep complexity under control wherever possible. For a simple data structure like the `SLList`, the number of special cases is small. More complicated data structures like trees can get much, much uglier. A cleaner, though less obvious solution, is to make it so that all `SLLists` are the "same", even if they are empty. We can do this by creating a special node that is always there, which we will call a **sentinel node**. The sentinel node will hold a value, which we won't care about. For example, the empty list created by `SLList L = new SLList()` would be as shown below: ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig22%2Fempty_sentinelized_SLList.png&width=768&dpr=3&quality=100&sign=a15f445&sv=2) empty\_sentinelized\_SLList.png And a `SLList` with the items 5, 10, and 15 would look like: ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig22%2Fthree_item_sentenlized_SLList.png&width=768&dpr=3&quality=100&sign=86d264b5&sv=2) three\_item\_sentenlized\_SLList.png In the figures above, the lavender ?? value indicates that we don't care what value is there. Since Java does not allow us to fill in an integer with question marks, we just pick some abitrary value like -518273 or 63 or anything else. Since a `SLList` without a sentinel has no special cases, we can simply delete the special case from our `addLast` method, yielding: As you can see, this code is much much cleaner! #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/4.-sllists#invariants) Invariants An invariant is a fact about a data structure that is guaranteed to be true (assuming there are no bugs in your code). A `SLList` with a sentinel node has at least the following invariants: * The `sentinel` reference always points to a sentinel node. * The front item (if it exists), is always at `sentinel.next.item`. * The `size` variable is always the total number of items that have been added. Invariants make it easier to reason about code, and also give you specific goals to strive for in making sure your code works. A true understanding of how convenient sentinels are will require you to really dig in and do some implementation of your own. You'll get plenty of practice in Project 1. However, we recommend that you wait until after you've finished the next section of this book before beginning Project 1. [Previous3\. References, Recursion, and Listschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/3.-references-recursion-and-lists) [Next5\. DLListschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/5.-dllists) Last updated 2 years ago sun-brightdesktopmoon Copy public class SLList { public IntNode first; public SLList(int x) { first = new IntNode(x, null); } } Copy IntList L1 = new IntList(5, null); SLList L2 = new SLList(5); Copy public class SLList { public IntNode first; public SLList(int x) { first = new IntNode(x, null); } /** Adds an item to the front of the list. */ public void addFirst(int x) { first = new IntNode(x, first); } } Copy /** Retrieves the front item from the list. */ public int getFirst() { return first.item; } Copy SLList L = new SLList(15); L.addFirst(10); L.addFirst(5); int x = L.getFirst(); Copy IntList L = new IntList(15, null); L = new IntList(10, L); L = new IntList(5, L); int x = L.first; Copy SLList L = new SLList(15); L.addFirst(10); L.first.next.next = L.first.next; Copy public class SLList { private IntNode first; ... Copy public class SLLTroubleMaker { public static void main(String[] args) { SLList L = new SLList(15); L.addFirst(10); L.first.next.next = L.first.next; } } Copy public class SLList { public class IntNode { public int item; public IntNode next; public IntNode(int i, IntNode n) { item = i; next = n; } } private IntNode first; public SLList(int x) { first = new IntNode(x, null); } ... Copy public class SLList { public static class IntNode { public int item; public IntNode next; public IntNode(int i, IntNode n) { item = i; next = n; } } private IntNode first; ... Copy /** Adds an item to the end of the list. */ public void addLast(int x) { IntNode p = first; /* Advance p to the end of the list. */ while (p.next != null) { p = p.next; } p.next = new IntNode(x, null); } Copy /** Returns the size of the list starting at IntNode p. */ private static int size(IntNode p) { if (p.next == null) { return 1; } return 1 + size(p.next); } Copy public int size() { return size(first); } Copy public class SLList { ... /* IntNode declaration omitted. */ private IntNode first; private int size; public SLList(int x) { first = new IntNode(x, null); size = 1; } public void addFirst(int x) { first = new IntNode(x, first); size += 1; } public int size() { return size; } ... } Copy public SLList() { first = null; size = 0; } Copy public void addLast(int x) { size += 1; IntNode p = first; while (p.next != null) { p = p.next; } p.next = new IntNode(x, null); } Copy public void addLast(int x) { size += 1; if (first == null) { first = new IntNode(x, null); return; } IntNode p = first; while (p.next != null) { p = p.next; } p.next = new IntNode(x, null); } Copy public void addLast(int x) { size += 1; IntNode p = sentinel; while (p.next != null) { p = p.next; } p.next = new IntNode(x, null); } sun-brightdesktopmoon --- # 10. Inheritance II: Extends, Casting, Higher Order Functions | CS61B Textbook This section covers inheritance through extends, along with encapsulation, casting, and higher order functions. Its contents correspond to Lecture 9. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/10.-inheritance-ii-extends-casting-higher-order-functions#video-playlist) Video Playlist [Previous9\. Inheritance I: Interface and Implementation Inheritancechevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/9.-inheritance-i-interface-and-implementation-inheritance) [Next10.1 Implementation Inheritance: Extendschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/10.-inheritance-ii-extends-casting-higher-order-functions/10.1-implementation-inheritance-extends) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 2. Defining and Using Classes | CS61B Textbook If you do not have prior Java experience, we recommend that you work through the exercises in [HW0arrow-up-right](http://sp19.datastructur.es/materials/hw/hw0/hw0.html) before reading this chapter. It will cover various syntax issues that we will not discuss in the book. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/2.-defining-and-using-classes#static-vs-non-static-methods) Static vs. Non-Static Methods **Static Methods** All code in Java must be part of a class (or something similar to a class, which we'll learn about later). Most code is written inside of methods. Let's consider an example: Copy public class Dog { public static void makeNoise() { System.out.println("Bark!"); } } If we try running the `Dog` class, we'll simply get an error message: Copy $ java Dog Error: Main method not found in class Dog, please define the main method as: public static void main(String[] args) The `Dog` class we've defined doesn't do anything. We've simply defined something that `Dog` **can** do, namely make noise. To actually run the class, we'd either need to add a main method to the `Dog` class, as we saw in chapter 1.1. Or we could create a separate [`DogLauncher`arrow-up-right](https://www.youtube.com/watch?v=Q-LE-jJQLTM) class that runs methods from the `Dog` class. For example, consider the program below: Copy public class DogLauncher { public static void main(String[] args) { Dog.makeNoise(); } } A class that uses another class is sometimes called a "client" of that class, i.e. `DogLauncher` is a client of `Dog`. Neither of the two techniques is better: Adding a main method to `Dog` may be better in some situations, and creating a client class like `DogLauncher` may be better in others. The relative advantages of each approach will become clear as we gain additional practice throughout the course. **Instance Variables and Object Instantiation** Not all dogs are alike. Some dogs like to yap incessantly, while others bellow sonorously, bringing joy to all who hear their glorious call. Often, we write programs to mimic features of the universe we inhabit, and Java's syntax was crafted to easily allow such mimicry. One approach to allowing us to represent the spectrum of Dogdom would be to create separate classes for each type of Dog. As you should have seen in the past, classes can be instantiated, and instances can hold data. This leads to a more natural approach, where we create instances of the `Dog` class and make the behavior of the `Dog` methods contingent upon the properties of the specific `Dog`. To make this more concrete, consider the class below: As an example of using such a Dog, consider: When run, this program will create a `Dog` with weight 20, and that `Dog` will soon let out a nice "bark. bark.". Some key observations and terminology: * An `Object` in Java is an instance of any class. * The `Dog` class has its own variables, also known as _instance variables_ or _non-static variables_. These must be declared inside the class, unlike languages like Python or Matlab, where new variables can be added at runtime. * The method that we created in the `Dog` class did not have the `static` keyword. We call such methods _instance methods_ or _non-static methods_. * To call the `makeNoise` method, we had to first _instantiate_ a `Dog` using the `new` keyword, and then make a specific `Dog` bark. In other words, we called `d.makeNoise()` instead of `Dog.makeNoise()`. * Once an object has been instantiated, it can be _assigned_ to a _declared_ variable of the appropriate type, e.g. `d = new Dog();` * Variables and methods of a class are also called _members_ of a class. * Members of a class are accessed using _dot notation_. **Constructors in Java** As you've hopefully seen before, we usually construct objects in object oriented languages using a _constructor_: Here, the instantiation is parameterized, saving us the time and messiness of manually typing out potentially many instance variable assignments. To enable such syntax, we need only add a "constructor" to our Dog class, as shown below: The constructor with signature `public Dog(int w)` will be invoked anytime that we try to create a `Dog` using the `new` keyword and a single integer parameter. For those of you coming from Python, the constructor is very similar to the `__init__` method. **Terminology Summary** **Array Instantiation, Arrays of Objects** As we saw in HW0, arrays are also instantiated in Java using the new keyword. For example: Similarly, we can create arrays of instantiated objects in Java, e.g. Observe that new is used in two different ways: Once to create an array that can hold two `Dog` objects, and twice to create each actual `Dog`. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/2.-defining-and-using-classes#class-methods-vs-instance-methods) Class Methods vs. Instance Methods Java allows us to define two types of methods: * Class methods, a.k.a. static methods. * Instance methods, a.k.a. non-static methods. Instance methods are actions that can be taken only by a specific instance of a class. Static methods are actions that are taken by the class itself. Both are useful in different circumstances. As an example of a static method, the `Math` class provides a `sqrt` method. Because it is static, we can call it as follows: If `sqrt` had been an instance method, we would have instead the awkward syntax below. Luckily `sqrt` is a static method so we don't have to do this in real programs. Sometimes, it makes sense to have a class with both instance and static methods. For example, suppose want the ability to compare two dogs. One way to do this is to add a static method for comparing Dogs. This method could be invoked by, for example: Observe that we've invoked using the class name, since this method is a static method. We could also have implemented `maxDog` as a non-static method, e.g. Above, we use the keyword `this` to refer to the current object. This method could be invoked, for example, with: Here, we invoke the method using a specific instance variable. **Exercise 1.2.1**: What would the following method do? If you're not sure, try it out. **Static Variables** It is occasionally useful for classes to have static variables. These are properties inherent to the class itself, rather than the instance. For example, we might record that the scientific name (or binomen) for Dogs is "Canis familiaris": Static variables should be accessed using the name of the class rather than a specific instance, e.g. you should use `Dog.binomen`, not `d.binomen`. While Java technically allows you to access a static variable using an instance name, it is bad style, confusing, and in my opinion an error by the Java designers. **Exercise 1.2.2**: Complete this exercise: * Video: [linkarrow-up-right](https://youtu.be/8Gq-8mVbyFU) * Slide: [linkarrow-up-right](https://docs.google.com/presentation/d/10BFLHH8VaoYy7XaazwjaoTtLw3zvasX4HCssDruqw84/edit#slide=id.g6caa9a6fe_057) * Solution Video: [linkarrow-up-right](https://youtu.be/Osuy8UEH03M) #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/2.-defining-and-using-classes#public-static-void-mainstring-args) public static void main(String\[\] args) With what we've learned so far, it's time to demystify the declaration we've been using for the main method. Breaking it into pieces, we have: * `public`: So far, all of our methods start with this keyword. * `static`: It is a static method, not associated with any particular instance. * `void`: It has no return type. * `main`: This is the name of the method. * `String[] args`: This is a parameter that is passed to the main method. **Command Line Arguments** Since main is called by the Java interpreter itself rather than another Java class, it is the interpreter's job to supply these arguments. They refer usually to the command line arguments. For example, consider the program `ArgsDemo` below: This program prints out the 0th command line argument, e.g. In the example above, `args` will be an array of Strings, where the entries are {"these", "are", "command", "line", "arguments"}. **Summing Command Line Arguments** **Exercise 1.2.3**: Try to write a program that sums up the command line arguments, assuming they are numbers. For a solution, see the webcast or the code provided on GitHub. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/2.-defining-and-using-classes#using-libraries) Using Libraries One of the most important skills as a programmer is knowing how to find and use existing libraries. In the glorious modern era, it is often possible to save yourself tons of work and debugging by turning to the web for help. In this course, you're welcome to do this, with the following caveats: * Do not use libraries that we do not provide. * Cite your sources. * Do not search for solutions for specific homework or project problems. For example, it's fine to search for "convert String integer Java". However, it is not OK to search for "Project 2048 Berkeley". For more on collaboration and academic honesty policy, see the course syllabus. [Previous1.4 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/1.-introduction/1.4-exercises) [Next3\. References, Recursion, and Listschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/3.-references-recursion-and-lists) Last updated 2 years ago sun-brightdesktopmoon Copy $ java DogLauncher Bark! Copy public class TinyDog { public static void makeNoise() { System.out.println("yip yip yip yip"); } } public class MalamuteDog { public static void makeNoise() { System.out.println("arooooooooooooooo!"); } } Copy public class Dog { public int weightInPounds; public void makeNoise() { if (weightInPounds < 10) { System.out.println("yipyipyip!"); } else if (weightInPounds < 30) { System.out.println("bark. bark."); } else { System.out.println("woof!"); } } } Copy public class DogLauncher { public static void main(String[] args) { Dog d; d = new Dog(); d.weightInPounds = 20; d.makeNoise(); } } Copy public class DogLauncher { public static void main(String[] args) { Dog d = new Dog(20); d.makeNoise(); } } Copy public class Dog { public int weightInPounds; public Dog(int w) { weightInPounds = w; } public void makeNoise() { if (weightInPounds < 10) { System.out.println("yipyipyip!"); } else if (weightInPounds < 30) { System.out.println("bark. bark."); } else { System.out.println("woof!"); } } } Copy public class ArrayDemo { public static void main(String[] args) { /* Create an array of five integers. */ int[] someArray = new int[5]; someArray[0] = 3; someArray[1] = 4; } } Copy public class DogArrayDemo { public static void main(String[] args) { /* Create an array of two dogs. */ Dog[] dogs = new Dog[2]; dogs[0] = new Dog(8); dogs[1] = new Dog(20); /* Yipping will result, since dogs[0] has weight 8. */ dogs[0].makeNoise(); } } Copy x = Math.sqrt(100); Copy Math m = new Math(); x = m.sqrt(100); Copy public static Dog maxDog(Dog d1, Dog d2) { if (d1.weightInPounds > d2.weightInPounds) { return d1; } return d2; } Copy Dog d = new Dog(15); Dog d2 = new Dog(100); Dog.maxDog(d, d2); Copy public Dog maxDog(Dog d2) { if (this.weightInPounds > d2.weightInPounds) { return this; } return d2; } Copy Dog d = new Dog(15); Dog d2 = new Dog(100); d.maxDog(d2); Copy public static Dog maxDog(Dog d1, Dog d2) { if (weightInPounds > d2.weightInPounds) { return this; } return d2; } Copy public class Dog { public int weightInPounds; public static String binomen = "Canis familiaris"; ... } Copy public class ArgsDemo { public static void main(String[] args) { System.out.println(args[0]); } } Copy $ java ArgsDemo these are command line arguments these sun-brightdesktopmoon --- # 3. References, Recursion, and Lists | CS61B Textbook ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/3.-references-recursion-and-lists#lists) Lists You've no doubt used a list data structure at some point in the past. For example, in Python: Copy L = [3, 5, 6] L.append(7) While Java does have a built-in List type, we're going to eschew using it for now. In this chapter, we'll build our own list from scratch, along the way learning some key features of Java. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/3.-references-recursion-and-lists#the-mystery-of-the-walrus) The Mystery of the Walrus To begin our journey, we will first ponder the profound Mystery of the Walrus. Try to predict what happens when we run the code below. Does the change to b affect a? Hint: If you're coming from Python, Java has the same behavior. Copy Walrus a = new Walrus(1000, 8.3); Walrus b; b = a; b.weight = 5; System.out.println(a); System.out.println(b); Now try to predict what happens when we run the code below. Does the change to x affect y? Copy int x = 5; int y; y = x; x = 2; System.out.println("x is: " + x); System.out.println("y is: " + y); The answer can be found [herearrow-up-right](http://cscircles.cemc.uwaterloo.ca/java_visualize/#code=public+class+PollQuestions+%7B%0A+++public+static+void+main%28String%5B%5D+args%29+%7B%0A++++++Walrus+a+%3D+new+Walrus%281000,+8.3%29%3B%0A++++++Walrus+b%3B%0A++++++b+%3D+a%3B%0A++++++b.weight+%3D+5%3B%0A++++++System.out.println%28a%29%3B%0A++++++System.out.println%28b%29%3B++++++%0A%0A++++++int+x+%3D+5%3B%0A++++++int+y%3B%0A++++++y+%3D+x%3B%0A++++++x+%3D+2%3B%0A++++++System.out.println%28%22x+is%3A+%22+%2B+x%29%3B%0A++++++System.out.println%28%22y+is%3A+%22+%2B+y%29%3B++++++%0A+++%7D%0A+++%0A+++public+static+class+Walrus+%7B%0A++++++public+int+weight%3B%0A++++++public+double+tuskSize%3B%0A++++++%0A++++++public+Walrus%28int+w,+double+ts%29+%7B%0A+++++++++weight+%3D+w%3B%0A+++++++++tuskSize+%3D+ts%3B%0A++++++%7D%0A%0A++++++public+String+toString%28%29+%7B%0A+++++++++return+String.format%28%22weight%3A+%25d,+tusk+size%3A+%25.2f%22,+weight,+tuskSize%29%3B%0A++++++%7D%0A+++%7D%0A%7D&mode=edit) . While subtle, the key ideas that underlie the Mystery of the Walrus will be incredibly important to the efficiency of the data structures that we'll implement in this course, and a deep understanding of this problem will also lead to safer, more reliable code. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/3.-references-recursion-and-lists#bits) Bits All information in your computer is stored in _memory_ as a sequence of ones and zeros. Some examples: * 72 is often stored as 01001000 * 205.75 is often stored as 01000011 01001101 11000000 00000000 * The letter H is often stored as 01001000 (same as 72) * The true value is often stored as 00000001 In this course, we won't spend much time talking about specific binary representations, e.g. why on earth 205.75 is stored as the seemingly random string of 32 bits above. Understanding specific representations is a topic of [CS61Carrow-up-right](http://www-inst.eecs.berkeley.edu/~cs61c/) , the followup course to 61B. Though we won't learn the language of binary, it's good to know that this is what is going on under the hood. One interesting observation is that both 72 and H are stored as 01001000. This raises the question: how does a piece of Java code know how to interpret 01001000? The answer is through types! For example, consider the code below: If we run this code, we get: In this case, both the x and c variables contain the same bits (well, almost...), but the Java interpreter treats them differently when printed. In Java, there are 8 primitive types: byte, short, int, long, float, double, boolean, and char. Each has different properties that we'll discuss throughout the course, with the exception of short and float, which you'll likely never use. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/3.-references-recursion-and-lists#declaring-a-variable-simplified) Declaring a Variable (Simplified) You can think of your computer as containing a vast number of memory bits for storing information, each of which has a unique address. Many billions of such bits are available to the modern computer. When you declare a variable of a certain type, Java finds a contiguous block with exactly enough bits to hold a thing of that type. For example, if you declare an int, you get a block of 32 bits. If you declare a byte, you get a block of 8 bits. Each data type in Java holds a different number of bits. The exact number is not terribly important to us in this class. For the sake of having a convenient metaphor, we'll call one of these blocks a "box" of bits. In addition to setting aside memory, the Java interpreter also creates an entry in an internal table that maps each variable name to the location of the first bit in the box. For example, if you declared `int x` and `double y`, then Java might decide to use bits 352 through 384 of your computer's memory to store x, and bits 20800 through 20864 to store y. The interpreter will then record that int x starts at bit 352 and y starts at bit 20800. For example, after executing the code: We'd end up with boxes of size 32 and 64 respectively, as shown in the figure below: ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2Fx_and_y_empty_bitwise.png&width=768&dpr=3&quality=100&sign=643b9c06&sv=2) x\_and\_y\_empty\_bitwise The Java language provides no way for you to know the location of the box, e.g. you can't somehow find out that x is in position 352. In other words, the exact memory address is below the level of abstraction accessible to us in Java. This is unlike languages like C where you can ask the language for the exact address of a piece of data. For this reason, I have omitted the addresses from the figure above. This feature of Java is a tradeoff! Hiding memory locations from the programmer gives you less control, which prevents you from doing certain [types of optimizationsarrow-up-right](http://www.informit.com/articles/article.aspx?p=2246428&seqNum=5) . However, it also avoids a [large class of very tricky programming errorsarrow-up-right](http://www.informit.com/articles/article.aspx?p=2246428&seqNum=1) . In the modern era of very low cost computing, this tradeoff is usually well worth it. As the wise Donald Knuth once said: "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil". As an analogy, you do not have direct control over your heartbeat. While this restricts your ability to optimize for certain situations, it also avoids the possibility of making stupid errors like accidentally turning it off. Java does not write anything into the reserved box when a variable is declared. In other words, there are no default values. As a result, the Java compiler prevents you from using a variable until after the box has been filled with bits using the `=` operator. For this reason, I have avoided showing any bits in the boxes in the figure above. When you assign values to a memory box, it is filled with the bits you specify. For example, if we execute the lines: Then the memory boxes from above are filled as shown below, in what I call **box notation**. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2Fx_and_y_empty_filled.png&width=768&dpr=3&quality=100&sign=32022993&sv=2) x\_and\_y\_empty\_filled.png The top bits represent -1431195969, and the bottom bits represent 567213.112. Why these specific sequences of bits represent these two numbers is not important, and is a topic covered in CS61C. However, if you're curious, see [integer representationsarrow-up-right](https://en.wikipedia.org/wiki/Two%27s_complement) and [double representationsarrow-up-right](https://en.wikipedia.org/wiki/IEEE_floating_point) on wikipedia. Note: Memory allocation is actually somewhat more complicated than described here, and is a topic of CS 61C. However, this model is close enough to reality for our purposes in 61B. **Simplified Box Notation** While the box notation we used in the previous section is great for understanding approximately what's going on under the hood, it's not useful for practical purposes since we don't know how to interpret the binary bits. Thus, instead of writing memory box contents in binary, we'll write them in human readable symbols. We will do this throughout the rest of the course. For example, after executing: We can represent the program environment using what I call **simplified box notation**, shown below: ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2Fx_and_y_simplified_box_notation.png&width=768&dpr=3&quality=100&sign=d12dfa7c&sv=2) x\_and\_y\_simplified\_box\_notation.png #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/3.-references-recursion-and-lists#the-golden-rule-of-equals-groe) The Golden Rule of Equals (GRoE) Now armed with simplified box notation, we can finally start to resolve the Mystery of the Walrus. It turns out our Mystery has a simple solution: When you write `y = x`, you are telling the Java interpreter to copy the bits from x into y. This Golden Rule of Equals (GRoE) is the root of all truth when it comes to understanding our Walrus Mystery. This simple idea of copying the bits is true for ANY assignment using `=` in Java. To see this in action, click [this linkarrow-up-right](http://cscircles.cemc.uwaterloo.ca/java_visualize/#code=public+class+PollQuestions+%7B%0A+++public+static+void+main(String%5B%5D+args%29+%7B%0A++++++int+x+%3D+5%3B%0A++++++int+y%3B%0A++++++y+%3D+x%3B%0A++++++x+%3D+2%3B%0A++++++System.out.println(%22x+is%3A+%22+%2B+x%29%3B%0A++++++System.out.println(%22y+is%3A+%22+%2B+y%29%3B++++++%0A+++%7D%0A%7D&mode=display&curInstr=0) . #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/3.-references-recursion-and-lists#reference-types) Reference Types Above, we said that there are 8 primitive types: byte, short, int, long, float, double, boolean, char. Everything else, including arrays, is not a primitive type but rather a `reference type`. **Object Instantiation** When we _instantiate_ an Object using `new` (e.g. Dog, Walrus, Planet), Java first allocates a box for each instance variable of the class, and fills them with a default value. The constructor then usually (but not always) fills every box with some other value. For example, if our Walrus class is: And we create a Walrus using `new Walrus(1000, 8.3);`, then we end up with a Walrus consisting of two boxes of 32 and 64 bits respectively: ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2Fanonymous_walrus.png&width=768&dpr=3&quality=100&sign=6ff7c6ae&sv=2) anonymous\_walrus.png In real implementations of the Java programming language, there is actually some additional overhead for any object, so a Walrus takes somewhat more than 96 bits. However, for our purposes, we will ignore such overhead, since we will never interact with it directly. The Walrus we've created is anonymous, in the sense that it has been created, but it is not stored in any variable. Let's now turn to variables that store objects. **Reference Variable Declaration** When we _declare_ a variable of any reference type (Walrus, Dog, Planet, array, etc.), Java allocates a box of 64 bits, no matter what type of object. At first glance, this might seem to lead to a Walrus Paradox. Our Walrus from the previous section required more than 64 bits to store. Furthermore, it may seem bizarre that no matter the type of object, we only get 64 bits to store it. However, this problem is easily resolved with the following piece of information: the 64 bit box contains not the data about the walrus, but instead the address of the Walrus in memory. As an example, suppose we call: The first line creates a box of 64 bits. The second line creates a new Walrus, and the address is returned by the `new` operator. These bits are then copied into the `someWalrus` box according to the GRoE. If we imagine our Walrus weight is stored starting at bit `5051956592385990207` of memory, and tuskSize starts at bit `5051956592385990239`, we might store `5051956592385990207` in the Walrus variable. In binary, `5051956592385990207` is represented by the 64 bits `0100011000011100001001111100000100011101110111000001111000111111`, giving us in box notation: ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2FsomeWalrus_bit_notation.png&width=768&dpr=3&quality=100&sign=2d8bded7&sv=2) someWalrus\_bit\_notation.png We can also assign the special value `null` to a reference variable, corresponding to all zeros. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2FsomeWalrus_bit_notation_null.png&width=768&dpr=3&quality=100&sign=1051ad19&sv=2) someWalrus\_bit\_notation\_null.png **Box and Pointer Notation** Just as before, it's hard to interpret a bunch of bits inside a reference variable, so we'll create a simplified box notation for reference variable as follows: * If an address is all zeros, we will represent it with null. * A non-zero address will be represented by an **arrow** pointing at an object instantiation. This is also sometimes called "box and pointer" notation. For the examples from the previous section, we'd have: ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2FsomeWalrus_simplified_bit_notation.png&width=768&dpr=3&quality=100&sign=61d12b86&sv=2) someWalrus\_simplified\_bit\_notation.png ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2FsomeWalrus_simplified_bit_notation_null.png&width=768&dpr=3&quality=100&sign=5157330e&sv=2) someWalrus\_simplified\_bit\_notation\_null.png **Resolving the Mystery of the Walrus** We're now finally ready to resolve, fully and completely, the Mystery of the Walrus. After the first line is executed, we have: ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2Fmystery_of_the_walrus_resolved_step1.png&width=768&dpr=3&quality=100&sign=e13fd4cf&sv=2) mystery\_of\_the\_walrus\_resolved\_step1.png After the second line is executed, we have: ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2Fmystery_of_the_walrus_resolved_step2.png&width=768&dpr=3&quality=100&sign=62e93de2&sv=2) mystery\_of\_the\_walrus\_resolved\_step2.png Note that above, b is undefined, not null. According to the GRoE, the final line simply copies the bits in the `a` box into the `b` box. Or in terms of our visual metaphor, this means that b will copy exactly the arrow in a and now show an arrow pointing at the same object. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2Fmystery_of_the_walrus_resolved_step3.png&width=768&dpr=3&quality=100&sign=d699b4f9&sv=2) mystery\_of\_the\_walrus\_resolved\_step3.png And that's it. There's no more complexity than this. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/3.-references-recursion-and-lists#parameter-passing) Parameter Passing When you pass parameters to a function, you are also simply copying the bits. In other words, the GRoE also applies to parameter passing. Copying the bits is usually called "pass by value". In Java, we **always** pass by value. For example, consider the function below: Suppose we invoke this function as shown below: After executing the first two lines of this function, the main method will have two boxes labeled `x` and `y` containing the values shown below: ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2Fmain_x_y.png&width=768&dpr=3&quality=100&sign=2072dbfc&sv=2) main\_x\_y.png When the function is invoked, the `average` function has its **own** scope with two new boxes labeled as `a` and `b`, and the bits are simply _copied_ in. This copying of bits is what we refer to when we say "pass by value". ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2Faverage_a_b.png&width=768&dpr=3&quality=100&sign=8834e37c&sv=2) average\_a\_b.png If the `average` function were to change `a`, then `x` in main would be unchanged, since the GRoE tells us that we'd simply be filling in the box labeled `a` with new bits. **Test Your Understanding** **Exercise 2.1.1**: Suppose we have the code below: Does the call to `doStuff` have an effect on walrus and/or x? Hint: We only need to know the GRoE to solve this problem. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/3.-references-recursion-and-lists#instantiation-of-arrays) Instantiation of Arrays As mentioned above, variables that store arrays are reference variables just like any other. As an example, consider the declarations below: Both of these declarations create memory boxes of 64 bits. `x` can only hold the address of an `int` array, and `planets` can only hold the address of a `Planet` array. Instantiating an array is very similar to instantiating an object. For example, if we create an integer array of size 5 as shown below: Then the `new` keyword creates 5 boxes of 32 bits each and returns the address of the overall object for assignment to x. Objects can be lost if you lose the bits corresponding to the address. For example if the only copy of the address of a particular Walrus is stored in `x`, then `x = null` will cause you to permanently lose this Walrus. This isn't necessarily a bad thing, since you'll often decide you're done with an object, and thus it's safe to simply throw away the reference. We'll see this when we build lists later in this chapter. **The Law of the Broken Futon** You might ask yourself why we spent so much time and space covering what seems like a triviality. This is probably especially true if you have prior Java experience. The reason is that it is very easy for a student to have a half-cocked understanding of this issue, allowing them to write code, but without true comprehension of what's going on. While this might be fine in the short term, in the long term, doing problems without full understanding may doom you to failure later down the line. There's a blog post about this so-called [Law of the Broken Futonarrow-up-right](https://mathwithbaddrawings.com/2015/04/08/the-math-ceiling-wheres-your-cognitive-breaking-point/) that you might find interesting. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/3.-references-recursion-and-lists#instantiation-of-arrays-1) \== vs. Arrays.equals Similar to =, we can think of the == operator in terms of bits. Whenever we write `x==y` we are asking Jafa to compare the literal bits in memory boxes `x` and `y`. For example, suppose we have the code: This code will print false, since `x` and `y` each contain the 64 bit address of two different arrays in memory, albeit two arrays which happen to contain the same information. If we want to compare the two content of the two arrays, we can use Arrays.equals instead, e.g. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/3.-references-recursion-and-lists#intlists) IntLists Now that we've truly understood the Mystery of the Walrus, we're ready to build our own list class. It turns out that a very basic list is trivial to implement, as shown below: You may remember something like this from 61a called a "Linked List". Such a list is ugly to use. For example, if we want to make a list of the numbers 5, 10, and 15, we can either do: Alternately, we could build our list backwards, yielding slightly nicer but harder to understand code: While you could in principle use the IntList to store any list of integers, the resulting code would be rather ugly and prone to errors. We'll adopt the usual object oriented programming strategy of adding helper methods to our class to perform basic tasks. **size and iterativeSize** We'd like to add a method `size` to the `IntList` class so that if you call `L.size()`, you get back the number of items in `L`. Consider writing a `size` and `iterativeSize` method before reading the rest of this chapter. `size` should use recursion, and `iterativeSize` should not. You'll probably learn more by trying on your own before seeing how I do it. The two videos provide a live demonstration of how one might implement these methods. My `size` method is as shown below: The key thing to remember about recursive code is that you need a base case. In this situation, the most reasonable base case is that rest is `null`, which results in a size 1 list. Exercise: You might wonder why we don't do something like `if (this == null) return 0;`. Why wouldn't this work? Answer: Think about what happens when you call size. You are calling it on an object, for example L.size(). If L were null, then you would get a NullPointer error! My `iterativeSize` method is as shown below. I recommend that when you write iterative data structure code that you use the name `p` to remind yourself that the variable is holding a pointer. You need that pointer because you can't reassign "this" in Java. The followups in [this Stack Overflow Postarrow-up-right](https://stackoverflow.com/questions/23021377/reassign-this-in-java-class) offer a brief explanation as to why. **get** While the `size` method lets us get the size of a list, we have no easy way of getting the ith element of the list. Exercise: Write a method `get(int i)` that returns the ith item of the list. For example, if `L` is 5 -> 10 -> 15, then `L.get(0)` should return 5, `L.get(1)` should return 10, and `L.get(2)` should return 15. It doesn't matter how your code behaves for invalid `i`, either too big or too small. For a solution, see the lecture video above or the lectureCode repository. Note that the method we've written takes linear time! That is, if you have a list that is 1,000,000 items long, then getting the last item is going to take much longer than it would if we had a small list. We'll see an alternate way to implement a list that will avoid this problem in a future lecture. [Previous2\. Defining and Using Classeschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/2.-defining-and-using-classes) [Next4\. SLListschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/4.-sllists) Last updated 1 year ago sun-brightdesktopmoon Copy char c = 'H'; int x = c; System.out.println(c); System.out.println(x); Copy H 72 Copy int x; double y; Copy x = -1431195969; y = 567213.112; Copy int x; double y; x = -1431195969; y = 567213.112; Copy int x = 5; int y; y = x; x = 2; System.out.println("x is: " + x); System.out.println("y is: " + y); Copy public static class Walrus { public int weight; public double tuskSize; public Walrus(int w, double ts) { weight = w; tuskSize = ts; } } Copy Walrus someWalrus; someWalrus = new Walrus(1000, 8.3); Copy Walrus a = new Walrus(1000, 8.3); Walrus b; b = a; Copy public static double average(double a, double b) { return (a + b) / 2; } Copy public static void main(String[] args) { double x = 5.5; double y = 10.5; double avg = average(x, y); } Copy public class PassByValueFigure { public static void main(String[] args) { Walrus walrus = new Walrus(3500, 10.5); int x = 9; doStuff(walrus, x); System.out.println(walrus); System.out.println(x); } public static void doStuff(Walrus W, int x) { W.weight = W.weight - 100; x = x - 5; } } Copy int[] x; Planet[] planets; Copy x = new int[]{0, 1, 2, 95, 4}; Copy int[] x = new int[]{0, 1, 2, 95, 4}; int[] y = new int[]{0, 1, 2, 95, 4}; System.out.println(x == y); #false Copy int[] x = new int[]{0, 1, 2, 95, 4}; int[] y = new int[]{0, 1, 2, 95, 4}; System.out.println(Arrays.equals(x, y)); #true Copy public class IntList { public int first; public IntList rest; public IntList(int f, IntList r) { first = f; rest = r; } } Copy IntList L = new IntList(5, null); L.rest = new IntList(10, null); L.rest.rest = new IntList(15, null); Copy IntList L = new IntList(15, null); L = new IntList(10, L); L = new IntList(5, L); Copy /** Return the size of the list using... recursion! */ public int size() { if (rest == null) { return 1; } return 1 + this.rest.size(); } Copy /** Return the size of the list using no recursion! */ public int iterativeSize() { IntList p = this; int totalSize = 0; while (p != null) { totalSize += 1; p = p.rest; } return totalSize; } sun-brightdesktopmoon --- # 9. Inheritance I: Interface and Implementation Inheritance | CS61B Textbook ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/9.-inheritance-i-interface-and-implementation-inheritance#the-problem) The Problem Recall the two list classes we created last week: SLList and AList. If you take a look at their documentation, you'll notice that they are very similar. In fact, all of their supporting methods are the same! Suppose we want to write a class `WordUtils`that includes functions we can run on lists of words, including a method that calculates the longest string in an SLList. **Exercise 4.1.1.** Try writing this method by yourself. The method should take in an SLList of strings and return the longest string in the list. Here is the method that we came up with. Copy public static String longest(SLList list) { int maxDex = 0; for (int i = 0; i < list.size(); i += 1) { String longestString = list.get(maxDex); String thisString = list.get(i); if (thisString.length() > longestString.length()) { maxDex = i; } } return list.get(maxDex); } How do we make this method work for AList as well? All we really have to do is change the method's signature: the parameter should be changed to Now we have two methods in our `WordUtils` class with exactly the same method name. and This is actually allowed in Java! It's something called _method overloading_. When you call WordUtils.longest, Java knows which one to run according to what kind of parameter you supply it. If you supply it with an AList, it will call the AList method. Same with an SLList. It's nice that Java is smart enough to know how to deal with two of the same methods for different types, but overloading has several downsides: * It's super repetitive and ugly, because you now have two virtually identical blocks of code. * It's more code to maintain, meaning if you want to make a small change to the `longest` method such as correcting a bug, you need to change it in the method for each type of list. * If we want to make more list types, we would have to copy the method for every new list class. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/9.-inheritance-i-interface-and-implementation-inheritance#hypernyms-hyponyms-and-interface-inheritance) Hypernyms, Hyponyms, and Interface Inheritance In the English language and life in general, there exist logical hierarchies to words and objects. Dog is what is called a _hypernym of_ poodle, malamute, husky, etc. In the reverse direction, poodle, malamute, and husky, are _hyponyms_ of dog. These words form a hierarchy of "is-a" relationships: * a poodle "is-a" dog * a dog "is-a" canine * a canine "is-a" carnivore * a carnivore "is-an" animal ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fassets%2Fhierarchy.png&width=768&dpr=3&quality=100&sign=d6573856&sv=2) hierarchy The same hierarchy goes for SLLists and ALists! SLList and AList are both hyponyms of a more general list. We will formalize this relationship in Java: if a SLList is a hyponym of List61B, then the SLList class is a **subclass** of the List61B class and the List61B class is a **superclass** of the SLList class. **Figure 4.1.1** ![subclass](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fassets%2Fsubclass.png&width=300&dpr=3&quality=100&sign=739416bd&sv=2) In Java, in order to _express_ this hierarchy, we need to do **two things**: * Step 1: Define a type for the general list hypernym -- we will choose the name List61B. * Step 2: Specify that SLList and AList are hyponyms of that type. The new List61B is what Java calls an **interface**. It is essentially a contract that specifies what a list must be able to do, but it doesn't provide any implementation for those behaviors. Can you think of why? Here is our List61B interface. At this point, we have satisfied the first step in establishing the relationship hierarchy: creating a hypernym. Now, to complete step 2, we need to specify that AList and SLList are hyponyms of the List61B class. In Java, we define this relationship in the class definition. We will add to `public class AList {...}` a relationship-defining word: implements. `public class AList implements List61B{...}` `implements List61B` is essentially a promise. AList is saying "I promise I will have and define all the attributes and behaviors specified in the List61B interface" Now we can edit our `longest` method in `WordUtils` to take in a List61B. Because AList and SLList share an "is-a" relationship. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/9.-inheritance-i-interface-and-implementation-inheritance#overriding) Overriding We promised we would implement the methods specified in List61B in the AList and SLList classes, so let's go ahead and do that. When implementing the required functions in the subclass, it's useful (and actually required in 61B) to include the `@Override` tag right on top of the method signature. Here, we have done that for just one method. It is good to note that even if you don’t include this tag, you _are_ still overriding the method. So technically, you don't _have_ to include it. However, including the tag acts as a safeguard for you as the programmer by alerting the compiler that you intend to override this method. Why would this be helpful you ask? Well, it's kind of like having a proofreader! The compiler will tell you if something goes wrong in the process. Say you want to override the `addLast` method. What if you make a typo and accidentally write `addLsat`? If you don't include the @Override tag, then you might not catch the mistake, which could make debugging a more difficult and painful process. Whereas if you include @Override, the compiler will stop and prompt you to fix your mistakes before your program even runs. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/9.-inheritance-i-interface-and-implementation-inheritance#interface-inheritance) Interface Inheritance Interface Inheritance refers to a relationship in which a subclass inherits all the methods/behaviors of the superclass. As in the List61B class we defined in the **Hyponyms and Hypernyms** section, the interface includes all the method signatures, but not implementations. It's up to the subclass to actually provide those implementations. This inheritance is also multi-generational. This means if we have a long lineage of superclass/subclass relationships like in **Figure 4.1.1**, AList not only inherits the methods from List61B but also every other class above it all the way to the highest superclass AKA AList inherits from Collection. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/9.-inheritance-i-interface-and-implementation-inheritance#groe) GRoE ----------------------------------------------------------------------------------------------------------------------------- Recall the Golden Rule of Equals we introduced in the first chapter. This means whenever we make an assignment `a = b` , we copy the bits from b into a, with the requirement that b is the same type as a. You can't assign `Dog b = 1` or `Dog b = new Cat()` because 1 is not a Dog and neither is Cat. Let's try to apply this rule to the `longest` method we wrote previously in this chapter. `public static String longest(List61B list)` takes in a List61B. We said that this could take in AList and SLList as well, but how is that possible since AList and List61B are different classes? Well, recall that AList shares an "is-a" relationship with List61B, Which means an AList should be able to fit into a List61B box! **Exercise 4.1.2** Do you think the code below will compile? If so, what happens when it runs? Here are possible answers: * Will not compile. * Will compile, but will cause an error on the **new** line * When it runs, an SLList is created and its address is stored in the someList variable, but it crashes on someList.addFirst() since the List class doesn't implement addFirst; * When it runs, and SLList is created and its address is stored in the someList variable. Then the string "elk" is inserted into the SLList referred to by addFirst. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/9.-inheritance-i-interface-and-implementation-inheritance#implementation-inheritance) Implementation Inheritance Previously, we had an interface List61B that only had method headers identifying **what** List61B's should do. But, now we will see that we can write methods in List61B that already have their implementation filled out. These methods identify **how** hypernyms of List61B should behave. In order to do this, you must include the `default` keyword in the method signature. If we define this method in List61B Then everything that implements the List61B class can use the method! However, there is one small inefficiency in this method. Can you catch it? For an SLList, the `get` method needs to jump through the entirety of the list. during each call. It's much better to just print while jumping through! We want SLList to print a different way than the way specified in its interface. To do this, we need to override it. In SLList, we implement this method; Now, whenever we call print() on an SLList, it will call this method instead of the one in List61B. You may be wondering, how does Java know which print() to call? Good question. Java is able to do this due to something called **dynamic method selection**. We know that variables in java have a type. `List61B lst = new SLList();` In the above declaration and instantiation, lst is of type "List61B". This is called the "static type" However, the objects themselves have types as well. the object that lst points to is of type SLList. Although this object is intrinsically an SLList (since it was declared as such), it is also a List61B, because of the “is-a” relationship we explored earlier. But, because the object itself was instantiated using the SLList constructor, We call this its "dynamic type". Aside: the name “dynamic type” is actually quite semantic in its origin! Should lst be reassigned to point to an object of another type, say a AList object, lst’s dynamic type would now be AList and not SLList! It’s dynamic because it changes based on the type of the object it’s currently referring to. When Java runs a method that is overriden, it searches for the appropriate method signature in it's **dynamic type** and runs it. **IMPORTANT: This does not work for overloaded methods!** Say there are two methods in the same class and you run this code The first call to peek() will use the second peek method that takes in an SLList. The second call to peek() will use the first peek method which takes in a List61B. This is because the only distinction between two overloaded methods is the types of the parameters. When Java checks to see which method to call, it checks the **static type** and calls the method with the parameter of the same type. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/9.-inheritance-i-interface-and-implementation-inheritance#interface-inheritance-vs-implementation-inheritance) Interface Inheritance vs Implementation Inheritance How do we differentiate between "interface inheritance" and "implementation inheritance"? Well, you can use this simple distinction: * Interface inheritance (what): Simply tells what the subclasses should be able to do. * EX) all lists should be able to print themselves, how they do it is up to them. * Implementation inheritance (how): Tells the subclasses how they should behave. * EX) Lists should print themselves exactly this way: by getting each element in order and then printing them. When you are creating these hierarchies, remember that the relationship between a subclass and a superclass should be an "is-a" relationship. AKA Cat should only implement Animal Cat **is an** Animal. You should not be defining them using a "has-a" relationship. Cat **has-a** Claw, but Cat definitely should not be implementing Claw. Finally, Implementation inheritance may sound nice and all but there are some drawbacks: * We are fallible humans, and we can't keep track of everything, so it's possible that you overrode a method but forgot you did. * It may be hard to resolve conflicts in case two interfaces give conflicting default methods. * It encourages overly complex code. [Previous8\. ArrayListchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/8.-arraylist) [Next10\. Inheritance II: Extends, Casting, Higher Order Functionschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/10.-inheritance-ii-extends-casting-higher-order-functions) Last updated 2 years ago * [The Problem](https://cs61b-2.gitbook.io/cs61b-textbook/9.-inheritance-i-interface-and-implementation-inheritance#the-problem) * [Hypernyms, Hyponyms, and Interface Inheritance](https://cs61b-2.gitbook.io/cs61b-textbook/9.-inheritance-i-interface-and-implementation-inheritance#hypernyms-hyponyms-and-interface-inheritance) * [Overriding](https://cs61b-2.gitbook.io/cs61b-textbook/9.-inheritance-i-interface-and-implementation-inheritance#overriding) * [Interface Inheritance](https://cs61b-2.gitbook.io/cs61b-textbook/9.-inheritance-i-interface-and-implementation-inheritance#interface-inheritance) * [GRoE](https://cs61b-2.gitbook.io/cs61b-textbook/9.-inheritance-i-interface-and-implementation-inheritance#groe) * [Implementation Inheritance](https://cs61b-2.gitbook.io/cs61b-textbook/9.-inheritance-i-interface-and-implementation-inheritance#implementation-inheritance) * [Interface Inheritance vs Implementation Inheritance](https://cs61b-2.gitbook.io/cs61b-textbook/9.-inheritance-i-interface-and-implementation-inheritance#interface-inheritance-vs-implementation-inheritance) sun-brightdesktopmoon Copy SLList list Copy AList list Copy public static String longest(SLList list) Copy public static String longest(AList list) Copy public interface List61B { public void addFirst(Item x); public void add Last(Item y); public Item getFirst(); public Item getLast(); public Item removeLast(); public Item get(int i); public void insert(Item x, int position); public int size(); } Copy @Override public void addFirst(Item x) { insert(x, 0); } Copy public static void main(String[] args) { List61B someList = new SLList(); someList.addFirst("elk"); } Copy default public void print() { for (int i = 0; i < size(); i += 1) { System.out.print(get(i) + " "); } System.out.println(); } Copy @Override public void print() { for (Node p = sentinel.next; p != null; p = p.next) { System.out.print(p.item + " "); } } Copy public static void peek(List61B list) { System.out.println(list.getLast()); } public static void peek(SLList list) { System.out.println(list.getFirst()); } Copy SLList SP = new SLList(); List61B LP = SP; SP.addLast("elk"); SP.addLast("are"); SP.addLast("cool"); peek(SP); peek(LP); sun-brightdesktopmoon --- # 12. Inheritance IV: Iterators, Object Methods | CS61B Textbook This section covers Lists and Sets, how to throw informative exceptions, iterations, and object methods. Its contents correspond to Lecture 11. [Previous11.6 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.6-exercises) [Next12.1 Lists and Sets in Javachevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/12.-inheritance-iv-iterators-object-methods/12.1-lists-and-sets-in-java) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 11. Inheritance III: Subtype Polymorphism, Comparators, Comparable | CS61B Textbook In this chapter of the textbook we will be: * Reviewing Dynamic Method Selection * Defining Subtype Polymorphism and contrasting it against Explicit Higher Order Functions * Seeing Applications of Subtype Polymorphism in: * Comparators * Comparables The following videos for Lecture 10 correspond to the content for this chapter of the textbook. [Previous10.5 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/10.-inheritance-ii-extends-casting-higher-order-functions/10.5-exercises) [Next11.1 A Review of Dynamic Method Selectionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.1-a-review-of-dynamic-method-selection) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 1.4 Exercises | CS61B Textbook 1. **True/False**: All variables, parameters, and methods must have a declared type in Java, and the type can never change. 2. Suppose we have a function `smaller(a, b)` that takes in two `int` arguments `a` and `b` and returns the smaller of the two. What would the expression `String x = smaller(10, 20) + 3;` output? 3. Choose all statements that are true in Java: * All code must be part of a class. * The end and beginning of code segments are delimited using curly brackets `{}`. * All statements in Java end with a semi-colon `;`. * Any code we want to run must be inside of a function `public static void main(String[] args)`. chevron-rightSolutions[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/1.-introduction/1.4-exercises#solutions) 1. True. See "Static Typing" for more information. 2. This line of code would cause a compilation error because the declared type `String` is incompatible with the type returned by `smaller` and adding `3`, which would be an `int.` 3. All the following statements are true. [Previous1.3 Basic Java Featureschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/1.-introduction/1.3-basic-java-features) [Next2\. Defining and Using Classeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/2.-defining-and-using-classes) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 7. Testing | CS61B Textbook ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/7.-testing#testing-and-selection-sort) Testing and Selection Sort One of the most important skills an intermediate to advanced programmer can have is the ability to tell when your code is correct. In this chapter, we'll discuss how you can write tests to evaluate code correctness. Along the way, we'll also discuss an algorithm for sorting called Selection Sort. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/7.-testing#a-new-way) A New Way When you write a program, it may have errors. In a classroom setting, you gain confidence in your code's correctness through some combination of user interaction, code analysis, and autograder testing, with this last item being of the greatest importance in many cases, particularly as it is how you earn points. Autograders, of course, are not magic. They are code that the instructors write that is fundamentally not all that different from the code that you are writing. In the real world, these tests are written by the programmers themselves, rather than some benevolent Josh-Hug-like third party. In this chapter, we'll explore how we can write our own tests. Our goal will be to create a class called `Sort` that provides a method `sort(String[] x)` that destructively sorts the strings in the array `x`. As a totally new way of thinking, we'll start by writing `testSort()` first, and only after we've finished the test, we'll move on to writing the actual sorting code. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/7.-testing#a-d-hoc-testing) Ad Hoc Testing Writing a test for `Sort.sort` is relatively straightforward, albeit tedious. We simply need to create an input, call `sort`, and check that the output of the method is correct. If the output is not correct, we print out the first mismatch and terminate the test. For example, we might create a test class as follows: Copy public class TestSort { /** Tests the sort method of the Sort class. */ public static void testSort() { String[] input = {"i", "have", "an", "egg"}; String[] expected = {"an", "egg", "have", "i"}; Sort.sort(input); for (int i = 0; i < input.length; i += 1) { if (!input[i].equals(expected[i])) { System.out.println("Mismatch in position " + i + ", expected: " + expected + ", but got: " + input[i] + "."); break; } } } public static void main(String[] args) { testSort(); } } We can test out our test by creating a blank `Sort.sort` method as shown below: If we run the `testSort()` method with this blank `Sort.sort` method, we'd get: The fact that we're getting an error message is a good thing! This means our test is working. What's very interesting about this is that we've now created a little game for ourselves to play, where the goal is to modify the code for `Sort.sort` so that this error message no longer occurs. It's a bit of a psychological trick, but many programmers find the creation of these little mini-puzzles for themselves to be almost addictive. In fact, this is a lot like the situation where you have an autograder for a class, and you find yourself hooked on the idea of getting the autograder to give you its love and approval. You now have the ability to create a judge for your code, whose esteem you can only win by completing the code correctly. **Important note:** You may be asking "Why are you looping through the entire array? Why don't you just check if the arrays are equal using `==`? ". The reason is, when we test for equality of two objects, we cannot simply use the `==` operator. The `==` operator compares the literal bits in the memory boxes, e.g. `input == expected` would test whether or not the addresses of `input` and `expected` are the same, not whether the values in the arrays are the same. Instead, we used a loop in `testSort`, and print out the first mismatch. You could also use the built-in method `java.util.Arrays.equals` instead of a loop. While the single test above wasn't a ton of work, writing a suite of such _ad hoc_ tests would be very tedious, as it would entail writing a bunch of different loops and print statements. In the next section, we'll see how the `org.junit` library saves us a lot of work. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/7.-testing#junit-testing) JUnit Testing The Google Truth library provides a number of helpful methods and useful capabilities for simplifying the writing of tests. For example, we can replace our simple _ad hoc_ test from above with: **This code is much simpler**, and does more or less the exact same thing, i.e. if the arrays are not equal, it will tell us the first mismatch. For example, if we run `testSort()` on a `Sort.sort` method that does nothing, we'd get: While this output is a little uglier than our _ad hoc_ test, we'll see at the very end of this chapter how to make it nicer. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/7.-testing#selection-sort) Selection Sort Before we can write a `Sort.sort` method, we need some algorithm for sorting. Perhaps the simplest sorting algorithm around is "selection sort." Selection sort consists of three steps: * Find the smallest item. * Move it to the front. * Selection sort the remaining N-1 items (without touching the front item). For example, suppose we have the array `{6, 3, 7, 2, 8, 1}`. The smallest item in this array is `1`, so we'd move the `1` to the front. There are two natural ways to do this: One is to stick the `1` at the front and slide all the numbers over, i.e. `{1, 6, 3, 7, 2, 8}`. However, the much more efficient way is to simply swap the `1` with the old front (in this case `6`), yielding `{1, 3, 7, 2, 8, 6}`. We'd simply repeat the same process for the remaining digits, i.e. the smallest item in `... 3, 7, 2, 8, 6}` is `2`. Swapping to the front, we get `{1, 2, 7, 3, 8, 6}`. Repeating until we've got a sorted array, we'd get `{1, 2, 3, 7, 8, 6}`, then `{1, 2, 3, 6, 8, 7}`, then finally `{1, 2, 3, 6, 7, 8}`. We could mathematically prove the correctness of this sorting algorithm on any arrays by using the concept of invariants that was originally introduced in chapter 2.4, though we will not do so in this textbook. Before proceeding, try writing out your own short array of numbers and perform selection sort on it, so that you can make sure you get the idea. Now that we know how selection sort works, we can write in a few short comments in our blank `Sort.sort` method to guide our thinking: In the following sections, I will attempt to complete an implementation of selection sort. I'll do so in a way that resembles how a student might approach the problem, so **I'll be making a few intentional errors along the way**. These intentional errors are a good thing, as they'll help demonstrate the usefulness of testing. If you spot any of the errors while reading, don't worry, we'll eventually come around and correct them. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/7.-testing#findsmallest) findSmallest The most natural place to start is to write a method for finding the smallest item in a list. As with `Sort.sort`, we'll start by writing a test before we even complete the method. First, we'll create a dummy `findSmallest` method that simply returns some arbitrary value: Obviously this is not a correct implementation, but we've chosen to defer actually thinking about how `findSmallest` works until after we've written a test. Using the `org.junit` library, adding such a test to our `TestSort` class is very easy, as shown below: As with `TestSort.testsort`, we then run our `TestSort.testFindSmallest` method to make sure that it fails. When we run this test, we'll see that it actually passes, i.e. no message appears. This is because we just happened to hard code the correct return value `x[2]`. Let's modify our `findSmallest` method so that it returns something that is definitely incorrect: After making this change, when we run `TestSort.testFindSmallest`, we'll get an error, which is a good thing: As before, we've set up for ourselves a little game to play, where our goal is now to modify the code for `Sort.findSmallest` so that this error no longer appears. This is a smaller goal than getting `Sort.sort` to work, which might be even more addictive. Side note: It might have seem rather contrived that I just happened to return the right value `x[2]`. However, when I was recording this lecture video, I actually did make this exact mistake without intending to do so! Next we turn to actually writing `findSmallest`. This seems like it should be relatively straightforward. If you're a Java novice, you might end up writing code that looks something like this: However, this will yield the compilation error "< cannot be applied to 'java.lang.String'". The issue is that Java does not allow comparisons between Strings using the < operator. When you're programming and get stuck on an issue like this that is easily describable, it's probably best to turn to a search engine. For example, we might search "less than strings Java" with Google. Such a search might yield a Stack Overflow post like [this onearrow-up-right](https://stackoverflow.com/questions/5153496/how-can-i-compare-two-strings-in-java-and-define-which-of-them-is-smaller-than-t) . One of the popular answers for this post explains that the `str1.compareTo(str2)` method will return a negative number if `str1 < str2`, 0 if they are equal, and a positive number if `str1 > str2`. Incorporating this into our code, we might end up with: Note that we've used a `@source` tag in order to cite our sources. I'm showing this by example for those of you who are taking 61B as a formal course. This is not a typical real world practice. Since we are using syntax features that are totally new to us, we might lack confidence in the correctness of our `findSmallest` method. Luckily, we just wrote that test a little while ago. If we try running it, we'll see that nothing gets printed, which means our code is probably correct. We can augment our test to increase our confidence by adding more test cases. For example, we might change `testFindSmallest` so that it reads as shown below: Rerunning the test, we see that it still passes. We are not absolutely certain that it works, but we are much more certain that we would have been without any tests. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/7.-testing#swap) Swap Looking at our `sort` method below, the next helper method we need to write is something to move an item to the front, which we'll call `swap`. Writing a `swap` method is very straightforward, and you've probably done so before. A correct implementation might look like: However, for the moment, let's introduce an intentional error so that we can demonstrate the utility of testing. A more naive programmer might have done something like: Writing a test for this method is quite easy with the help of JUnit. An example test is shown below. Note that we have also edited the main method so that it calls `testSwap` instead of `testFindSmallest` or `testSort`. Running this test on our buggy `swap` yields an error, as we'd expect. It's worth briefly noting that it is important that we call only `testSwap` and not `testSort` as well. For example, if our `main` method was as below, the entire `main` method will terminate execution as soon as `testSort` fails, and `testSwap` will never run: We will learn a more elegant way to deal with multiple tests at the end of this chapter that will avoid the need to manually specify which tests to run. Now that we have a failing test, we can use it to help us debug. One way to do this is to set a breakpoint inside the `swap` method and use the visual debugging feature in IntelliJ. If you would like more information about and practice on debugging, check out [Lab3arrow-up-right](https://sp19.datastructur.es/materials/lab/lab3/lab3) . Stepping through the code line-by-line makes it immediately clear what is wrong (see video or try it yourself), and we can fix it by updating our code to include a temporary variable as that the beginning of this section: Rerunning the test, we see that it now passes. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/7.-testing#revising-findsmallest) Revising findSmallest Now that we have multiple pieces of our method done, we can start trying to connect them up together to create a `Sort` method. It's clear how to use our `findSmallest` and `swap` methods, but when we do so, we immediately realize there is a bit of a mismatch: `findSmallest` returns a `String`, and `swap` expects two indices. In other words, what `findSmallest` should have been returning is the index of the smallest String, not the String itself. Making silly errors like this is normal and really easy to do, so don't sweat it if you find yourself doing something similar. Iterating on a design is part of the process of writing code. Luckily, this new design can be easily changed. We simply need to adjust `findSmallest` to return an `int`, as shown below: Since this is a non-trivial change, we should also update `testFindSmallest` and make sure that `findSmallest` still works. After modifying `TestSort` so that this test is run, and running `TestSort.main`, we see that our code passes the tests. Now, revising sort, we can fill in the first two steps of our sorting algorithm. All that's left is to somehow selection sort the remaining items, perhaps using recursion. We'll tackle this in the next section. Reflecting on what we've accomplished, it's worth noting how we created tests first, and used these to build confidence that the actual methods work before we ever tried to use them for anything. This is an incredibly important idea, and one that will serve you well if you decide to adopt it. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/7.-testing#recursive-helper-methods) Recursive Helper Methods To begin this section, consider how you might make the recursive call needed to complete `sort`: For those of you who are used to a language like Python, it might be tempting to try and use something like slice notation, e.g. However, there is no such thing in Java as a reference to a sub-array, i.e. we can't just pass the address of the next item in the array. This problem of needing to consider only a subset of a larger array is very common. A typical solution is to create a private helper method that has an additional parameter (or parameters) that delineate which part of the array to consider. For example, we might write a private helper method also called `sort` that consider only the items starting with item `start`. Unlike our public sort method, it's relatively straightforward to use recursion now that we have the additional parameter `start`, as shown below. We'll test this method in the next section. Now that we have a helper method, we need to set up the correct original call. If we set the start to 0, we effectively sort the entire array. This approach is quite common when trying to use recursion on a data structure that is not inherently recursive, e.g. arrays. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/7.-testing#debugging-and-completing-sort) Debugging and Completing Sort Running our `testSort` method, we immediately run into a problem: Using the Java debugger, we see that the problem is that somehow `start` is reaching the value 4. Stepping through the code carefully (see video above), we find that the issue is that we forgot to include a base case in our recursive `sort` method. Fixing this is straightforward: Rerunning this test again, we get another error: Again, with judicious use of the IntelliJ debugger (see video), we can identify a line of code whose result does not match our expectations. Of note is the fact that I debugged the code at a higher level of abstraction than you might have otherwise, which I achieve by using `Step Over` more than `Step Into`. As discussed in lab 3, debugging at a higher level of abstraction saves you a lot of time and energy, by allowing you to compare the results of entire function calls with your expectation. Specifically, we find that when sorting the last 3 (out of 4) items, the `findSmallest` method is giving as the 0th item (`"an"`) rather than the 3rd item (`"egg"`) when called on the input `{"an", "have", "i", "egg"}`. Looking carefully at the definition of `findSmallest`, this behavior is not a surprise, since `findSmallest` looks at the entire array, not just the items starting from position `start`. This sort of design flaw is very common, and writing tests and using the debugger is a great way to go about fixing them. To fix our code, we revise `findSmallest` so that it takes a second parameter `start`, i.e. `findSmallest(String[] x, int start)`. In this way, we ensure that we're finding the smallest item only out of the last however many are still unsorted. The revision is as shown below: Given that we've made a significant change to one of our building blocks, i.e. `findSmallest`, we should ensure that our changes are correct. We first modify `testFindSmallest` so that it uses our new parameter, as shown below: We then modify `TestSort.main` so that it runs `testFindSmallest`. This test passes, strongly suggesting that our revisions to `findSmallest` were correct. We next modify `Sort.sort` so that it uses the new `start` parameter in `findSmallest`: We then modify `TestSort` so that it runs `TestSort.sort` and voila, the method works. We are done! You have now seen the "new way" from the beginning of this lecture, which we'll reflect on for the remainder of this chapter. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/7.-testing#reflections-on-the-development-process) Reflections on the Development Process When you're writing and debugging a program, you'll often find yourself switching between different contexts. Trying to hold too much in your brain at once is a recipe for disaster at worst, and slow progress at best. Having a set of automated tests helps reduce this cognitive load. For example, we were in the middle of writing `sort` when we realized there was a bug in `findSmallest`. We were able to switch contexts to consider `findSmallest` and establish that it was correct using our `testFindSmallest` method, and then switch back to `sort`. This is in sharp contrast to a more naive approach where you would simply be calling `sort` over and over and trying to figure out if the behavior of the overall algorithm suggests that the `findSmallest` method is correct. As an analogy, you could test that a parachute's ripcord works by getting in an airplane, taking off, jumping out, and pulling the ripcord and seeing if the parachute comes out. However, you could also just pull it on the ground and see what happens. So, too, is it unnecessary to use `sort` to try out `findSmallest`. As mentioned earlier in this chapter, tests also allow you to gain confidence in the basic pieces of your program, so that if something goes wrong, you have a better idea of where to start looking. Lastly, tests make it easier to refactor your code. Suppose you decide to rewrite `findSmallest` so that it is faster or more readable. We can safely do so by making our desired changes and seeing if the tests still work. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/7.-testing#more-testing-features) More Testing Features If we add `@Test` before a method AND make the function non-static, green arrows appear. * The single green arrow by testSort means “run this function”. * The double green arrow means run all tests in this class. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Flh7-us.googleusercontent.com%2FAxG53TwAIPggHLPD_XgOBQkOXuaAfUs5lyTY7_WBhpeS8EmNiNFppveTHIXU0HUgji_NxgpI6v6wAfGJfQuMDoRkSFm78_fL5PT_1QkjfiEyrTv7GSIPhWOSX9P_RhKgw6XKc_-M0K-992_ktzGMtEokSQ%3Ds2048&width=300&dpr=3&quality=100&sign=4a532ae0&sv=2) The reason why the function has to be non-static is unclear, though this probably has to do with things happening behind the scene. One added benefit of doing this is that IntelliJ will now gamify bug fixing and design. You have concrete mini-goals and your progress is summarized in bottom left. You win when you get green checks for every test. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Flh7-us.googleusercontent.com%2FBRu_eKYg7yRI7jAqgGXnHzN4CvNiKWtVMq-SbDC9vzjS2Vd8rNbGB-GBUWcGxyinejZWzO-dmWbJ2WGa3jRDpJ56yPXHExjkUJYfCL46V7stzGSFT2dOO0FsrIQqgOLdE6iFfrVvCfr0n6IN1EjQcRXYnw%3Ds2048&width=300&dpr=3&quality=100&sign=7eb65227&sv=2) #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/7.-testing#testing-philosophy) Testing Philosophy **Correctness Tool #1: Autograder** Let's go back to ground zero. The autograder was likely the first correctness tool you were exposed to. Our autograder is in fact based on JUnit plus some extra custom libraries. There are some great benefits to autograders. Perhaps most importantly, it verifies correctness for you, saving you from the tedious and non-instructive task of writing all of your own tests. It also gamifies the assessment process by providing juicy points as an incentive to acheiving correctness. This can also backfire if students spend undue amounts of time chasing final points that won't actually affect their grade or learning. However, autograders don't exist in the real world and relying on autograders can build bad habits. One's workflow is hindered by sporadically uploading your code and waiting for the autograder to run. _Autograder Driven Development_ is an extreme version of this in which students write all their code, fix their compiler errors, and then submit to the autograder. After getting back errors, students may try to make some changes, sprinkle in print statements, and submit again. And repeat. Ultimately, you are not in control of either your workflow or your code if you rely on an autograder. **Correctness Tool #2: JUnit Tests** JUnit testing, as we have seen, unlocks a new world for you. Rather than relying on an autograder written by someone else, you write tests for each piece of your program. We refer to each of these pieces as a unit. This allows you to have confidence in each unit of your code - you can depend on them. This also helps decrease debugging time as you can isolate attention to one unit of code at a time (often a single method). Unit testing also forces you to clarify what each unit of code should be accomplishing. There are some downsides to unit tests, however. First, writing thorough tests takes time. It's easy to write incomplete unit tests which give a false confidence to your code. It's also difficult to write tests for units that depend on other units (consider the `addFirst` method in your `LinkedListDeque`). _**Test-Driven Development (TDD)**_ TDD is a development process in which we write tests for code before writing the code itself. The steps are as follows: 1. Identify a new feature. 2. Write a unit test for that feature. 3. Run the test. It should fail. 4. Write code that passes the test. Yay! 5. Optional: refactor code to make it faster, cleaner, etc. Except now we have a reference to tests that should pass. Test-Driven Development is not required in this class and may not be your style but unit testing in general is most definitely a good idea. **Correctness Tool #3: Integration Testing** Unit tests are great but we should also make sure these units work properly together ([unlike this memearrow-up-right](https://media.giphy.com/media/3o7rbPDRHIHwbmcOBy/giphy.gif) ). Integration testing verifies that components interact properly together. JUnit can in fact be used for this. You can imagine unit testing as the most nitty gritty, with integration testing a level of abstraction above this. The challenge with integration testing is that it is tedious to do manually yet challenging to automate. And at a high level of abstraction, it's easy to miss subtle or rare errors. As a summary, you should **definitely write tests but only when they might be useful!** Taking inspiration from TDD, writing your tests before writing code can also be very helpful in some cases. [Previous6\. Arrayschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/6.-arrays) [Next8\. ArrayListchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/8.-arraylist) Last updated 2 years ago sun-brightdesktopmoon Copy public class Sort { /** Sorts strings destructively. */ public static void sort(String[] x) { } } Copy Mismatch in position 0, expected: an, but got: i. Copy import static com.google.common.truth.Truth.assertThat; public class TestSort { /** Tests the sort method of the Sort class. */ public static void testSort() { String[] input = {"cows", "dwell", "above", "clouds"}; String[] expected = {"above", "clouds", "cows", "dwell"}; Sort.sort(input); assertThat(input).isEqualTo(expected); } public static void main(String[] args) { testSort(); } } Copy Exception in thread "main" arrays first differed at element [0]; expected:<[an]> but was:<[i]> at org.junit.internal.ComparisonCriteria.arrayEquals(ComparisonCriteria.java:55) at org.junit.Assert.internalArrayEquals(Assert.java:532) ... Copy public class Sort { /** Sorts strings destructively. */ public static void sort(String[] x) { // find the smallest item // move it to the front // selection sort the rest (using recursion?) } } Copy public class Sort { /** Sorts strings destructively. */ public static void sort(String[] x) { // find the smallest item // move it to the front // selection sort the rest (using recursion?) } /** Returns the smallest string in x. */ public static String findSmallest(String[] x) { return x[2]; } } Copy public class TestSort { @Test public void testFindSmallest() { String[] input = {"rawr", "a", "zaza", "newway"}; String expected = "zaza"; String actual = Sort.findSmallest(input); assertThat(actual).isEqualTo(expected); } } Copy /** Returns the smallest string in x. */ public static String findSmallest(String[] x) { return x[3]; } Copy Exception in thread "main" java.lang.AssertionError: expected:<[an]> but was:<[null]> at org.junit.Assert.failNotEquals(Assert.juava:834) at TestSort.testFindSmallest(TestSort.java:9) at TestSort.main(TestSort.java:24) Copy /** Returns the smallest string in x. */ public static String findSmallest(String[] x) { String smallest = x[0]; for (int i = 0; i < x.length; i += 1) { if (x[i] < smallest) { smallest = x[i]; } } return smallest; } Copy /** Returns the smallest string in x. * @source Got help with string compares from https://goo.gl/a7yBU5. */ public static String findSmallest(String[] x) { String smallest = x[0]; for (int i = 0; i < x.length; i += 1) { int cmp = x[i].compareTo(smallest); if (cmp < 0) { smallest = x[i]; } } return smallest; } Copy public static void testFindSmallest() { String[] input = {"i", "have", "an", "egg"}; String expected = "an"; String actual = Sort.findSmallest(input); assertThat(actual).isEqualTo(expected); String[] input2 = {"there", "are", "many", "pigs"}; String expected2 = "are"; String actual2 = Sort.findSmallest(input2); assertThat(actual2).isEqualTo(expected2); } Copy /** Sorts strings destructively. */ public static void sort(String[] x) { // find the smallest item // move it to the front // selection sort the rest (using recursion?) } Copy public static void swap(String[] x, int a, int b) { String temp = x[a]; x[a] = x[b]; x[b] = temp; } Copy public static void swap(String[] x, int a, int b) { x[a] = x[b]; x[b] = x[a]; } Copy public class TestSort { ... /** Test the Sort.swap method. */ public static void testSwap() { String[] input = {"i", "have", "an", "egg"}; int a = 0; int b = 2; String[] expected = {"an", "have", "i", "egg"}; Sort.swap(input, a, b); assertThat(expected).isEqualTo(input); } public static void main(String[] args) { testSwap(); } } Copy Exception in thread "main" arrays first differed in element [2]; expected:<[i]> but was:<[an]> at TestSort.testSwap(TestSort.java:36) Copy public static void main(String[] args) { testSort(); testFindSmallest(); testSwap(); } Copy public static void swap(String[] x, int a, int b) { String temp = x[a]; x[a] = x[b]; x[b] = temp; } Copy /** Sorts strings destructively. */ public static void sort(String[] x) { // find the smallest item // move it to the front // selection sort the rest (using recursion?) } Copy /** Sorts strings destructively. */ public static void sort(String[] x) { // find the smallest item String smallest = findSmallest(x); // move it to the front swap(x, 0, smallest); // selection sort the rest (using recursion?) } Copy public static int findSmallest(String[] x) { int smallestIndex = 0; for (int i = 0; i < x.length; i += 1) { int cmp = x[i].compareTo(x[smallestIndex]); if (cmp < 0) { smallestIndex = i; } } return smallestIndex; } Copy public static void testFindSmallest() { String[] input = {"i", "have", "an", "egg"}; int expected = 2; int actual = Sort.findSmallest(input); assertThat(actual).isEqualTo(expected); String[] input2 = {"there", "are", "many", "pigs"}; int expected2 = 1; int actual2 = Sort.findSmallest(input); assertThat(actual2).isEqualTo(expected2); } Copy /** Sorts strings destructively. */ public static void sort(String[] x) { // find the smallest item // move it to the front // selection sort the rest (using recursion?) int smallestIndex = findSmallest(x); swap(x, 0, smallestIndex); } Copy /** Sorts strings destructively. */ public static void sort(String[] x) { int smallestIndex = findSmallest(x); swap(x, 0, smallestIndex); // recursive call?? } Copy /** Sorts strings destructively. */ public static void sort(String[] x) { int smallestIndex = findSmallest(x); swap(x, 0, smallestIndex); sort(x[1:]) } Copy /** Sorts strings destructively starting from item start. */ private static void sort(String[] x, int start) { // TODO } Copy /** Sorts strings destructively starting from item start. */ private static void sort(String[] x, int start) { int smallestIndex = findSmallest(x); swap(x, start, smallestIndex); sort(x, start + 1); } Copy /** Sorts strings destructively. */ public static void sort(String[] x) { sort(x, 0); } Copy Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 4 at Sort.swap(Sort.java:16) Copy /** Sorts strings destructively starting from item start. */ private static void sort(String[] x, int start) { if (start == x.length) { return; } int smallestIndex = findSmallest(x); swap(x, start, smallestIndex); sort(x, start + 1); } Copy Exception in thread "main" arrays first differed at element [0]; expected<[an]> bit was:<[have]> Copy public static int findSmallest(String[] x, int start) { int smallestIndex = start; for (int i = start; i < x.length; i += 1) { int cmp = x[i].compareTo(x[smallestIndex]); if (cmp < 0) { smallestIndex = i; } } return smallestIndex; } Copy public static void testFindSmallest() { String[] input = {"i", "have", "an", "egg"}; int expected = 2; int actual = Sort.findSmallest(input, 0); assertThat(actual).isEqualTo(expected); String[] input2 = {"there", "are", "many", "pigs"}; int expected2 = 2; int actual2 = Sort.findSmallest(input2, 2); assertThat(actual2).isEqualTo(expected2); } Copy /** Sorts strings destructively starting from item start. */ private static void sort(String[] x, int start) { if (start == x.length) { return; } int smallestIndex = findSmallest(x, start); swap(x, start, smallestIndex); sort(x, start + 1); } sun-brightdesktopmoon --- # 1.2 Java Workflow | CS61B Textbook Taking a program from a `.java` file into an executable has two main steps in Java: **compilation** and **interpretation**. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FhedhIwHW0XpvKamJ52mc%252F1-2-compile-interpret.svg%3Falt%3Dmedia%26token%3Da953a222-d3a8-4f12-9053-5d01e0ec67c8&width=768&dpr=3&quality=100&sign=8f8415b2&sv=2) To run the code in `Hello.java`, we would first **compile** the code into a `.class` file using the command `javac HelloWorld.java`. Then, to run the code, we would use the command `java HelloWorld`. In your terminal, the result would look like the following: [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/1.-introduction/1.2-java-workflow#class-files) Class Files ------------------------------------------------------------------------------------------------------------------- There are several reasons for the usage of `.class` files, which we will only cover briefly here. First of all, `.class` files are guaranteed to have been type-checked, making the distributed code safer. They are also more efficient to execute, and protect the actual source code in cases of intellectual property. We will not go into the details of `.class` files in this textbook beyond knowing that they are created after compilation. [Previous1.1 Your First Java Programchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/1.-introduction/1.1-your-first-java-program) [Next1.3 Basic Java Featureschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/1.-introduction/1.3-basic-java-features) Last updated 2 years ago sun-brightdesktopmoon Copy $ javac HelloWorld.java $ java HelloWorld Hello World! sun-brightdesktopmoon --- # 14. Disjoint Sets | CS61B Textbook [14.1 Introductionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/14.-disjoint-sets/14.1-introduction) [14.2 Quick Findchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/14.-disjoint-sets/14.2-quick-find) [14.3 Quick Unionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/14.-disjoint-sets/14.3-quick-union) [14.4 Weighted Quick Union (WQU)chevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/14.-disjoint-sets/14.4-weighted-quick-union-wqu) [14.5 Weighted Quick Union with Path Compressionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/14.-disjoint-sets/14.5-weighted-quick-union-with-path-compression) [14.6 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/14.-disjoint-sets/14.6-exercises) [Previous13.10 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/13.-asymptotics-i/13.10-exercises) [Next14.1 Introductionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/14.-disjoint-sets/14.1-introduction) Last updated 3 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 13. Asymptotics I | CS61B Textbook [13.1 An Introduction to Asymptotic Analysischevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/13.-asymptotics-i/13.1-an-introduction-to-asymptotic-analysis) [13.2 Runtime Characterizationchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/13.-asymptotics-i/13.2-runtime-characterization) [13.3 Checkpoint: An Exercisechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/13.-asymptotics-i/13.3-checkpoint-an-exercise) [13.4 Asymptotic Behaviorchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/13.-asymptotics-i/13.4-asymptotic-behavior) [13.6 Simplified Analysis Processchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/13.-asymptotics-i/13.6-simplified-analysis-process) [13.7 Big-Thetachevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/13.-asymptotics-i/13.7-big-theta) [13.8 Big-Ochevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/13.-asymptotics-i/13.8-big-o) [13.9 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/13.-asymptotics-i/13.9-summary) [13.10 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/13.-asymptotics-i/13.10-exercises) [Previous12.6 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/12.-inheritance-iv-iterators-object-methods/12.6-exercises) [Next13.1 An Introduction to Asymptotic Analysischevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/13.-asymptotics-i/13.1-an-introduction-to-asymptotic-analysis) Last updated 3 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 1.1 Your First Java Program | CS61B Textbook The classic first program when introducing any new language is **Hello World**, or a program that prints `Hello World` to the console. In Java, **Hello World** can be written as such: Copy public class HelloWorld { public static void main(String[] args) { System.out.println("Hello world!"); } } As compared to other languages like Python, this may seem needlessly verbose. However, there are several reasons for the verbosity of Java, which will be covered in the next few chapters. For now, notice some key syntatical features of the code snippet above: * The **class declaration** `public class HelloWorld`: in Java, all code lives within classes. * The `**main**` function: all the code that runs must be inside of a method declared as `public static void main(String[] args)`. Future chapters will cover the exact meaning of this declaration. * **Curly braces** `{}` enclose sections of code (functions, classes, and other types of code that will be covered in future chapters). * All statements must end with a **semi-colon**. For fun, see [Hello world! in other languagesarrow-up-right](https://www.rosettacode.org/wiki/Hello_world/Text) . [Previous1\. Introductionchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/1.-introduction) [Next1.2 Java Workflowchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/1.-introduction/1.2-java-workflow) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 15. Asymptotics II | CS61B Textbook This chapter covers various asymptotic analysis examples, which provide useful insights on how to analyze the efficiency of algorithms. [Previous14.6 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/14.-disjoint-sets/14.6-exercises) [Next15.1 For Loopschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/15.-asymptotics-ii/15.1-for-loops) Last updated 3 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 16. ADTs and BSTs | CS61B Textbook In this Chapter we will discuss: * Abstract Data Types * Binary Search Tree * BST Definitions * BST Operations * Sets vs. Maps, Summary Additionally the video playlist is one that accompanies this chapter is the following: [Previous15.6 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/15.-asymptotics-ii/15.6-exercises) [Next16.1 Abstract Data Typeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/16.-adts-and-bsts/16.1-abstract-data-types) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 1.3 Basic Java Features | CS61B Textbook #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/1.-introduction/1.3-basic-java-features#variables-and-loops) Variables and Loops The program below will print out the integers from 0 through 9. When we run this program, we see: Some interesting features of this program that might jump out at you: * Our variable x must be declared before it is used, _and it must be given a type!_ * Our loop definition is contained inside of curly braces, and the boolean expression that is tested is contained inside of parentheses. * Our print statement is just `System.out.print` instead of `System.out.println`. This means we should not include a newline (a return). * Our print statement adds a number to a space. This makes sure the numbers don't run into each other. Try removing the space to see what happens. * When we run it, our prompt ends up on the same line as the numbers (which you can fix in the following exercise if you'd like). Of these features the most important one is the fact that variables have a declared type. We'll come back to this in a bit, but first, an exercise. **Exercise 1.1.2.** Modify `HelloNumbers` so that it prints out the cumulative sum of the integers from 0 to 9. For example, your output should start with 0 1 3 6 10... and should end with 45. Also, if you've got an aesthetic itch, modify the program so that it prints out a new line at the end. The program below will print out the integers from 0 through 9. When we run this program, we see: Some interesting features of this program that might jump out at you: * Our variable x must be declared before it is used, _and it must be given a type!_ * Our loop definition is contained inside of curly braces, and the boolean expression that is tested is contained inside of parentheses. * Our print statement is just `System.out.print` instead of `System.out.println`. This means we should not include a newline (a return). * Our print statement adds a number to a space. This makes sure the numbers don't run into each other. Try removing the space to see what happens. * When we run it, our prompt ends up on the same line as the numbers (which you can fix in the following exercise if you'd like). Of these features the most important one is the fact that variables have a declared type. We'll come back to this in a bit, but first, an exercise. **Exercise 1.1.2.** Modify `HelloNumbers` so that it prints out the cumulative sum of the integers from 0 to 9. For example, your output should start with 0 1 3 6 10... and should end with 45. Also, if you've got an aesthetic itch, modify the program so that it prints out a new line at the end. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/1.-introduction/1.3-basic-java-features#code-style-comments-javadoc) Static Typing Java is a **statically typed language**, which means that all variables, parameters, and methods must have a declared type. After declaration, _the type can never change_. Expressions also have an implicit type; for example, the expression `3 + 5` has type `int`. Because all types are declared statically, the compiler checks that types are compatible before the program even runs. This means that expressions with an incompatible type will fail to compile instead of crashing the program at runtime. The advantages of static typing include: * catching type errors earlier in the coding process, reducing the debugging burden on the programmer. * avoiding type errors for end users. * making it easier to read and reason about code. * avoiding expensive runtime type checks, making code more efficient. However, static typing also has several disadvantages; namely: * more verbose code. * less generalizable code. One of the most important features of Java is that all variables and expressions have a so-called `static type`. Java variables can contain values of that type, and only that type. Furthermore, the type of a variable can never change. One of the key features of the Java compiler is that it performs a static type check. For example, suppose we have the program below: Compiling this program, we see: The compiler rejects this program out of hand before it even runs. This is a big deal, because it means that there's no chance that somebody running this program out in the world will ever run into a type error! This is in contrast to dynamically typed languages like Python, where users can run into type errors during execution! In addition to providing additional error checking, static types also let the programmer know exactly what sort of object he or she is working with. We'll see just how important this is in the coming weeks. This is one of my personal favorite Java features. To summarize, static typing has the following advantages: * The compiler ensures that all types are compatible, making it easier for the programmer to debug their code. * Since the code is guaranteed to be free of type errors, users of your compiled programs will never run into type errors. For example, Android apps are written in Java, and are typically distributed only as .class files, i.e. in a compiled format. As a result, such applications should never crash due to a type error since they have already been checked by the compiler. * Every variable, parameter, and function has a declared type, making it easier for a programmer to understand and reason about code. However, static typing also has several disadvantages, which will be discussed further in later chapters. To name a few: * More verbose code. * Less generalizable code. **Extra Thought Exercise** In Java, we can say `System.out.println(5 + " ");`. But in Python, we can't say `print(5 + "horse")`, like we saw above. Why is that so? Consider these two Java statements: and The first one of these will succeed; the second will give a compiler error. Since Java is strongly typed, if you tell it `h` is a string, it can concatenate the elements and give you a string. But when `h` is an `int`, it can't concatenate a number and a string and give you a number. Python doesn't constrain the type, and it can't make an assumption for what type you want. Is `x = 5 + "horse"` supposed to be a number? A string? Python doesn't know. So it errors. In this case, `System.out.println(5 + "horse");`, Java interprets the arguments as a string concatentation, and prints out "5horse" as your result. Or, more usefully, `System.out.println(5 + " ");` will print a space after your "5". What does `System.out.println(5 + "10");` print? 510, or 15? How about `System.out.println(5 + 10);`? #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/1.-introduction/1.3-basic-java-features#defining-functions-in-java) Defining Functions in Java In languages like Python, functions can be declared anywhere, even outside of functions. For example, the code below declares a function that returns the larger of two arguments, and then uses this function to compute and print the larger of the numbers 8 and 10: Since all Java code is part of a class, we must define functions so that they belong to some class. Functions that are part of a class are commonly called "methods". We will use the terms interchangably throughout the course. The equivalent Java program to the code above is as follows: The new piece of syntax here is that we declared our method using the keywords `public static`, which is a very rough analog of Python's `def` keyword. We will see alternate ways to declare methods in the next chapter. The Java code given here certainly seems much more verbose! You might think that this sort of programming language will slow you down, and indeed it will, in the short term. Think of all of this stuff as safety equipment that we don't yet understand. When we're building small programs, it all seems superfluous. However, when we get to building large programs, we'll grow to appreciate all of the added complexity. As an analogy, programming in Python can be a bit like [Dan Osman free-soloing Lover's Leaparrow-up-right](https://www.youtube.com/watch?v=NCByLWtM7y4) . It can be very fast, but dangerous. Java, by contrast is more like using ropes, helmets, etc. as in [this videoarrow-up-right](https://www.youtube.com/watch?v=tr6UIfPEuI0) . #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/1.-introduction/1.3-basic-java-features#code-style-comments-javadoc-1) Code Style, Comments, Javadoc Code can be beautiful in many ways. It can be concise. It can be clever. It can be efficient. One of the least appreciated aspects of code by novices is code style. When you program as a novice, you are often single mindedly intent on getting it to work, without regard to ever looking at it again or having to maintain it over a long period of time. In this course, we'll work hard to try to keep our code readable. Some of the most important features of good coding style are: * Consistent style (spacing, variable naming, brace style, etc) * Size (lines that are not too wide, source files that are not too long) * Descriptive naming (variables, functions, classes), e.g. variables or functions with names like `year` or `getUserName` instead of `x` or `f`. * Avoidance of repetitive code: You should almost never have two significant blocks of code that are nearly identical except for a few changes. * Comments where appropriate. Line comments in Java use the `//` delimiter. Block (a.k.a. multi-line comments) comments use `/*` and `*/`. The golden rule is this: Write your code so that it is easy for a stranger to understand. Here is the course's official [style guidearrow-up-right](https://sp19.datastructur.es/materials/guides/style-guide.html) . It's worth taking a look! Often, we are willing to incur slight performance penalties, just so that our code is simpler to [grokarrow-up-right](https://en.wikipedia.org/wiki/Grok) . We will highlight examples in later chapters. **Comments** We encourage you to write code that is self-documenting, i.e. by picking variable names and function names that make it easy to know exactly what's going on. However, this is not always enough. For example, if you are implementing a complex algorithm, you may need to add comments to describe your code. Your use of comments should be judicious. Through experience and exposure to others' code, you will get a feeling for when comments are most appropriate. One special note is that all of your methods and almost all of your classes should be described in a comment using the so-called [Javadocarrow-up-right](https://en.wikipedia.org/wiki/Javadoc) format. In a Javadoc comment, the block comment starts with an extra asterisk, e.g. `/**`, and the comment often (but not always) contains descriptive tags. We won't discuss these tags in this textbook, but see the link above for a description of how they work. As an example without tags: The widely used [javadoc toolarrow-up-right](http://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html) can be used to generate HTML descriptions of your code. We'll see examples in a later chapter. [Previous1.2 Java Workflowchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/1.-introduction/1.2-java-workflow) [Next1.4 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/1.-introduction/1.4-exercises) Last updated 2 years ago sun-brightdesktopmoon Copy public class HelloNumbers { public static void main(String[] args) { int x = 0; while (x < 10) { System.out.print(x + " "); x = x + 1; } } } Copy $ javac HelloNumbers.java $ java HelloNumbers $ 0 1 2 3 4 5 6 7 8 9 Copy public class HelloNumbers { public static void main(String[] args) { int x = 0; while (x < 10) { System.out.print(x + " "); x = x + 1; } } } Copy $ javac HelloNumbers.java $ java HelloNumbers $ 0 1 2 3 4 5 6 7 8 9 Copy public class HelloNumbers { public static void main(String[] args) { int x = 0; while (x < 10) { System.out.print(x + " "); x = x + 1; } x = "horse"; } } Copy $ javac HelloNumbers.java HelloNumbers.java:9: error: incompatible types: String cannot be converted to int x = "horse"; ^ 1 error Copy String h = 5 + "horse"; Copy int h = 5 + "horse"; Copy def larger(x, y): if x > y: return x return y print(larger(8, 10)) Copy public class LargerDemo { public static int larger(int x, int y) { if (x > y) { return x; } return y; } public static void main(String[] args) { System.out.println(larger(8, 10)); } } Copy public class LargerDemo { /** Returns the larger of x and y. */ public static int larger(int x, int y) { if (x > y) { return x; } return y; } public static void main(String[] args) { System.out.println(larger(8, 10)); } } sun-brightdesktopmoon --- # 17. B-Trees | CS61B Textbook In this section, we build off our knowledge of binary search trees to understand a new self-balancing search tree structure: B-Trees. [Previous16.7 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/16.-adts-and-bsts/16.7-exercises) [Next17.1 BST Performancechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/17.-b-trees/17.1-bst-performance) Last updated 3 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 18. Red Black Trees | CS61B Textbook This chapter will continue our discussion on self-balancing trees through a more colorful lens. [Previous17.7 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/17.-b-trees/17.7-exercises) [Next18.1 Rotating Treeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/18.-red-black-trees/18.1-rotating-trees) Last updated 3 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 10.3 Casting | CS61B Textbook ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/10.-inheritance-ii-extends-casting-higher-order-functions/10.3-casting#dynamic-method-selection-and-type-checking-puzzle) Dynamic Method Selection and Type Checking Puzzle **Static vs. Dynamic Type Reminder:** Every variable in Java has a static type. This is the type specified when the variable is declared, and is checked at compile time. Every variable also has a dynamic type; this type is specified when the variable is instantiated, and is checked at runtime. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/10.-inheritance-ii-extends-casting-higher-order-functions/10.3-casting#compile-time-type-checking-and-expressions) Compile-Time Type Checking and Expressions Compiler allows method calls based on compile-time type of variable. The compiler also allows assignments based on compile-time types. Expressions have compile-time types: * An expression using the new keyword has the specified compile-time type. Example: Copy SLList sl = new VengefulSLList(); * Compile-time type of right hand side (RHS) expression is VengefulSLList. * A VengefulSLList is-an SLList, so assignment is allowed. Copy VengefulSLList vsl = new SLList(); * Compile-time type of RHS expression is SLList. * An SLList is not necessarily a VengefulSLList, so compilation error results. Expressions have compile-time types: * Method calls have compile-time type equal to their declared type. Copy public static Dog maxDog(Dog d1, Dog d2) { … } * Any call to maxDog will have compile-time type Dog! Example: * Compilation error! RHS has compile-time type Dog [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/10.-inheritance-ii-extends-casting-higher-order-functions/10.3-casting#casting) Casting ------------------------------------------------------------------------------------------------------------------------------------------------ Java has a special syntax for specifying the compile-time type of any expression. * Put desired type in parenthesis before the expression. * Tells compiler to pretend it sees a particular type. Casting is a powerful but dangerous tool. * Tells Java to treat an expression as having a different compile-time type. * In example below, effectively tells the compiler to ignore its type checking duties. * Does not actually change anything: sunglasses don’t make the world dark. [Previous10.2 Encapsulationchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/10.-inheritance-ii-extends-casting-higher-order-functions/10.2-encapsulation) [Next10.4 Higher Order Functions in Javachevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/10.-inheritance-ii-extends-casting-higher-order-functions/10.4-higher-order-functions-in-java) Last updated 2 years ago * [Dynamic Method Selection and Type Checking Puzzle](https://cs61b-2.gitbook.io/cs61b-textbook/10.-inheritance-ii-extends-casting-higher-order-functions/10.3-casting#dynamic-method-selection-and-type-checking-puzzle) * [Compile-Time Type Checking and Expressions](https://cs61b-2.gitbook.io/cs61b-textbook/10.-inheritance-ii-extends-casting-higher-order-functions/10.3-casting#compile-time-type-checking-and-expressions) * [Casting](https://cs61b-2.gitbook.io/cs61b-textbook/10.-inheritance-ii-extends-casting-higher-order-functions/10.3-casting#casting) sun-brightdesktopmoon Copy Poodle frank = new Poodle("Frank", 5); Poodle frankJr = new Poodle("Frank Jr.", 15); Dog largerDog = maxDog(frank, frankJr); Poodle largerPoodle = maxDog(frank, frankJr); sun-brightdesktopmoon --- # 11.1 A Review of Dynamic Method Selection | CS61B Textbook In previous lectures we have gone over classes extending other classes, and to make sense of this we considered whether the sub class has an "is-a relationship" with the superclass. For example, if we had the two classes: * Dog: Implements bark() method * ShowDog: Extends Dog, overrides bark method This would be a valid extension as a ShowDog is-a Dog and a Dog is-an Object. These relationships satisfy the condition of a subclass being-an instance of the super class. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FnAJGy8b3QssQgxGfwWyG%252Fimage.png%3Falt%3Dmedia%26token%3D2087b4a9-cddf-42f6-a59d-97343ad80439&width=300&dpr=3&quality=100&sign=d2250072&sv=2) [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.1-a-review-of-dynamic-method-selection#with-this-in-mind-what-are-the-rules-to-decide-if-the-code-would-run-or-run-into-errors) With this in Mind, what are the Rules to Decide if the Code Would Run or Run into Errors? ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- This is a particularly tricky problem to solve but the rules to look out for include: * Compliers will allow Memory Boxes to hold any subtype of itself * For example, compliers will allow the Dog memory box to hold a ShowDog object as a ShowDog is a subtype of the Dog Class * Compliers will allow calls based on Static type. * For example, if a variable were declared as a Dog it's static type would be a Dog and the complier would allowed it to call bark() * **Overridden non-static methods are selected at run time based on dynamic type.** * **Everything else is based on static types,** inculding overloaded methods. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.1-a-review-of-dynamic-method-selection#static-type-vs.-dynamic-type) Static Type vs. Dynamic Type ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Every variable in Java has a “compile-time type”, a.k.a. “static type”. * This is the type specified at declaration. Never changes! Variables also have a “run-time type”, a.k.a. “dynamic type”. * This is the type specified at instantiation (e.g. when using new). * Equal to the type of the object being pointed at. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.1-a-review-of-dynamic-method-selection#accompanying-lecture) Accompanying Lecture ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [Previous11\. Inheritance III: Subtype Polymorphism, Comparators, Comparablechevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable) [Next11.2 Subtype Polymorphism vs Explicit Higher Order Functionschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.2-subtype-polymorphism-vs-explicit-higher-order-functions) Last updated 3 years ago * [With this in Mind, what are the Rules to Decide if the Code Would Run or Run into Errors?](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.1-a-review-of-dynamic-method-selection#with-this-in-mind-what-are-the-rules-to-decide-if-the-code-would-run-or-run-into-errors) * [Static Type vs. Dynamic Type](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.1-a-review-of-dynamic-method-selection#static-type-vs.-dynamic-type) * [Accompanying Lecture](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.1-a-review-of-dynamic-method-selection#accompanying-lecture) sun-brightdesktopmoon sun-brightdesktopmoon --- # 19.3 "Valid" & "Good" Hashcodes | CS61B Textbook Professor Hug's Lecture on Valid/Good Hashcodes [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/19.-hashing-i/19.3-valid-and-good-hashcodes#valid-hashcodes) Valid Hashcodes! -------------------------------------------------------------------------------------------------------------------------------------- You may see this term in discussions and potentially on exams. What exactly makes a hash code "valid"? There are two properties: 1. _**Deterministic**_**:** The `hashCode()` function of two objects A and B who are equal to each other (`A.equals(B) == true`) have the same hashcode. _This also means the hash function cannot rely on attributes of the object that are not reflected in the_ `.equals()` _method._ 2. _**Consistent**_**:** The `hashCode()` function returns the same integer every time it is called on the same instance of an object. This means the `hashCode()` function must be independent of time/stopwatches, random number generators, or any methods that would not give us a consistent `hashCode()` across multiple `hashCode()` function calls on the same object instance. Note that there are no requirements that state that unequal objects should have different hash function values. One could argue that these two requirements are in fact the same requirement. We can restate the requirement of consistency. Imagine we make a pointer named `A` to an object `O` at 12:00 pm and a pointer named `B` to this same object `O` at 1:00 pm. We know that the hash code should return the same integer for both objects, due to the consistency requirement. However, how do we formally define our statement “this same object `O`” above? Technically, the only reason we consider `B` to be pointing to the same thing as `A` is because of the `.equals()`method! This is starting to sound a lot like the determinism requirement. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/19.-hashing-i/19.3-valid-and-good-hashcodes#good-hashcodes) Good Hashcodes! ------------------------------------------------------------------------------------------------------------------------------------ You'll probably see this term a lot as well. But what makes a hashcode "good"? There are a few properties that can make a good `hashCode()`: 1. The `hashCode()` function must be valid. 2. The `hashCode()` function values should be spread as uniformly as possible over the set of all integers. 3. The `hashCode()` function should be relatively quick to compute \[ideally O(1) constant time mathematical operations\] Now let’s think more specifically about the impact of the hashing function. In general, we assume most hash functions will be “relatively quick”. Why do we make this assumption? Given how intrinsic the hashing function is to our data structure, the runtime of this function will have a significant effect on the overall runtime of our data structure. This means we want our hash code to be “easily” computable (ideally constant time), so that we may maintain the O(1) runtime characteristic that makes hashing so special and efficient! [Previous19.2 Hash Codechevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/19.-hashing-i/19.2-hash-code) [Next19.4 Handling Collisions: Linear Probing and External Chainingchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/19.-hashing-i/19.4-handling-collisions-linear-probing-and-external-chaining) Last updated 1 year ago * [Valid Hashcodes!](https://cs61b-2.gitbook.io/cs61b-textbook/19.-hashing-i/19.3-valid-and-good-hashcodes#valid-hashcodes) * [Good Hashcodes!](https://cs61b-2.gitbook.io/cs61b-textbook/19.-hashing-i/19.3-valid-and-good-hashcodes#good-hashcodes) sun-brightdesktopmoon sun-brightdesktopmoon --- # 11.6 Exercises | CS61B Textbook [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.6-exercises#exercises) Exercises ------------------------------------------------------------------------------------------------------------------------------------------------------------ 1. Which of the following are examples of subtype polymorphism? * Having a `sqrt(int)` and `sqrt(double)` method in the same class. * Having a `Dog` override the `makeSound` method that it inherits from `Animal`. * Creating a class that implements the built-in `Comparable` interface. 2. How would you compare two strings alphabetically in Java? 3. Suppose we correctly define a `Comparator` class called `SixComparator` for integers that compares them based on the number of `6`s they contain. What will the code `(new SixComparator()).compare(12345678, 45666678)` return? 4. What is the main difference between the `Comparable` and `Comparator` interfaces? [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.6-exercises#solutions) Solutions ------------------------------------------------------------------------------------------------------------------------------------------------------------ chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.6-exercises#problem-1) **Having a** `**Dog**` **override the** `**makeSound**` **method that it inherits from** `**Animal**`**.** Overriding `makeSound` allows us to have different implementations of the same method via subtypes. **Creating a class that implements the built-in** `**Comparable**` **interface.** Implementing the Comparable interface involves overriding Comparables’ methods, allowing for differing behavior of the same method across types. chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.6-exercises#problem-2) `s1.compareTo(s2)` chevron-rightProblem 3[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.6-exercises#problem-3) It will return some negative number, since `12345678` has less `6`'s than `45666678`. Note that there is no guarantee of what the negative number's value is, only that it is less than 0. chevron-rightProblem 4[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.6-exercises#problem-4) An object that implements `Comparable` can compare another object to itself, whereas a Comparator compares two objects other than itself. A good way to remember this is that a `Comparable` has an inherent property of being _able_ _to be compared_, while a `Comparator` is an external source of truth. [Previous11.5 Chapter Summarychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.5-chapter-summary) [Next12\. Inheritance IV: Iterators, Object Methodschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/12.-inheritance-iv-iterators-object-methods) Last updated 3 years ago * [Exercises](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.6-exercises#exercises) * [Solutions](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.6-exercises#solutions) sun-brightdesktopmoon sun-brightdesktopmoon --- # 19. Hashing I | CS61B Textbook [19.1 Introduction to Hashing: Data Indexed Arrayschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/19.-hashing-i/19.1-introduction-to-hashing-data-indexed-arrays) [19.2 Hash Codechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/19.-hashing-i/19.2-hash-code) [19.3 "Valid" & "Good" Hashcodeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/19.-hashing-i/19.3-valid-and-good-hashcodes) [19.4 Handling Collisions: Linear Probing and External Chainingchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/19.-hashing-i/19.4-handling-collisions-linear-probing-and-external-chaining) [19.5 Resizing & Hash Table Performancechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/19.-hashing-i/19.5-resizing-and-hash-table-performance) [19.6 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/19.-hashing-i/19.6-summary) [19.7 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/19.-hashing-i/19.7-exercises) [Previous18.6 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/18.-red-black-trees/18.6-exercises) [Next19.1 Introduction to Hashing: Data Indexed Arrayschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/19.-hashing-i/19.1-introduction-to-hashing-data-indexed-arrays) Last updated 1 year ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 21. Heaps and Priority Queues | CS61B Textbook [21.1 Priority Queueschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/21.-heaps-and-priority-queues/21.1-priority-queues) [21.2 Heapschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/21.-heaps-and-priority-queues/21.2-heaps) [21.3 PQ Implementationchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/21.-heaps-and-priority-queues/21.3-pq-implementation) [21.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/21.-heaps-and-priority-queues/21.4-summary) [21.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/21.-heaps-and-priority-queues/21.5-exercises) [Previous20.4 Mutable vs. Immutable Typeschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/20.-hashing-ii/20.4-mutable-vs.-immutable-types) [Next21.1 Priority Queueschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/21.-heaps-and-priority-queues/21.1-priority-queues) Last updated 3 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 11.2 Subtype Polymorphism vs Explicit Higher Order Functions | CS61B Textbook We've seen how inheritance lets us reuse existing code in a superclass while implementing small modifications by overriding a superclass's methods or writing brand new methods in the subclass. Inheritance also makes it possible to design general data structures and methods using _polymorphism_. Polymorphism, at its core, means 'many forms'. In Java, polymorphism refers to how objects can have many forms or types. In object-oriented programming, polymorphism relates to how an object can be regarded as an instance of its own class, an instance of its superclass, an instance of its superclass's superclass, and so on. Consider a variable `deque` of static type Deque. A call to `deque.addFirst()` will be determined at the time of execution, depending on the run-time type, or dynamic type, of `deque` when `addFirst` is called. As we saw in the last chapter, Java picks which method to call using dynamic method selection. Suppose we want to write a python program that prints a string representation of the larger of two objects. There are two approaches to this. 1. Explicit HoF Approach Copy def print_larger(x, y, compare, stringify): if compare(x, y): return stringify(x) return stringify(y) 1. Subtype Polymorphism Approach Copy def print_larger(x, y): if x.largerThan(y): return x.str() return y.str() Using the explicit higher order function approach, you have a common way to print out the larger of two objects. In contrast, in the subtype polymorphism approach, the object _itself_ makes the choices. The `largerFunction` that is called is dependent on what x and y actually are. [Previous11.1 A Review of Dynamic Method Selectionchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.1-a-review-of-dynamic-method-selection) [Next11.3 Comparableschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.3-comparables) Last updated 3 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 12.2 Exceptions | CS61B Textbook In this section, we will learn how to throw exceptions to effectively handle errors that may arise in our code. Our `ArraySet` implementation from the previous section has a small error. When we add `null` to our ArraySet, we get a NullPointerException. The probelm lies in the `contains` method where we check `items[i].equals(x)`. If the value at `items[i]` is null, then we are calling `null.equals(x)` -> NullPointerException. Exceptions cause normal flow of control to stop. We can in fact choose to throw our own exceptions. In python you may have seen this with the `raise` keyword. In Java, Exceptions are objects and we throw exceptions using the following format: `throw new ExceptionObject(parameter1, ...)` Let's throw an exception when a user tries to add null to our `ArraySet`. We'll throw an `IllegalArgumentException` which takes in one parameter (a `String` message). Our updated `add` method: We get an Exception either way - why does this better? 1. We have control of our code: we consciously decide at what point to stop the flow of our program 2. More useful Exception type and helpful error message for those using our code However, it would be better if the program doesn't crash at all. There are different things we could do in this case. Here are some below: **Approach 1**: Don't add `null` to the array if it is passed into `add` **Approach 2**: Change the `contains` method to account for the case if `items[i] == null`. Whatever you decide, it is important that users know what to expect. That is why documentation (such as comments about your methods) is very important. [Previous12.1 Lists and Sets in Javachevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/12.-inheritance-iv-iterators-object-methods/12.1-lists-and-sets-in-java) [Next12.3 Iterationchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/12.-inheritance-iv-iterators-object-methods/12.3-iteration) Last updated 3 years ago sun-brightdesktopmoon Copy /* Associates the specified value with the specified key in this map. Throws an IllegalArgumentException if the key is null. */ public void add(T x) { if (x == null) { throw new IllegalArgumentException("can't add null"); } if (contains(x)) { return; } items[size] = x; size += 1; } sun-brightdesktopmoon --- # 11.5 Chapter Summary | CS61B Textbook **Review: Typing Rules** * Compiler allows the memory box to hold any subtype. * Compiler allows calls based on static type. * Overriden non-static methods are selected at runtime based on dynamic type. * For overloaded methods, the method is selected at compile time. **Subtype Polymorphism** Consider a variable of static type `Deque`. The behavior of calling `deque.method()` depends on the dynamic type. Thus, we could have many subclasses the implement the `Deque` interface, all of which will be able to call `deque.method()`. **Subtype Polymorphism Example** Suppose we want to write a function `max()` that returns the max of any array regardless of type. If we write a method `max(Object[] items)`, where we use the ‘>’ operator to compare each element in the array, this will not work! Why is this the case? Well, this makes the assumption that all objects can be compared. But some objects cannot! Alternatively, we could write a `max()` function inside the Dog class, but now we have to write a `max()` function for each class that we want to compare! Remember, our goal is to write a “one true max method” that works for all comparable objects. **Solution: OurComparable Interface** The solution is to create an interface that contains a `compareTo(Object)` method; let’s call this interface `OurComparable`. Now, our `max()` method can take a `OurComparable[]` parameter, and since we guarantee that any object which extends the interface has all the methods inside the interface, we guarantee that we will always be able to call a `compareTo` method, and that this method will correctly return some ordering of the objects. Now, we can specify a “one true max method”. Of course, any object that needs to be compared must implement the `compareTo` method. However, instead of re-implementing the `max` logic in every class, we only need to implement the logic for picking the ordering of the objects, given two objects. **Even Better: Java’s In-Built Comparable** Java has an in-built `Comparable` interface that uses generics to avoid any weird casting issues. Plus, Comparable already works for things like `Integer`, `Character`, and `String`; moreover, these objects have already implemented a `max`, `min`, etc. method for you. Thus you do not need to re-do work that’s already been done! **Comparators** The term “Natural Order” is used to refer to the ordering implied by a `Comparable`’s `compareTo` method. However, what if we want to order our `Dog` objects by something other than `size`? We will instead pass in a `Comparator` interface, which demands a `compare()` method. We can then implement the `compare()` method anyway we want to achieve our ordering. [Previous11.4 Comparatorschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.4-comparators) [Next11.6 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/11.-inheritance-iii-subtype-polymorphism-comparators-comparable/11.6-exercises) Last updated 3 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 13.9 Summary | CS61B Textbook To summarize this chapter: * Given a piece of code, we can express its runtime as a function R(N)R(N)R(N) * NNN is a **property** of the input of the function often representing the **size** of the input * Rather than finding the exact value of R(N)R(N)R(N), we only worry about finding the **order of growth** of R(N)R(N)R(N). * One approach (not universal): * Choose a representative operation * Let C(N)C(N)C(N) be the count of how many times that operation occurs as a function of NNN. * Determine order of growth f(N)f(N)f(N) for C(N)C(N)C(N), i.e. C(N)∈Θ(f(N))C(N)\\in \\Theta(f(N))C(N)∈Θ(f(N)) * Often (but not always) we consider the worst case count. * If operation takes constant time, then R(N)∈Θ(f(N))R(N)\\in \\Theta(f(N))R(N)∈Θ(f(N)). [Previous13.8 Big-Ochevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/13.-asymptotics-i/13.8-big-o) [Next13.10 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/13.-asymptotics-i/13.10-exercises) Last updated 3 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 12.5 Chapter Summary | CS61B Textbook You can find the code from this lecture [herearrow-up-right](https://github.com/Berkeley-CS61B/lectureCode-sp23/tree/main/lec12_inheritance4) . ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/12.-inheritance-iv-iterators-object-methods/12.5-chapter-summary#exceptions) Exceptions Most likely you have encountered an exception in your code such as a `NullPointerException` or an `IndexOutOfBoundsException`. Now we will learn about how we can “throw” exceptions ourselves. Here is an example of an exception that we throw: Copy throw new RuntimeException("For no reason."); This is useful to ensure reasonable functioning of our code, even when facing unexpected behavior. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/12.-inheritance-iv-iterators-object-methods/12.5-chapter-summary#iteration) Iteration #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/12.-inheritance-iv-iterators-object-methods/12.5-chapter-summary#difference-between-iterators-and-iterables) Difference between Iterators and Iterables These two words are very closely related, but have two different meanings that are often easy to confuse. The first thing to know is that these are both Java interfaces, with different methods that need to be implemented. Here is a simplified interface for Iterator: Copy public interface Iterator { boolean hasNext(); T next(); } Here is a simplified interface for Iterable: Copy public interface Iterable { Iterator iterator(); } Notice that in order for an object (for example an ArrayList or LinkedList) to be _iterable_, it must include a method that returns an _iterator_. The iterator is the object that actively steps through an iterable object. Keep this relationship and distinction in mind as you work with these two interfaces. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/12.-inheritance-iv-iterators-object-methods/12.5-chapter-summary#object-methods) Object Methods #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/12.-inheritance-iv-iterators-object-methods/12.5-chapter-summary#tostring) toString The `toString()` method returns a string representation of objects. For example, `System.out.println(someObject)` calls the `toString()` method of `someObject`, and prints to console whatever string it returns. This is most helpful when we are debugging, as it allows us to much more easily understand the current state of our Objects. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/12.-inheritance-iv-iterators-object-methods/12.5-chapter-summary#vs-.equals) \== vs .equals We have two concepts of equality in Java- “==” and the “.equals()” method. The key difference is that when using ==, we are checking if two objects have the same address in memory (that they point to the same instance or object). On the other hand, .equals() is a method that can be overridden by a class and can be used to define some custom way of determining equality. This permits the class to utilize the additional knowledge it has about itself to more accurately answer questions of equality. For example, say we wanted to check if two stones are equal: If we want to consider s and r equal because they have the same weight. If we do check equality using ==, these Stones would not be considered equal because they do not have the same memory address. On the other hand, if you override the equals method of Stone as follows We would have that the stones would be considered equal because they have the same weight. [Previous12.4 Object Methodschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/12.-inheritance-iv-iterators-object-methods/12.4-object-methods) [Next12.6 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/12.-inheritance-iv-iterators-object-methods/12.6-exercises) Last updated 2 years ago * [Exceptions](https://cs61b-2.gitbook.io/cs61b-textbook/12.-inheritance-iv-iterators-object-methods/12.5-chapter-summary#exceptions) * [Iteration](https://cs61b-2.gitbook.io/cs61b-textbook/12.-inheritance-iv-iterators-object-methods/12.5-chapter-summary#iteration) * [Object Methods](https://cs61b-2.gitbook.io/cs61b-textbook/12.-inheritance-iv-iterators-object-methods/12.5-chapter-summary#object-methods) sun-brightdesktopmoon Copy public class Stone{ int weight; public Stone(int weight){ this.weight = weight; } } Stone s = new Stone(100); Stone r = new Stone(100); Copy public boolean equals(Object o){ return this.weight == ((Stone) o).weight } sun-brightdesktopmoon --- # 20. Hashing II | CS61B Textbook [20.1 Hash Table Recap, Default Hash Functionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/20.-hashing-ii/20.1-hash-table-recap-default-hash-function) [20.2 Distribution By Other Hash Functionschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/20.-hashing-ii/20.2-distribution-by-other-hash-functions) [20.3 Contains & Duplicate Itemschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/20.-hashing-ii/20.3-contains-and-duplicate-items) [20.4 Mutable vs. Immutable Typeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/20.-hashing-ii/20.4-mutable-vs.-immutable-types) [Previous19.7 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/19.-hashing-i/19.7-exercises) [Next20.1 Hash Table Recap, Default Hash Functionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/20.-hashing-ii/20.1-hash-table-recap-default-hash-function) Last updated 1 year ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 28. Reductions and Decomposition | CS61B Textbook [28.1 Topological Sorts and DAGschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/28.-reductions-and-decomposition/28.1-topological-sorts-and-dags) [28.2 Shortest Paths on DAGschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/28.-reductions-and-decomposition/28.2-shortest-paths-on-dags) [28.3 Longest Pathchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/28.-reductions-and-decomposition/28.3-longest-path) [28.4 Reductions and Decompositionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/28.-reductions-and-decomposition/28.4-reductions-and-decomposition) [28.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/28.-reductions-and-decomposition/28.5-exercises) [Previous27.5 Summary, Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/27.-software-engineering-i/27.5-summary-exercises) [Next28.1 Topological Sorts and DAGschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/28.-reductions-and-decomposition/28.1-topological-sorts-and-dags) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 30. Quicksort | CS61B Textbook [30.1 Partitioningchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/30.-quicksort/30.1-partitioning) [30.2 Quicksort Algorithmchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/30.-quicksort/30.2-quicksort-algorithm) [30.3 Quicksort Performance Caveatschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/30.-quicksort/30.3-quicksort-performance-caveats) [30.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/30.-quicksort/30.4-summary) [30.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/30.-quicksort/30.5-exercises) [Previous29.6 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/29.-basic-sorts/29.6-exercises) [Next30.1 Partitioningchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/30.-quicksort/30.1-partitioning) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 29.6 Exercises | CS61B Textbook [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/29.-basic-sorts/29.6-exercises#factual) Factual -------------------------------------------------------------------------------------------------------- 1. How many inversions are there in the array `[10, 100, 60, 40, 50]`? 2. What is the space complexity of selection sort? chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/29.-basic-sorts/29.6-exercises#problem-1) 5; the violations are 100 > 60, 100 > 40, 100 > 50, 60 > 40, and 60 > 50. chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/29.-basic-sorts/29.6-exercises#problem-2) Θ(1)\\Theta(1)Θ(1). All swaps in selection sort happen in-place. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/29.-basic-sorts/29.6-exercises#procedural) Procedural -------------------------------------------------------------------------------------------------------------- 1. Draw the process of heapsort on the array `[5, 9, 2]`, starting with the heapified array and removing the maximum each time. 2. If we are insertion sorting `[5, 6, 7, 1, 8, 9, 2]`, how many total swaps will occur? chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/29.-basic-sorts/29.6-exercises#problem-1-1) After heapification: `[9, 5, 2]`. We sink in reverse level order, which means that we swap `5` with `9`. Then, the algorithm proceeds by popping off `9` (`[5, 2 | 9]`), then `5` (`[2 | 5, 9]`), then `2` (`[2, 5, 9]`). chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/29.-basic-sorts/29.6-exercises#problem-2-1) 8; simply count the number of inversions (5 > 1, 5 > 2, 6 > 1, 6 > 2, 7 > 1, 7 > 2, 8 > 2, 9 > 2). [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/29.-basic-sorts/29.6-exercises#metacognitive) Metacognitive -------------------------------------------------------------------------------------------------------------------- 1. Which sort do you expect to run more quickly on a reversed array, selection sort or insertion sort? chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/29.-basic-sorts/29.6-exercises#problem-1-2) Asymptotically, both selection and insertion sort run in Θ(N2)\\Theta(N^2)Θ(N2) on a reverse-sorted array. However, note that selection sort only does NNN total swaps (finding the maximum, then swapping to the front), while insertion sort does on the order of N2N^2N2 swaps (swapping each item to the front), so insertion sort will actually be slower by a constant factor. [Previous29.5 Summarychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/29.-basic-sorts/29.5-summary) [Next30\. Quicksortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/30.-quicksort) Last updated 2 years ago * [Factual](https://cs61b-2.gitbook.io/cs61b-textbook/29.-basic-sorts/29.6-exercises#factual) * [Procedural](https://cs61b-2.gitbook.io/cs61b-textbook/29.-basic-sorts/29.6-exercises#procedural) * [Metacognitive](https://cs61b-2.gitbook.io/cs61b-textbook/29.-basic-sorts/29.6-exercises#metacognitive) sun-brightdesktopmoon sun-brightdesktopmoon --- # 31. Software Engineering II | CS61B Textbook [31.1 Complexity IIchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.1-complexity-ii) [31.2 Sources of Complexitychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.2-sources-of-complexity) [31.3 Modular Designchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.3-modular-design) [31.4 Teamworkchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.4-teamwork) [31.5 Exeriseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.5-exerises) [Previous30.5 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/30.-quicksort/30.5-exercises) [Next31.1 Complexity IIchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.1-complexity-ii) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 32. More Quick Sort, Sorting Summary | CS61B Textbook [32.1 Quicksort Flavors vs. MergeSortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/32.-more-quick-sort-sorting-summary/32.1-quicksort-flavors-vs.-mergesort) [32.2 Quick Selectchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/32.-more-quick-sort-sorting-summary/32.2-quick-select) [32.3 Stability, Adaptiveness, and Optimizationchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/32.-more-quick-sort-sorting-summary/32.3-stability-adaptiveness-and-optimization) [32.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/32.-more-quick-sort-sorting-summary/32.4-summary) [32.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/32.-more-quick-sort-sorting-summary/32.5-exercises) [Previous31.5 Exeriseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.5-exerises) [Next32.1 Quicksort Flavors vs. MergeSortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/32.-more-quick-sort-sorting-summary/32.1-quicksort-flavors-vs.-mergesort) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 33. Software Engineering III | CS61B Textbook [33.1 Candy Crush, SnapChat, and Friendschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/33.-software-engineering-iii/33.1-candy-crush-snapchat-and-friends) [33.2 The Ledger of Harmschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/33.-software-engineering-iii/33.2-the-ledger-of-harms) [33.3 Your Lifechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/33.-software-engineering-iii/33.3-your-life) [33.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/33.-software-engineering-iii/33.4-summary) [33.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/33.-software-engineering-iii/33.5-exercises) [Previous32.5 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/32.-more-quick-sort-sorting-summary/32.5-exercises) [Next33.1 Candy Crush, SnapChat, and Friendschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/33.-software-engineering-iii/33.1-candy-crush-snapchat-and-friends) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # DISCLAIMER | CS61B Textbook Fall 2025 This textbook is under construction. Sections may be missing and/or erroneous. Please contact circlecly@berkeley.edu with any questions or comments. [PreviousContributorschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/readme-1) [Next1\. Introductionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/1.-introduction) Last updated 6 months ago --- # Contributors | CS61B Textbook Fall 2025 * Professor Joshua Hug * Vanessa Teo * Angel Aldaco * Dhruti Pandya * Thomas Lee * Teresa Luo * Mihir Mirchandani * William Lee * Vidya Ganga * Ergun Acikoz * Kenneth Wang * Carl Ji * Nathalys Pham * Stella Kaval * Aniruth Narayanan * Circle Chen * Ricardo Sandoval [PreviousFall 2025 Textbookchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025) [NextDISCLAIMERchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/disclaimer) Last updated 4 months ago --- # 31.3 Modular Design | CS61B Textbook Modular design is a powerful tool for managing complexity because it divides the project complexity into manageable pieces. One way to implement modular design is to create helper methods or interfaces. This way, the programmer can individually handle each component of complexity rather than having to always keep track of the details of every piece of code that they write. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.3-modular-design#modules-should-be-simple) Modules should be simple: In an ideal world, every module is totally independent from one another. Unfortunately, this is not possible because code from each module needs to call other modules. However, we can still try to minimize these dependencies between modules! In other words, we want to minimize how many _things_ you need to know about a given module in order to use it. This is exactly what we mean when we talk about the difference between _implementation_ versus _interface_. A good module will not require the user to know the specific implementation in order to use it. Rather, it should be sufficient to just know the interface of the module. Changing the implementation of a module should not affect the interface. John Ousterhout once said: "The best modules are those whose interfaces are much simpler than their implementation." This is a good rule to swear by, and putting it into practice will save a lot of headache. One other technique of minimizing complexity is to restrict what the user can do. If a user does not _need_ to interact with an instance variable, then don't give them access to it. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.3-modular-design#interface-rules) Interface rules: Interfaces have a further set of rules. These rules are divided into _formal_ and _informal_ rules. The difference between the two is that informal rules are not enforced by the compiler. Formal rules are the list of method signatures. If a method is not implemented in a class that implements the interface, then the compiler will give an error. Some examples of informal rules are: * If your iterator class does not call hasNext() on its own (for some reason) and instead requires the user to call it. * Any exceptions that are thrown. * Any runtime specifications. Be especially wary of informal rules! They are hard to keep track of. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.3-modular-design#modules-should-be-deep) Modules should be deep Another cool idea is that Modules should be deep. Their simple interfaces but powerful functionality. We do this a lot like thats the 61B story! A set for example is a deep module that has power functioanlity and simple interfaces. So Red Black BSTs is very deep. I can add, contain, and delete, and there is nothing informal I need to know, it's all under the hood. Powerful functionality means that all operations are efficient. Tree balancing is maintained using sophisticated yet subtle rules. They are tricky and we hide them under the surface. The most important way to keep modules deep is by practicing information hiding. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.3-modular-design#information-hiding) Information Hiding That is, make your variables private, don't let anyone see what's inside the module as much as you can. Embed all the cleverness inside the modules. So that will keep your interfaces simple. And also it would keep it easy to modify your system. If I made a mistake, I can go fix that without thinking about it in another context. The opposite of hiding information is leaking information. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.3-modular-design#leaking-information) Leaking Information This occurs when design decisions are reflected across multiple modules. * Any change to one module requires a change to all modules * Information leakage is one of the most important red flags in software design * One of the best skills you can learn as a software designed is a high level of sensitivity to information leakage ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.3-modular-design#temporal-decomposotion) Temporal Decomposotion One of the biggest causes of information leaking is "temporal decomposition," especially in BYOW. The structure of your system very much reflects the order in which events occur. For example, student often do the following in BYOW: * Game is started with an input string, so call interactWithInputString() * Parse the String and find the seed by extracting N#####S (example code that contains the seed.)¹ * Generate the world * Process each character using move(World, char) * etc. * Game is started with no input String, so call interactWithKeyboard * Display a menu and collect the seed (number we are using to generate the world).¹ * Generate the world * Until done, call moveWithKeyboard(World) * etc. ¹ Because the temporal discussion of when you worked on the project and the temporal decomposition of when these things happen, you don't really recognize that they should be sharing code that collects and extracts the seed for example. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.3-modular-design#summary) Summary * Buld classes that provide functionality needed in many places in your code. * Create deep modules, classes with simple interfaces that do complicated things * avoid over-reliance on temporal decomposition where your decomposition is driven primarily by the order in which things occur. * It’s OK to use some temporal decomposition, but try to fix any information leakage that occurs! * Be strategic, not tactical. * Most importantly: Hide information from yourself when unneeded! [Previous31.2 Sources of Complexitychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.2-sources-of-complexity) [Next31.4 Teamworkchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.4-teamwork) Last updated 2 years ago * [Modules should be simple:](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.3-modular-design#modules-should-be-simple) * [Interface rules:](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.3-modular-design#interface-rules) * [Modules should be deep](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.3-modular-design#modules-should-be-deep) * [Information Hiding](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.3-modular-design#information-hiding) * [Leaking Information](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.3-modular-design#leaking-information) * [Temporal Decomposotion](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.3-modular-design#temporal-decomposotion) * [Summary](https://cs61b-2.gitbook.io/cs61b-textbook/31.-software-engineering-ii/31.3-modular-design#summary) sun-brightdesktopmoon sun-brightdesktopmoon --- # 37. Software Engineering IV | CS61B Textbook [37.1 The end is nearchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/37.-software-engineering-iv/37.1-the-end-is-near) [Previous36.5 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.5-exercises) [Next37.1 The end is nearchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/37.-software-engineering-iv/37.1-the-end-is-near) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 32.3 Stability, Adaptiveness, and Optimization | CS61B Textbook [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/32.-more-quick-sort-sorting-summary/32.3-stability-adaptiveness-and-optimization#stability) Stability -------------------------------------------------------------------------------------------------------------------------------------------------------------- A sort is stable if the order of equivalent elements is preserved. The following is an example of a stable sort. After sorting by section, notice how Bas, Jana, Jouni, and Rosella are in the same order as before sorting. If we want records sorted by section and then by name within each section, we can sort by name and then by section as below. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FRilq7W7nGtswHmyl5GCP%252FScreenshot%25202023-04-10%2520at%252012.16.47%2520AM.png%3Falt%3Dmedia%26token%3D1f9b6e83-2b33-458b-a4c1-a372519463f3&width=768&dpr=3&quality=100&sign=2c1aa32&sv=2) The following example is an unstable sort. It can make things really annoying! If we want records sorted by section and then by name within each section, we can't just sort by name and then by section as before. After an unstable sort, the previous ordering is not maintained. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252Fg6FpmZLksYV79KAacWNu%252FScreenshot%25202023-04-10%2520at%252012.28.10%2520AM.png%3Falt%3Dmedia%26token%3Da283f697-990e-41f5-a819-9c4c0d1fff14&width=768&dpr=3&quality=100&sign=464c4af4&sv=2) Are some of the sorts we learned stable? Insertion sort is stable! Equivalent elements move past their equivalent brethren. MergeSort is stable. HeapSort is not stable. QuickSort can be stable depending on its partitioning scheme, but its stability cannot be assumed since many of its popular partitioning schemes, like Hoare, are unstable. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/32.-more-quick-sort-sorting-summary/32.3-stability-adaptiveness-and-optimization#optimizations) Optimizations ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- Adaptiveness - A sort that is adaptive exploits the existing order of the array. Examples are InsertionSort, SmoothSort, and TimSort. Switch to Insertion Sort - When a subproblem reaches size 15 or lower, use insertion sort. It is very very fast for inputs of small sizes. Exploit restrictions on set of keys - For example, if the number of keys is some constant, we can use this constraint to sort faster by applying 3-way QuickSort. Switch from QuickSort - If the recursion goes too deep, switch to a different type of sort. [Previous32.2 Quick Selectchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/32.-more-quick-sort-sorting-summary/32.2-quick-select) [Next32.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/32.-more-quick-sort-sorting-summary/32.4-summary) Last updated 2 years ago * [Stability](https://cs61b-2.gitbook.io/cs61b-textbook/32.-more-quick-sort-sorting-summary/32.3-stability-adaptiveness-and-optimization#stability) * [Optimizations](https://cs61b-2.gitbook.io/cs61b-textbook/32.-more-quick-sort-sorting-summary/32.3-stability-adaptiveness-and-optimization#optimizations) sun-brightdesktopmoon sun-brightdesktopmoon --- # 34. Sorting and Algorithmic Bounds | CS61B Textbook [34.1 Sorting Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/34.-sorting-and-algorithmic-bounds/34.1-sorting-summary) [34.2 Math Problems Out of Nowherechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/34.-sorting-and-algorithmic-bounds/34.2-math-problems-out-of-nowhere) [34.3 Theoretical Bounds on Sortingchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/34.-sorting-and-algorithmic-bounds/34.3-theoretical-bounds-on-sorting) [34.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/34.-sorting-and-algorithmic-bounds/34.4-summary) [34.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/34.-sorting-and-algorithmic-bounds/34.5-exercises) [Previous33.5 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/33.-software-engineering-iii/33.5-exercises) [Next34.1 Sorting Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/34.-sorting-and-algorithmic-bounds/34.1-sorting-summary) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 1. Introduction | CS61B Textbook Fall 2025 This section covers basic features of Java and how to run programs on the command line. [PreviousDISCLAIMERchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/disclaimer) [Next1.1 Your First Java Programchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/1.-introduction/1.1-your-first-java-program) Last updated 6 months ago --- # 11. There is no chapter 11. | CS61B Textbook Fall 2025 [Previous10.4 Summarychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.4-higher-order-functions-in-java) [Next12\. Inheritance III: Iterators, Object Methodschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods) Last updated 6 months ago --- # 33.5 Exercises | CS61B Textbook Note: there are no exercises for this chapter. For the final exam, there is no need to specifically study this lecture. Any questions relevant to software engineering will be based more on experience with projects than specific terminology and lecture material. [Previous33.4 Summarychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/33.-software-engineering-iii/33.4-summary) [Next34\. Sorting and Algorithmic Boundschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/34.-sorting-and-algorithmic-bounds) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 33.4 Summary | CS61B Textbook In this fun lecture, we steered a bit away from core CS61B exam material and discussed a high-level overview of the impacts that students can have on society and on their own lives using the skills obtained in CS 61B and beyond. We covered specific societal impacts in the realm of economics within the context of industry, and social impacts with how humans in our global civilization interact with one another using the digital products that we can create using our coding skills. Most importantly, we delved into the potential _harms_ to society that our coding skills could have – from the psychological foundations that could be altered on our youth to the reconfiguration of politics and governmental leaders are elected. With great power comes great responsibility, and it is evermore vital that we consider the deep, all-encompassing question of ethics in creating software for humanity. With the demand for software engineers and the financial impact that creating software solutions to the worldwide marketplace has, the value added to you as a worker in our society is immense & very fruitful. However, it is important to consider factors beyond just work to create a multifaceted thing called your life. We only work so many hours in the weeks in our lives– so it is incredibly important to ensure that work (and learning CS to become a software engineer, etc.) is just a subpart of what we consider our entire life. Have fun on the journey! [Previous33.3 Your Lifechevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/33.-software-engineering-iii/33.3-your-life) [Next33.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/33.-software-engineering-iii/33.5-exercises) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 37.1 The end is near | CS61B Textbook Another Software Engineering lecture!? Yes!! We're actually now almost done with 61B. Best of luck on finals! Today, we'll actually just be spending time reflecting on 61B. 61B has been taught since ~Spring 1994. Before that, it was CS60C, which goes back to at least 1988. In modern times there have been 4 varieties of 61B: * Hilfinger: 4 extremely long real world projects that are somewhat based on data structures material. * Hug: 1 (or 2) long real world project that is somewhat based on data structures material. Remaining material ties in tightly to lectures. * 61BL: Lab based class that focuses heavily on data structures, but with one large real world project (Gitlet). * Shewchuk / Yelick (extinct): Focus on implementing data structures. No large real world project ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/37.-software-engineering-iv/37.1-the-end-is-near#id-61b-1.0) 61B 1.0 Gitlet was first offered in the Spring 2015 offering of 61B. * My first solo offering of the class. * Projects had significant authorship from students. * Project 0 - Bomb Checkers (me, but implemented by Jimmy Lee). * Project 1 - ngordnet (me). * Project 2 - Gitlet (Joey Moghadam). * Project 3 - Fun with Tries (me, adapted from my old Princeton HWs). * Joey also used the project as one of his assignments in Summer 2015. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/37.-software-engineering-iv/37.1-the-end-is-near#id-61b-2.0) 61B 2.0 CS61B Version 2 (Spring 2016, 2017) * Fall 2014/Spring 2015 observation: Hated that students had to split time between the core data structures content and a huge project that wasn’t related to that content. * Decided to have the messy real world project due right before data structures: * 2016: Build a text editor. * 2017: Create software for manipulating text databases. (This is a very cool project! I really hope you can try this for yourselves and then take CS 186!!) ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/37.-software-engineering-iv/37.1-the-end-is-near#feedback-helps) Feedback helps... ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2Flh3.googleusercontent.com%2FEYU0gslQ-pFXdqjr9voSyTbvcHUY7Ztp1DP5FmIlRDXW1T7_f2OyJObaQTxmHPs8CZLOnMm-UX5SpRpp_uzbCQvd161nJZh7jYifdMJM9Qo35KrbaHChB0wYBKbiLlt3qRjRJZCEsmb9O5EFrSJiMZlvDw%3Ds2048&width=768&dpr=3&quality=100&sign=973083&sv=2) Gitlet Feedback CS61B Current Version (3.6) (Fall 2022 and Spring 2023) * Replaced Gitlet with Ngordnet. * Ngordnet is much more data structures focused. * 2A: Build a TimeSeries and build an NgramMap. * 2B: Build whatever you need to support additional functionality, including implementing a graph somehow. GSIs voted ~3 to 1 to keep Ngordnet over Gitlet. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/37.-software-engineering-iv/37.1-the-end-is-near#summary) Summary Overall this course was meant to help you with Software Engineering. And all of you have done an amazing job making data structures such as HashMaps and ArrayLists and using Java to solve real-world problems that I hope was interesting! Many people even come out of this class signing up for Linguistics 100 after learning about hyponyms and doing Ngordnet. Wherever you now go equipped with this amazing knowledge of 61B, I wish you the best of luck! Here's to solving more problems with CS! Cheers and good luck on Finals! Spring/summer 2015 Gitlet was way too hard. * No testing provided. * No tips on persistence. [Previous37\. Software Engineering IVchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/37.-software-engineering-iv) [Next38\. Compression and Complexitychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity) Last updated 2 years ago * [61B 1.0](https://cs61b-2.gitbook.io/cs61b-textbook/37.-software-engineering-iv/37.1-the-end-is-near#id-61b-1.0) * [61B 2.0](https://cs61b-2.gitbook.io/cs61b-textbook/37.-software-engineering-iv/37.1-the-end-is-near#id-61b-2.0) * [Feedback helps...](https://cs61b-2.gitbook.io/cs61b-textbook/37.-software-engineering-iv/37.1-the-end-is-near#feedback-helps) * [Summary](https://cs61b-2.gitbook.io/cs61b-textbook/37.-software-engineering-iv/37.1-the-end-is-near#summary) sun-brightdesktopmoon sun-brightdesktopmoon --- # 36. Sorting and Data Structures Conclusion | CS61B Textbook [36.1 Radix vs. Comparison Sortingchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting) [36.2 The Just-In-Time Compilerchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.2-the-just-in-time-compiler) [36.3 Radix Sorting Integerschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.3-radix-sorting-integers) [36.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.4-summary) [36.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.5-exercises) [Previous35.5 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.5-exercises) [Next36.1 Radix vs. Comparison Sortingchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 35.2 LSD Radix Sort | CS61B Textbook ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252F17vIG2wxJU2Fahu8r2Lb%252FScreen%2520Shot%25202023-04-15%2520at%25202.44.24%2520AM.png%3Falt%3Dmedia%26token%3D2613ad28-5c06-429b-93d4-cdb5949ce42d&width=768&dpr=3&quality=100&sign=94013dc2&sv=2) Least Significant Digit Radix Sort -- Using Count Sort Notice in the above picture that we had a completely random input of numbers 22, 34, 41, etc. Then in the second box, we had sorted by right most digit, so 41, 41, 31 would come first as 1 is the lowest rightmost digit. Next would come 32, 22, 12, as 2 is the second least rightmost digit. Notice that we still have to take care of sorting 32, 22, and 12 which is why we move to the second right most digit in our last box which has everything sorted. What is the runtime of LSD sort? * Θ(WN+WR)\\Theta(WN+WR)Θ(WN+WR) * NNN: Number of items, RRR: size of alphabet, WWW: Width of each item in # digits ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FzLGidJKP8E2n2Fn1CpRU%252FScreen%2520Shot%25202023-04-15%2520at%25202.49.06%2520AM.png%3Falt%3Dmedia%26token%3De803ef6a-a8f4-4a69-bb65-c4c110da338b&width=768&dpr=3&quality=100&sign=79875787&sv=2) Non-equal Key Lengths [Previous35.1 Counting Sortchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.1-counting-sort) [Next35.3 MSD Radix Sortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.3-msd-radix-sort) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 36.5 Exercises | CS61B Textbook [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.5-exercises#factual) Factual ----------------------------------------------------------------------------------------------------------------------------------- 1. What happens when a Java file is compiled? HINT: Think about `.java` and `.class` files. 2. Why can a block of code run faster the second time it executes as compared to the first time? 3. What's the optimal way to sort an array of integers? Assume the integers are uniformly randomly distributed. chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.5-exercises#problem-1) When a Java file is compiled, it is transformed from its human-readable `.java` source code format into byte code in a `.class` file format that can be executed by the Java Virtual Machine (JVM). chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.5-exercises#problem-2) If some segment of code is called many times, the interpreter studies and re-implements your code based on what is observed while the code is running. For example, if you create many unused data structures, they might be optimized out of your code. chevron-rightProblem 3[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.5-exercises#problem-3) As we saw in lecture, LSD sort outperforms quicksort for an array of integers, particularly if we optimize for a better base than base 10. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.5-exercises#metacognitive) Metacognitive ----------------------------------------------------------------------------------------------------------------------------------------------- 1. Under what conditions would LSD sort be faster than mergesort? chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.5-exercises#problem-1-1) There are two main cases when LSD sort is much faster than mergesort: when there are a large number of strings (large N), and when all strings are very similar to each other. When N is really large, we see the asymptotic behavior of LSD sort beat the asymptotic behavior of merge sort, which is slower with a runtime of Nlog⁡NN \\log NNlogN. When the strings are very similar, then each comparison in merge sort is slow, roughly linear time in the length of the string. Instead of comparing each letter in each string at every merge, LSD only examines the letters of each string once. [Previous36.4 Summarychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.4-summary) [Next37\. Software Engineering IVchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/37.-software-engineering-iv) Last updated 2 years ago * [Factual](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.5-exercises#factual) * [Metacognitive](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.5-exercises#metacognitive) sun-brightdesktopmoon sun-brightdesktopmoon --- # 35.4 Summary | CS61B Textbook **Terminology.** * Radix - just another word for ‘base’ as in the base of a number system. For example, the radix for words written in lowercase English letters is 26. For number written in Arabic numerals it is 10. * Radix sort - a sort that works with one character at a time (by grouping objects that have the same digit in the same position). * Note: I will use ‘character’ and ‘digit’ interchangably in this study guide. **Counting Sort.** Allows you to sort NNN keys that are integers between 000 and R−1R-1R−1 in Θ(N+R)\\Theta(N+R)Θ(N+R)time. Beats linearithmic lower bound by avoiding any binary compares. This is a completely different philosophy for how things should be sorted. This is the most important concept for this lecture. **LSD.** In the LSD algorithm, we sort by each digit, working from right to left. Requires examination of Θ(WN)\\Theta(WN)Θ(WN)digits, where WWWis the length of the longest key. Runtime is Θ(WN+WR)\\Theta(WN+WR)Θ(WN+WR), though we usually think of RRR as a constant and just say Θ(WN)\\Theta(WN)Θ(WN). The Θ(WR)\\Theta(WR)Θ(WR) part of the runtime is due to the creation fo length RRR arrows for counting sort. We usually do LSD sort using counting sort as a subroutine, but it’s worth thinking about whether other sorts might work as well. **LSD vs Comparison Sorting.** Our comparison sorts, despite requiring Θ(N∗logN)\\Theta(N\*logN)Θ(N∗logN) time, can still be faster than LSD sort. For extremely large N, LSD sort will naturally win, but log N is typically pretty small. Know which algorithm is best in the two extreme cases of very long dissimilar strings and very long, nearly equal strings. **MSD.** In MSD sorting, we work from left to right, and solve each resulting subproblem independently. Thus, for each problem, we may have as many as RRR subproblem. Worst case runtime is exactly the same as LSD sort, Θ(WN+WR)\\Theta(WN+WR)Θ(WN+WR), though can be much better. In the very best case, where we only have to look at the top character (only possible for R\>NR>NR\>N), we have a runtime of Θ(N+R)\\Theta(N+R)Θ(N+R). [Previous35.3 MSD Radix Sortchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.3-msd-radix-sort) [Next35.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.5-exercises) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 8. ArrayList | CS61B Textbook Fall 2025 #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/8.-arraylist#naive-resizing-arrays) Naive Resizing Arrays **Optional Exercise 2.5.3:** Suppose we have an AList in the state shown in the figure below. What will happen if we call `addLast(11)`? What should we do about this problem? ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig25%2Ffull_naive_alist.png&width=768&dpr=3&quality=100&sign=8dd233ad&sv=2) dllist\_circular\_sentinel\_size\_2.png The answer, in Java, is that we simply build a new array that is big enough to accomodate the new data. For example, we can imagine adding the new item as follows: The process of creating a new array and copying items over is often referred to as "resizing". It's a bit of a misnomer since the array doesn't actually change size, we are just making a **new** one that has a bigger size. **Exercise 2.5.4:** Try to implement the `addLast(int i)` method to work with resizing arrays. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/8.-arraylist#analyzing-the-naive-resizing-array) Analyzing the Naive Resizing Array The approach that we attempted in the previous section has terrible performance. By running a simple computational experiment where we call `addLast` 100,000 times, we see that the `SLList` completes so fast that we can't even time it. By contrast our array based list takes several seconds. To understand why, consider the following exercise: **Exercise 2.5.5:** Suppose we have an array of size 100. If we call insertBack two times, how many total boxes will we need to create and fill throughout this entire process? How many total boxes will we have at any one time, assuming that garbage collection happens as soon as the last reference to an array is lost? **Exercise 2.5.6:** Starting from an array of size 100, approximately how many memory boxes get created and filled if we call `addLast` 1,000 times? Creating all those memory boxes and recopying their contents takes time. In the graph below, we plot total time vs. number of operations for an SLList on the top, and for a naive array based list on the bottom. The SLList shows a straight line, which means for each `add` operation, the list takes the same additional amount of time. This means each single operation takes constant time! You can also think of it this way: the graph is linear, indicating that each operation takes constant time, since the integral of a constant is a line. By contrast, the naive array list shows a parabola, indicating that each operation takes linear time, since the integral of a line is a parabola. This has significant real world implications. For inserting 100,000 items, we can roughly compute how much longer by computing the ratio of N^2/N. Inserting 100,000 items into our array based list takes (100,000^2)/100,000 or 100,000 times as long. This is obviously unacceptable. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig25%2Finsert_experiment.png&width=768&dpr=3&quality=100&sign=54679adb&sv=2) fig25/insert\_experiment.png #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/8.-arraylist#geometric-resizing) Geometric Resizing We can fix our performance problems by growing the size of our array by a multiplicative amount, rather than an additive amount. That is, rather than **adding** a number of memory boxes equal to some resizing factor `RFACTOR`: We instead resize by **multiplying** the number of boxes by `RFACTOR`. Repeating our computational experiment from before, we see that our new `AList` completes 100,000 inserts in so little time that we don't even notice. We'll defer a full analysis of why this happens until the final chapter of this book. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/8.-arraylist#memory-performance) Memory Performance Our `AList` is almost done, but we have one major issue. Suppose we insert 1,000,000,000 items, then later remove 990,000,000 items. In this case, we'll be using only 10,000,000 of our memory boxes, leaving 99% completely unused. To fix this issue, we can also downsize our array when it starts looking empty. Specifically, we define a "usage ratio" R which is equal to the size of the list divided by the length of the `items` array. For example, in the figure below, the usage ratio is 0.04. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig25%2Fusage_ratio.png&width=768&dpr=3&quality=100&sign=e3f3cef5&sv=2) fig25/usage\_ratio.png In a typical implementation, we halve the size of the array when R falls to less than 0.25. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/8.-arraylist#generic-alists) Generic ALists Just as we did before, we can modify our `AList` so that it can hold any data type, not just integers. To do this, we again use the special angle braces notation in our class and substitute our arbitrary type parameter for integer wherever appropriate. For example, below, we use `Glorp` as our type parameter. There is one significant syntactical difference: Java does not allow us to create an array of generic objects due to an obscure issue with the way generics are implemented. That is, we cannot do something like: Instead, we have to use the awkward syntax shown below: This will yield a compilation warning, but it's just something we'll have to live with. We'll discuss this in more details in a later chapter. The other change we make is that we null out any items that we "delete". Whereas before, we had no reason to zero out elements that were deleted, with generic objects, we do want to null out references to the objects that we're storing. This is to avoid "loitering". Recall that Java only destroys objects when the last reference has been lost. If we fail to null out the reference, then Java will not garbage collect the objects that have been added to the list. This is a subtle performance bug that you're unlikely to observe unless you're looking for it, but in certain cases could result in a significant wastage of memory. [Previous7\. Testingchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/7.-testing) [Next9\. Inheritance I: Interface and Implementation Inheritancechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance) Last updated 6 months ago Copy int[] a = new int[size + 1]; System.arraycopy(items, 0, a, 0, size); a[size] = 11; items = a; size = size + 1; Copy public void insertBack(int x) { if (size == items.length) { resize(size + RFACTOR); } items[size] = x; size += 1; } Copy public void insertBack(int x) { if (size == items.length) { resize(size * RFACTOR); } items[size] = x; size += 1; } Copy Glorp[] items = new Glorp[8]; Copy Glorp[] items = (Glorp []) new Object[8]; --- # 5. DLLists | CS61B Textbook Fall 2025 In Chapter 2.2, we built the `SLList` class, which was better than our earlier naked recursive `IntList` data structure. In this section, we'll wrap up our discussion of linked lists, and also start learning the foundations of arrays that we'll need for an array based list we'll call an `AList`. Along the way, we'll also reveal the secret of why we used the awkward name `SLList` in the previous chapter. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/5.-dllists#addlast) addLast Consider the `addLast(int x)` method from the previous chapter. Copy public void addLast(int x) { size += 1; IntNode p = sentinel; while (p.next != null) { p = p.next; } p.next = new IntNode(x, null); } The issue with this method is that it is slow. For a long list, the `addLast` method has to walk through the entire list, much like we saw with the `size` method in chapter 2.2. Similarly, we can attempt to speed things up by adding a `last` variable, to speed up our code, as shown below: Copy public class SLList { private IntNode sentinel; private IntNode last; private int size; public void addLast(int x) { last.next = new IntNode(x, null); last = last.next; size += 1; } ... } **Exercise 2.3.1:** Consider the box and pointer diagram representing the `SLList` implementation above, which includes the last pointer. Suppose that we'd like to support `addLast`, `getLast`, and `removeLast` operations. Will the structure shown support rapid `addLast`, `getLast`, and `removeLast` operations? If not, which operations are slow? ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig23%2Fsllist_last_pointer.png&width=768&dpr=3&quality=100&sign=fd386d6e&sv=2) sllist\_last\_pointer.png **Answer 2.3.1:** `addLast` and `getLast` will be fast, but `removeLast` will be slow. That's because we have no easy way to get the second-to-last node, to update the `last` pointer, after removing the last node. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/5.-dllists#secondtolast) SecondToLast The issue with the structure from exercise 2.3.1 is that a method that removes the last item in the list will be inherently slow. This is because we need to first find the second to last item, and then set its next pointer to be null. Adding a `secondToLast` pointer will not help either, because then we'd need to find the third to last item in the list in order to make sure that `secondToLast` and `last` obey the appropriate invariants after removing the last item. **Exercise 2.3.2:** Try to devise a scheme for speeding up the `removeLast` operation so that it always runs in constant time, no matter how long the list. Don't worry about actually coding up a solution, we'll leave that to project 1. Just come up with an idea about how you'd modify the structure of the list (i.e. the instance variables). We'll describe the solution in Improvement #7. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/5.-dllists#improvement-7-looking-back) Improvement #7: Looking Back The most natural way to tackle this issue is to add a previous pointer to each `IntNode`, i.e. In other words, our list now has two links for every node. One common term for such lists is the "Doubly Linked List", which we'll call a `DLList` for short. This is in contrast to a single linked list from chapter 2.2, a.k.a. an `SLList`. The addition of these extra pointers will lead to extra code complexity. Rather than walk you through it, you'll build a doubly linked list on your own in project 1. The box and pointer diagram below shows more precisely what a doubly linked list looks like for lists of size 0 and size 2, respectively. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig23%2Fdllist_basic_size_0.png&width=768&dpr=3&quality=100&sign=e33679d8&sv=2) dllist\_basic\_size\_0.png ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig23%2Fdllist_basic_size_2.png&width=768&dpr=3&quality=100&sign=c1ba7ec2&sv=2) dllist\_basic\_size\_2.png #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/5.-dllists#improvement-8-sentinel-upgrade) Improvement #8: Sentinel Upgrade Back pointers allow a list to support adding, getting, and removing the front and back of a list in constant time. There is a subtle issue with this design where the `last` pointer sometimes points at the sentinel node, and sometimes at a real node. Just like the non-sentinel version of the `SLList`, this results in code with special cases that is much uglier than what we'll get after our 8th and final improvement. (Can you think of what `DLList` methods would have these special cases?) One fix is to add a second sentinel node to the back of the list. This results in the topology shown below as a box and pointer diagram. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig23%2Fdllist_double_sentinel_size_0.png&width=768&dpr=3&quality=100&sign=3bb5a2f8&sv=2) dllist\_double\_sentinel\_size\_0.png ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig23%2Fdllist_double_sentinel_size_2.png&width=768&dpr=3&quality=100&sign=768411e2&sv=2) dllist\_double\_sentinel\_size\_2.png An alternate approach is to implement the list so that it is circular, with the front and back pointers sharing the same sentinel node. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig23%2Fdllist_circular_sentinel_size_0.png&width=768&dpr=3&quality=100&sign=f45b5e10&sv=2) dllist\_circular\_sentinel\_size\_0.png ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig23%2Fdllist_circular_sentinel_size_2.png&width=768&dpr=3&quality=100&sign=f887c83a&sv=2) dllist\_circular\_sentinel\_size\_2.png Both the two-sentinel and circular sentinel approaches work and result in code that is free of ugly special cases, though I personally find the circular approach to be cleaner and more aesthetically beautiful. We will not discuss the details of these implementations, as you'll have a chance to explore one or both in project 1. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/5.-dllists#generic-dllists) Generic DLLists Our DLLists suffer from a major limitation: they can only hold integer values. For example, suppose we wanted to create a list of Strings: The code above would crash, since our `DLList` constructor and `addLast` methods only take an integer argument. Luckily, in 2004, the creators of Java added **generics** to the language, which will allow you to, among other things, create data structures that hold any reference type. The syntax is a little strange to grasp at first. The basic idea is that right after the name of the class in your class declaration, you use an arbitrary placeholder inside angle brackets: `<>`. Then anywhere you want to use the arbitrary type, you use that placeholder instead. For example, our `DLList` declaration before was: A generic `DLList` that can hold any type would look as below: Here, `BleepBlorp` is just a name I made up, and you could use most any other name you might care to use instead, like `GloopGlop`, `Horse`, `TelbudorphMulticulus` or whatever. Now that we've defined a generic version of the `DLList` class, we must also use a special syntax to instantiate this class. To do so, we put the desired type inside of angle brackets during declaration, and also use empty angle brackets during instantiation. For example: Since generics only work with reference types, we cannot put primitives like `int` or `double` inside of angle brackets, e.g. ``. Instead, we use the reference version of the primitive type, which in the case of `int` case is `Integer`, e.g. There are additional nuances about working with generic types, but we will defer them to a later chapter of this book, when you've had more of a chance to experiment with them on your own. For now, use the following rules of thumb: * In the .java file **implementing** a data structure, specify your generic type name only once at the very top of the file after the class name. * In other .java files, which use your data structure, specify the specific desired type during declaration, and use the empty diamond operator during instantiation. * If you need to instantiate a generic over a primitive type, use `Integer`, `Double`, `Character`, `Boolean`, `Long`, `Short`, `Byte`, or `Float` instead of their primitive equivalents. Minor detail: You may also declare the type inside of angle brackets when instantiating, though this is not necessary, so long as you are also declaring a variable on the same line. In other words, the following line of code is perfectly valid, even though the `Integer` on the right hand side is redundant. At this point, you know everything you need to know to implement the `LinkedListDeque` project on project 1, where you'll refine all of the knowledge you've gained in chapters 2.1, 2.2, and 2.3. [Previous4\. SLListschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/4.-sllists) [Next6\. Arrayschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/6.-arrays) Last updated 6 months ago Copy public class IntNode { public IntNode prev; public int item; public IntNode next; } Copy DLList d2 = new DLList("hello"); d2.addLast("world"); Copy public class DLList { private IntNode sentinel; private int size; public class IntNode { public IntNode prev; public int item; public IntNode next; ... } ... } Copy public class DLList { private IntNode sentinel; private int size; public class IntNode { public IntNode prev; public BleepBlorp item; public IntNode next; ... } ... } Copy DLList d2 = new DLList<>("hello"); d2.addLast("world"); Copy DLList d1 = new DLList<>(5); d1.insertFront(10); Copy DLList d1 = new DLList(5); --- # 35.3 MSD Radix Sort | CS61B Textbook chevron-rightSuppose we sort by topmost digit, then middle digit, then rightmost digit. Will we arrive at the correct result? [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.3-msd-radix-sort#suppose-we-sort-by-topmost-digit-then-middle-digit-then-rightmost-digit.-will-we-arrive-at-the-corre) No, this will result in sorting the array by right most digit only as we are not saving the topmost and middle digit. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FhqNX7UJTdZgipXuyMSuP%252FScreen%2520Shot%25202023-04-15%2520at%25202.55.34%2520AM.png%3Falt%3Dmedia%26token%3Ddcc41166-3464-4114-8ded-10f7239ada3f&width=768&dpr=3&quality=100&sign=82b18574&sv=2) MSD Radix Sort (correct edition) Notice first we sorted by leftmost digit. Then we grouped the data by the leftmost digit, so one group would start with a's, then the next group would start with b's, and so on and so forth. Then within our subgroups we would order by middle digit, and create newer subgroups. And finally we would break this up into further subgroups until we have all individual subproblems. This final result would be sorted. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.3-msd-radix-sort#runtimes) Runtimes Best Case. * We finish in one counting sort pass, looking only at the top digit: Θ(N+R)\\Theta(N+R)Θ(N+R) Worst Case. * We have to look at every character, degenerating to LSD sort: Θ(WN+WR)\\Theta(WN+WR)Θ(WN+WR) ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.3-msd-radix-sort#summary-of-runtimes) Summary of Runtimes ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FcfQFyXOaFTfZA88SQO84%252FScreen%2520Shot%25202023-04-15%2520at%25202.58.27%2520AM.png%3Falt%3Dmedia%26token%3Dd1f1cd73-4039-44fd-acd9-75fa91285246&width=768&dpr=3&quality=100&sign=789a6ba5&sv=2) [Previous35.2 LSD Radix Sortchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.2-lsd-radix-sort) [Next35.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.4-summary) Last updated 2 years ago * [Runtimes](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.3-msd-radix-sort#runtimes) * [Summary of Runtimes](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.3-msd-radix-sort#summary-of-runtimes) sun-brightdesktopmoon sun-brightdesktopmoon --- # 39.5 Exercises | CS61B Textbook [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.5-exercises#factual) Factual ------------------------------------------------------------------------------------------------------------------------ 1. True/false: suppose we have a 45000-bit program in Python that outputs a bitstream B. What is the maximum size of an interpreter written in Java that proves the Java-Kolmogorov complexity is less than 100,000? 2. What are the two defining properties of an NP problem? chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.5-exercises#problem-1) The interpreter must be 55,000 bits or less. If it is, we can simply decompress the bitstream by running the Python program in the interpreter, using less than 100000 bits total (program + interpreter). chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.5-exercises#problem-2) * The problem is a yes-no problem. * A solution to the problem is efficiently verifiable. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.5-exercises#procedural) Procedural ------------------------------------------------------------------------------------------------------------------------------ 1. What is the probability that some sequence of 1 million bits could be compressed to 900,000 bits or less? chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.5-exercises#problem-1-1) There are 210000002^{1000000}21000000 bit sequences of length 1 million, but only 20+21+...2899999+2900000\=2(2900000)−1≈29000012^0 + 2^1 + ... 2^{899999} + 2^{900000} = 2(2^{900000}) - 1 \\approx 2^{900001}20+21+...2899999+2900000\=2(2900000)−1≈2900001 bit sequences of length 900,000 or less. This means that the probability of any bitsequence being compressed to 900,000 bits or less is 290000121000000\=1299999\\frac{2^{900001}}{2^{1000000}} = \\frac{1}{2^{99999}}210000002900001​\=2999991​. [Previous39.4 P = NPchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.4-p-np) Last updated 2 years ago * [Factual](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.5-exercises#factual) * [Procedural](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.5-exercises#procedural) sun-brightdesktopmoon sun-brightdesktopmoon --- # 10. Inheritance II: Subtype Polymorphism, Comparators, Comparables, Generic Functions | CS61B Textbook Fall 2025 This section covers inheritance through extends, along with encapsulation, casting, and higher order functions. Its contents correspond to Lecture 9. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions#video-playlist) Video Playlist [Previous9.6 Abstract Data Typeschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.6-abstract-data-types) [Next10.1 Polymorphism vs. Function Passingchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.1-subtype-polymorphism-vs.-function-passing) Last updated 6 months ago --- # 35.5 Exercises | CS61B Textbook [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.5-exercises#factual) Factual -------------------------------------------------------------------------------------------------------- 1. If we use counting sort to sort all 61B students by the number of siblings they have, how long will the counting array be? Let NNN be the total number of students and MMM be the maximum number of siblings a student has. 2. When performing a radix sort like LSD or MSD, which of the following sorting algorithms could we use for each digit? * Counting sort * Heapsort * Mergesort * Selection sort 3. If we're sorting N lowercase english strings using MSD sort, what is the maximum number of subproblems after considering only the the most significant digit? chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.5-exercises#problem-1) The counting array will have MMM entries. In general for counting sort, the count array's length only needs to be equal to the maximum number of unique elements we are counting. chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.5-exercises#problem-2) Recall that the sub-sort for each digit must be stable. This rules out heapsort and selection sort. * check Counting sort * Heapsort * check Mergesort * Selection sort chevron-rightProblem 3[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.5-exercises#problem-3) 26, since there are 26 possible values for the first digit we're sorting on. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.5-exercises#metacognitive) Metacognitive -------------------------------------------------------------------------------------------------------------------- 1. What are some advantages of counting sort over quicksort? 2. Why do we use LSD over counting sort? 3. If we use MSD radix sort, we start with a single problem of size N, where N is the number of strings. Depending on the results on the most significant digit, we end up with a larger number of smaller subproblems, i.e. we're doing some divide and conquer. In the worst case, we end up with only one subproblem, also of size N. When does this happen? chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.5-exercises#problem-1-1) Many correct answers, but some advantages include: * For sufficiently large N, counting sort is faster (since it is linear as compared to N log N). * Counting sort is stable, since we scan from the first element to the last. * Counting sort doesn't use comparisons between elements, which might be useful if elements are not comparable. chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.5-exercises#problem-2-1) Counting sort is technically faster than LSD, since counting sort doesn't require splitting up items by digits/letters. However, counting sort can require extremely large amounts of memory in some cases (for example, when sorting `String`s, you need one entry in an array for every possible `String`). chevron-rightProblem 3[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.5-exercises#problem-3-1) This happens when items start with the same digit. In this case, all items end up in the same subgroup after the first iteration of MSD, and we recurse on a problem of size N. [Previous35.4 Summarychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.4-summary) [Next36\. Sorting and Data Structures Conclusionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion) Last updated 2 years ago * [Factual](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.5-exercises#factual) * [Metacognitive](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.5-exercises#metacognitive) sun-brightdesktopmoon sun-brightdesktopmoon --- # 39.1 Models of Compression | CS61B Textbook [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.1-models-of-compression#comparing-compression-algorithms) Comparing Compression Algorithms -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- In the last chapter, we saw many approaches to compression. This raises an interesting question: for a given bitstream, what is the best algorithm for compression? For example, consider compression the text of Moby Dick using different compression formats. In this case, `bzip2` yields the best compression. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FO7UqH9ubjgskBevPScdz%252Fimage.png%3Falt%3Dmedia%26token%3D3e947dc1-ed7b-4403-a5b2-7dcc1c672412&width=768&dpr=3&quality=100&sign=e8e301d6&sv=2) Different compression formats applied to `mobydick.txt` One might argue, however, that the best possible compression algorithm for `mobydick.txt` is simply as follows: Using this as our decompression function, we can condense all of Moby Dick into a single bit! However, there is a problem with this approach. If we include the code for the decompression algorithm as part of the compressed model (recall compression model 2 from the previous chapter), we see that Moby Dick is not compressed to one bit, but actually requires _more_ bits than the original text! [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.1-models-of-compression#decompression-algorithms) Decompression Algorithms ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- Ultimately, the goal of a compresion algorithm is to find short sequences of bits that generate desired longer sequences of bits. Formally stated, our problem is as follows: * Given a sequence of bits `B`, find a shorter sequence `DA + C(B)` that produces `B` when fed into an interpreter. `DA` represents the bits for the decompression algorithm, while `C(B)` represents the compressed version of `B`. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FMCYkJqoXfJQjA9H4QEtj%252Fimage.png%3Falt%3Dmedia%26token%3D8a2f85f6-19f2-410b-a7c4-57ff339cd5c2&width=768&dpr=3&quality=100&sign=fe891121&sv=2) Our compression model applied to `mobydick.txt`. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.1-models-of-compression#improving-compression) Improving Compression ---------------------------------------------------------------------------------------------------------------------------------------------------------------- Recall the `HugPlant` example from the previous chapter. Using Huffman encoding, we can achieve a compression ratio of 25%. However, using another algorithm we'll call `MysteryX` for now, we can compress `HugPlant.bmp` down to 29,432 bits! This achieves a 0.35% compression ratio. Out of the 283895942^{8389594}28389594 possible bistreams of length 8,389,5948,389,5948,389,594, only one in 283601512^{8360151}28360151 can achieve such a compression ratio. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FhQU7fNItG1XxGIxo6VRl%252Fimage.png%3Falt%3Dmedia%26token%3D42238a0b-5344-4478-8f31-00b7716240c0&width=768&dpr=3&quality=100&sign=835d3508&sv=2) A mystery compression algorithm that outperforms Huffman encoding by a large margin. What is `MysteryX`? Well, it's simply the Java code `HugPlant.java` written to generate the `.bmp` file! Going back to the model of self-extracting bits, we see the power of code and interpreters in compression. This leads us to two interesting questions: * **comprehensible compression:** given a target bitstream `B`, can we create an algorithm that outputs useful, readable Java code? * **optimal compression**: given a target bitstream `B`, can we find the _shortest_ possible program that outputs this bitstream? [Previous39\. Compression, Complexity, P = NPchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np) [Next39.2 Optimal Compression, Kolmogorov Complexitychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.2-optimal-compression-kolmogorov-complexity) Last updated 2 years ago * [Comparing Compression Algorithms](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.1-models-of-compression#comparing-compression-algorithms) * [Decompression Algorithms](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.1-models-of-compression#decompression-algorithms) * [Improving Compression](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.1-models-of-compression#improving-compression) sun-brightdesktopmoon Copy D(B): if input == 0: return "Call me Ishamel. ...." else: return the text as is sun-brightdesktopmoon --- # 12. Inheritance III: Iterators, Object Methods | CS61B Textbook Fall 2025 This section covers Lists and Sets, how to throw informative exceptions, iterations, and object methods. Its contents correspond to Lecture 10. [Previous11\. There is no chapter 11.chevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/11.-inheritance-iii-subtype-polymorphism-comparators-comparable) [Next12.1 Lists and Sets in Javachevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.1-lists-and-sets-in-java) Last updated 4 months ago --- # 35.1 Counting Sort | CS61B Textbook Imagine if instead of driving a slow Honda Civic, we started driving a fast Ferrari. Unfortunately, we won't actually be driving in a Ferrari today, but we will witness a blazing fast algorithm that's just as fast called Radix Sorts. When sorting an array, sorting requires Ω(Nlog⁡N)\\Omega(N \\log N)Ω(NlogN)compare operations in the worst case (array is sorted in descending order). Thus, the ultimate comparison based sorting algorithm has a worst case runtime of Θ(Nlog⁡N)\\Theta(N \\log N)Θ(NlogN). From an asymptotic perspective, that means no matter how clever we are, we can never beat Merge Sort’s worst case runtime of Θ(Nlog⁡N)\\Theta(N \\log N)Θ(NlogN). But what if we don't compare at all? ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FfO3Soswe0bCDHeCuMipB%252FScreen%2520Shot%25202023-04-15%2520at%25202.26.42%2520AM.png%3Falt%3Dmedia%26token%3Dd0524a55-9ab4-45fa-a7d0-e267facc29bd&width=768&dpr=3&quality=100&sign=5fe933d7&sv=2) Left is original, right is ordered output Essentially what just happened is that we first made a new array of the same size and then just copied all of the # indexes to the correct location. So first we look at 5 Sandra Vanilla Grimes and then copy this over to the 5th index in our new array. This does guarantee Θ(N)\\Theta(N)Θ(N) worst case time. However what if we were working with * Non-unique keys. * Non-consecutive keys. * Non-numerical keys. All of these cases are complex cases that aren't so simple to deal with. Essentially what we can do is create a simpler method which is to: * Count number of occurrences of each item. * Iterate through list, using count array to decide where to put everything. Bottom line, we can use counting sort to sort NNN objects in Θ(N)\\Theta(N)Θ(N) time. [Previous35\. Radix Sortschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts) [Next35.2 LSD Radix Sortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.2-lsd-radix-sort) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 35. Radix Sorts | CS61B Textbook [35.1 Counting Sortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.1-counting-sort) [35.2 LSD Radix Sortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.2-lsd-radix-sort) [35.3 MSD Radix Sortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.3-msd-radix-sort) [35.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.4-summary) [35.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.5-exercises) [Previous34.5 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/34.-sorting-and-algorithmic-bounds/34.5-exercises) [Next35.1 Counting Sortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/35.-radix-sorts/35.1-counting-sort) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 39.2 Optimal Compression, Kolmogorov Complexity | CS61B Textbook [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.2-optimal-compression-kolmogorov-complexity#kolmogorov-complexity) Kolmogorov Complexity ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ We define the **Kolmogorov complexity** of a bitstream `B` to be the shortest bitstream CBC\_BCB​ that outputs `B`. Let the _Java-Kolmogorov complexity_ KJ(B)K\_J(B)KJ​(B) be the shortest Java program that generates `B`. Note that for any bitstream BBB, K(B)K(B)K(B) definitely exists. However, finding and proving K(B)K(B)K(B) might be difficult or even impossible. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.2-optimal-compression-kolmogorov-complexity#languages-and-complexity) Languages and Complexity An important thing to note is that Kolmogorov complexity is language-independent. To run any program in one language in another, all I have to do is write an interpreter. For example, if I want to run a Python program that is not easily translatable to Java, I could instead just write a Java interpreter to read the text of the Python program and run it. In this case, KJ(B)≤KP(B)+IK\_J(B) \\leq K\_P(B) + IKJ​(B)≤KP​(B)+I, where III is the length of the interpreter (a constant value). This highlights a very deep fact about Kolmogorov complexity: most bitstreams are fundamentally incompressible no matter which language we choose for our compression algorithm. Consider a bitstream of 1,000,000 bits. Out of all compression algorithms possible, only 1 in 249999992^{4999999}24999999 bitstreams have a change of being compressed by more than 50% (499,999 bits or less). ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.2-optimal-compression-kolmogorov-complexity#uncomputability) Uncomputability Another important fact regarding Kolmogorov complexity is that it is impossible to compute. A proof of this fact is provided [herearrow-up-right](https://en.wikipedia.org/w/index.php?title=Kolmogorov_complexity#Uncomputability_of_Kolmogorov_complexity) . Practically, this means that it is impossible to write a "perfect" (optimal) compression algorithm, since we can't even compute the length of the shortest program! [Previous39.1 Models of Compressionchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.1-models-of-compression) [Next39.3 Space/Time-Bounded Compressionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.3-space-time-bounded-compression) Last updated 2 years ago * [Kolmogorov Complexity](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.2-optimal-compression-kolmogorov-complexity#kolmogorov-complexity) * [Languages and Complexity](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.2-optimal-compression-kolmogorov-complexity#languages-and-complexity) * [Uncomputability](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.2-optimal-compression-kolmogorov-complexity#uncomputability) sun-brightdesktopmoon sun-brightdesktopmoon --- # 39.4 P = NP | CS61B Textbook [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.4-p-np#reductions) Reductions ------------------------------------------------------------------------------------------------------------------------- It turns out that space-time-bounded compression reduces to 3SAT, INDSET, LONGESTPATH, and many other hard problems. (The actual proof of such reductions is incredibly complex and ommitted from this textbook). ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FzNxXfJvAA7l9tDxIlBQK%252Fimage.png%3Falt%3Dmedia%26token%3Dd3fbfeff-3071-4176-aed8-f47f8ff2e337&width=768&dpr=3&quality=100&sign=3c449295&sv=2) Space-time-bounded compresion can be solved with LONGEST\_PATH. The reason that space-time-compression can be turned into longest paths (or any other problem mentioned above) is that all these problems are part of a **complexity class** known as **NP**. A property of such problem is that any NP problem can be reduced to any NP-complete problem, including longest paths. In subsequent section, we will briefly cover what P, NP, and complexity classes are. However, most of these topics are far beyond the scope of this textbook or course, and would be better served by taking an upper-level algorithms course. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.4-p-np#p-and-np) P and NP --------------------------------------------------------------------------------------------------------------------- All yes/no problems can be divided into two main classes: * P: efficiently solvable problems. * NP: problems with solutions that are efficiently verifiable. This means that given an answer to the problem, you can efficiently check whether the answer is correct or not. Examples of problems in P include (note that P is a subset of NP): * Is this array sorted? * This can be solved by sorting the array using any sorting algorithm, and verified by checking that adjacent elements are increasing. * Does this array have duplicates? * This can be solved with a double for-loop, and verified in a similar manner. Examples of problems in NP include: * Is there a solution to this 3SAT problem? * Generating a solution to a 3SAT problem is difficult, but given an assignment of symbols to booleans, you can simply plug in the values and check that the equation is satisfied. * In graph G, does there exist a path from s to t of weight > k? * Genearting a solution to this (essentially longest paths) is difficult, but given a path, you can easily verify if it is a valid path from s to t and that its weight is > k. Examples of problems not in NP include: * Is this the best chest move I can make next? * There is no efficient way to verify that a chess move is indeed optimal, unless you draw out all possibilities for all subsequent moves. * What is the longest path? * This is not a yes-no question. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.4-p-np#np-completeness) NP-Completeness ----------------------------------------------------------------------------------------------------------------------------------- An unexpected property of NP problems is that every NP problem reduces to every NP-complete problem. This reduction is also "efficient", in that the problem can be transformed (pre-processed and post-processed) in polynomial time. This also means that solving any NP-complete problem essentially solves all problems in NP. As of today, there are tens of thousands of known NP-complete problems, but none of them have been solved yet. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252F2dBm6rom80OW1pQ3AQyZ%252Fimage.png%3Falt%3Dmedia%26token%3D7c685324-f9fb-45ad-84bc-c66f41a47c3b&width=768&dpr=3&quality=100&sign=f886d5ff&sv=2) NP-complete problems and reductions. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.4-p-np#p-np) P = NP? ---------------------------------------------------------------------------------------------------------------- An open question in computer science is whether P = NP; in other words, are all problems with efficiently verifiable problems (NP) also efficiently solvable (P)? One reason to suggest that P = NP might be true is that checking an answer is always efficient. Thus, given the right pruning, could we efficiently zero in on an answer? [Previous39.3 Space/Time-Bounded Compressionchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.3-space-time-bounded-compression) [Next39.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.5-exercises) Last updated 2 years ago * [Reductions](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.4-p-np#reductions) * [P and NP](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.4-p-np#p-and-np) * [NP-Completeness](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.4-p-np#np-completeness) * [P = NP?](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.4-p-np#p-np) sun-brightdesktopmoon sun-brightdesktopmoon --- # 36.3 Radix Sorting Integers | CS61B Textbook Obama knows Bubble sort! This is amazing! circle-info BTW: A 32-bit integer is a normal integer in Java and other programming languages that spans 4 billion values. This is something we learned in Hashing chapter 20 and is a 61C concept! ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.3-radix-sorting-integers#summary-of-video-above) Summary of Video above Barack Obama gets a Google Interview question: What is the most efficient way to sort a million 32-bit integers? Obama says that he would recommend not using Bubble sort! That's right folks! Obama knows his 61B 😄. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.3-radix-sorting-integers#whats-the-answer) What's the answer? The answer to this question actually is Radix sort because we know that in a very large N limit, Radix sort is simply fastest as it is linear with runtime Θ(WN) where W is the number of digits and N is the number of integers we are sorting. This is much faster than any Comparison Sort we learned about since the fastest runtimes seem to be Θ(N\*log(N)). ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.3-radix-sorting-integers#but-how-do-we-do-it) But how do we do it? We don't have a charAt() for every integer. And if we converted all integers to strings, that's a very time expensive operation as well. So how would you LSD radix sort an array of integers? Instead of using charAt, maybe write a helper method like getDthDigit(int N, int d). Example: getDthDigit(15009, 2) = 5. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.3-radix-sorting-integers#lsd-radix-sort-on-integers) LSD Radix Sort on Integers Note that we don't have to work with base 10. What we can instead do is increase this base to have less digits in our total number. This is really getting into 61C territory as we haven't really discussed binary representation of integers yet, but essentially what we want to do is convert to hexadecimal, a base 16 number that is represented with the following digits: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F}. Note that A is 10 and F is 15. 0 - 15 is 16 possible values for a digit! To convert a number from decimal (base 10) to hexadecimal (base 16), we do the following: ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252Fuvf9JmlLIkE1hznYq3hl%252FScreen%2520Shot%25202023-04-19%2520at%25201.18.26%2520AM.png%3Falt%3Dmedia%26token%3Dbba29e6f-60e1-45f2-80cf-363cae683800&width=768&dpr=3&quality=100&sign=c0d6de89&sv=2) Decimal to Hexadecimal Notice that our original number had 6 digits in base 10 and our resulting number has 5 digits! Notice we don't have to go to just base 16. We can go even bigger to base 256 to only have 3 digits! There are small yet amazing optimizations! ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FeCzQGo5LX6fW6lDiFG6Y%252FScreen%2520Shot%25202023-04-19%2520at%25201.21.11%2520AM.png%3Falt%3Dmedia%26token%3D05d91fc2-7a34-45c7-a370-f05c7f1a269b&width=768&dpr=3&quality=100&sign=28c9adcd&sv=2) Decimal to Ducentohexaquinquagesimal (Base 256) Please do not worry about memorizing what "ducentohexaquinquagesimal" is. The only important thing to learn here is that this conversion to higher bases may result in fewer digits for our LSD and MSD Radix sorts to have faster traversals. However, there is a tradeoff between W, the size of each integer, and the size of the array we need to maintain the counts. The most oprtimal is actually base 256! [Previous36.2 The Just-In-Time Compilerchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.2-the-just-in-time-compiler) [Next36.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.4-summary) Last updated 2 years ago * [Summary of Video above](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.3-radix-sorting-integers#summary-of-video-above) * [What's the answer?](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.3-radix-sorting-integers#whats-the-answer) * [But how do we do it?](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.3-radix-sorting-integers#but-how-do-we-do-it) * [LSD Radix Sort on Integers](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.3-radix-sorting-integers#lsd-radix-sort-on-integers) sun-brightdesktopmoon sun-brightdesktopmoon --- # 6. Arrays | CS61B Textbook Fall 2025 So far, we've seen how to harness recursive class definitions to create an expandable list class, including the `IntList`, `SLList`, and `DLList`. In the next two sections of this book, we'll discuss how to build a list class using arrays. This section of this book assumes you've already worked with arrays and is not intended to be a comprehensive guide to their syntax. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/6.-arrays#array-basics) Array Basics To ultimately build a list that can hold information, we need some way to get memory boxes. Prevously, we saw how we could get memory boxes with variable declarations and class instantiations. For example: * `int x;` gives us a 32 bit memory box that stores ints. * `Walrus w1;` gives us a 64 bit memory box that stores Walrus references. * `Walrus w2 = new Walrus(30, 5.6);` gets us 3 total memory boxes. One 64 bit box that stores Walrus references, one 32 bit box that stores the int size of the Walrus, and a 64 bit box that stores the double tuskSize of the Walrus. Arrays are a special type of object that consists of a numbered sequence of memory boxes. This is unlike class instances, which have named memory boxes. To get the ith item of an array, we use bracket notation as we saw in HW0 and Project 0, e.g. `A[i]` to get the `i`th element of A. Arrays consist of: * A fixed integer length, N * A sequence of N memory boxes (N = length) where all boxes are of the same type, and are numbered 0 through N - 1. Unlike classes, arrays do not have methods. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/6.-arrays#naive-alists) Naive ALists In this section, we'll build a new class called `AList` that can be used to store arbitrarily long lists of data, similar to our `DLList`. Unlike the `DLList`, the `AList` will use arrays to store data instead of a linked list. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/6.-arrays#linked-list-performance-puzzle) Linked List Performance Puzzle Suppose we wanted to write a new method for `DLList` called `int get(int i)`. Why would `get` be slow for long lists compared to `getLast`? For what inputs would it be especially slow? You may find the figure below useful for thinking about your answer. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig23%2Fdllist_circular_sentinel_size_2.png&width=768&dpr=3&quality=100&sign=f887c83a&sv=2) dllist\_circular\_sentinel\_size\_2.png #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/6.-arrays#linked-list-performance-puzzle-solution) Linked List Performance Puzzle Solution It turns out that no matter how clever you are, the `get` method will usually be slower than `getBack` if we're using the doubly linked list structure described in section 2.3. This is because, since we only have references to the first and last items of the list, we'll always need to walk through the list from the front or back to get to the item that we're trying to retrieve. For example, if we want to get item #417 in a list of length 10,000, we'll have to walk across 417 forward links to get to the item we want. In the very worst case, the item is in the very middle and we'll need to walk through a number of items proportional to the length of the list (specifically, the number of items divided by two). In other words, our worst case execution time for `get` is linear in the size of the entire list. This in contrast to the runtime for `getBack`, which is constant, no matter the size of the list. Later in the course, we'll formally define runtimes in terms of big O and big Theta notation. For now, we'll stick to an informal understanding. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/6.-arrays#our-first-attempt-the-naive-array-based-list) Our First Attempt: The Naive Array Based List Accessing the `i`th element of an array takes constant time on a modern computer. This suggests that an array-based list would be capable of much better performance for `get` than a linked-list based solution, since it can simply use bracket notation to get the item of interest. If you'd like to know **why** arrays have constant time access, check out this [Quora postarrow-up-right](https://www.quora.com/Why-does-accessing-an-array-element-take-constant-time) . **Optional Exercise 2.5.1:** Try to build an AList class that supports `addLast`, `getLast`, `get`, and `size` operations. Your AList should work for any size array up to 100. For starter code, see [https://github.com/Berkeley-CS61B/lectureCode/tree/master/lists4/DIYarrow-up-right](https://github.com/Berkeley-CS61B/lectureCode/tree/master/lists4/DIY) . [My solutionarrow-up-right](https://github.com/Berkeley-CS61B/lectureCode/tree/master/lists4/naive) has the following handy invariants. * The position of the next item to be inserted (using `addLast`) is always `size`. * The number of items in the AList is always `size`. * The position of the last item in the list is always `size - 1`. Other solutions might be slightly different. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/6.-arrays#removelast) removeLast The last operation we need to support is `removeLast`. Before we start, we make the following key observation: Any change to our list must be reflected in a change in one or more memory boxes in our implementation. This might seem obvious, but there is some profundity to it. The list is an abstract idea, and the `size`, `items`, and `items[i]` memory boxes are the concrete representation of that idea. Any change the user tries to make to the list using the abstractions we provide (`addLast`, `removeLast`) must be reflected in some changes to these memory boxes in a way that matches the user's expectations. Our invariants provide us with a guide for what those changes should look like. **Optional Exercise 2.5.2:** Try to write `removeLast`. Before starting, decide which of `size`, `items`, and `items[i]` needs to change so that our invariants are preserved after the operation, i.e. so that future calls to our methods provide the user of the list class with the behavior they expect. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/6.-arrays#appendix-extra-information-on-arrays) Appendix: Extra Information on Arrays There are three valid notations for array creation. Try running the code below and see what happens. Click [herearrow-up-right](http://pythontutor.com/iframe-embed.html#code=public%20class%20ArrayCreationDemo%20%7B%0A%20%20public%20static%20void%20main(String%5B%5D%20args%29%20%7B%0A%20%20%20%20int%5B%5D%20x%3B%0A%20%20%20%20int%5B%5D%20y%3B%0A%20%20%20%20x%20%3D%20new%20int%5B3%5D%3B%0A%20%20%20%20y%20%3D%20new%20int%5B%5D%7B1,%202,%203,%204,%205%7D%3B%0A%20%20%20%20int%5B%5D%20z%20%3D%20%7B9,%2010,%2011,%2012,%2013%7D%3B%0A%09%7D%0A%7D&codeDivHeight=400&codeDivWidth=350&cumulative=false&curInstr=0&heapPrimitives=false&origin=opt-frontend.js&py=java&rawInputLstJSON=%5B%5D&textReferences=false) for an interactive visualization. * `x = new int[3];` * `y = new int[]{1, 2, 3, 4, 5};` * `int[] z = {9, 10, 11, 12, 13};` All three notations create an array. The first notation, used to create `x`, will create an array of the specified length and fill in each memory box with a default value. In this case, it will create an array of length 3, and fill each of the 3 boxes with the default `int` value `0`. The second notation, used to create `y`, creates an array with the exact size needed to accommodate the specified starting values. In this case, it creates an array of length 5, with those five specific elements. The third notation, used to declare **and** create `z`, has the same behavior as the second notation. The only difference is that it omits the usage of `new`, and can only be used when combined with a variable declaration. None of these notations is better than any other. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/6.-arrays#array-access-and-modification) Array Access and Modification The following code showcases all of the key syntax we'll use to work with arrays. Try stepping through the code below and making sure you understand what happens when each line executes. To do so, click [herearrow-up-right](https://goo.gl/bertuh) for an interactive visualization. With the exception of the final line of code, we've seen all of this syntax before. The final line demonstrates one way to copy information from one array to another. `System.arraycopy` takes five parameters: * The array to use as a source * Where to start in the source array * The array to use as a destination * Where to start in the destination array * How many items to copy For Python veterans, `System.arraycopy(b, 0,x, 3, 2)` is the equivalent of `x[3:5] = b[0:2]` in Python. An alternate approach to copying arrays would be to use a loop. `arraycopy` is usually faster than a loop, and results in more compact code. The only downside is that `arraycopy` is (arguably) harder to read. Note that Java arrays only perform bounds checking at runtime. That is, the following code compiles just fine, but will crash at runtime. Try running this code locally in a java file or in the [visualizerarrow-up-right](https://goo.gl/YHufJ6) . What is the name of the error that you encounter when it crashes? Does the name of the error make sense? #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/6.-arrays#id-2d-arrays-in-java) 2D Arrays in Java What one might call a 2D array in Java is actually just an array of arrays. They follow the same rules for objects that we've already learned, but let's review them to make sure we understand how they work. Syntax for arrays of arrays can be a bit confusing. Consider the code `int[][] bamboozle = new int[4][]`. This creates an array of integer arrays called `bamboozle`. Specifically, this creates exactly four memory boxes, each of which can point to an array of integers (of unspecified length). Try running the code below line-by-lines, and see if the results match your intuition. For an interactive visualization, click [herearrow-up-right](http://goo.gl/VS4cOK) . **Exercise 2.4.1:** After running the code below, what will be the values of x\[0\]\[0\] and w\[0\]\[0\]? Check your work by clicking [herearrow-up-right](http://goo.gl/fCZ9Dr) . #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/6.-arrays#arrays-vs-classes) Arrays vs. Classes Both arrays and classes can be used to organize a bunch of memory boxes. In both cases, the number of memory boxes is fixed, i.e. the length of an array cannot be changed, just as class fields cannot be added or removed. The key differences between memory boxes in arrays and classes: * Array boxes are numbered and accessed using `[]` notation, and class boxes are named and accessed using dot notation. * Array boxes must all be the same type. Class boxes can be different types. One particularly notable impact of these difference is that `[]` notation allows us to specify which index we'd like at runtime. For example, consider the code below: If we run this code, we might get something like: By contrast, specifying fields in a class is not something we do at runtime. For example, consider the code below: If we tried compiling this, we'd get a syntax error. The same problem occurs if we try to use dot notation: Compiling, we'd get: This isn't a limitation you'll face often, but it's worth pointing out, just for the sake of good scholarship. For what it's worth, there is a way to specify desired fields at runtime called _reflection_, but it is considered very bad coding style for typical programs. You can read more about reflection [herearrow-up-right](https://docs.oracle.com/javase/tutorial/reflect/member/fieldValues.html) . **You should never use reflection in any 61B program**, and we won't discuss it in our course. In general, programming languages are partially designed to limit the choices of programmers to make code simpler to reason about. By restricting these sorts of features to the special Reflections API, we make typical Java programs easier to read and interpret. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/6.-arrays#appendix-java-arrays-vs-other-languages) Appendix: Java Arrays vs. Other Languages Compared to arrays in other languages, Java arrays: * Have no special syntax for "slicing" (such as in Python). * Cannot be shrunk or expanded (such as in Ruby). * Do not have member methods (such as in Javascript). * Must contain values only of the same type (unlike Python). [Previous5\. DLListschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/5.-dllists) [Next7\. Testingchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/7.-testing) Last updated 6 months ago * [Array Basics](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/6.-arrays#array-basics) * [Appendix: Extra Information on Arrays](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/6.-arrays#appendix-extra-information-on-arrays) Copy int[] z = null; int[] x, y; x = new int[]{1, 2, 3, 4, 5}; y = x; x = new int[]{-1, 2, 5, 4, 99}; y = new int[3]; z = new int[0]; int xL = x.length; String[] s = new String[6]; s[4] = "ketchup"; s[x[3] - x[1]] = "muffins"; int[] b = {9, 10, 11}; System.arraycopy(b, 0, x, 3, 2); Copy int[] x = {9, 10, 11, 12, 13}; int[] y = new int[2]; int i = 0; while (i < x.length) { y[i] = x[i]; i += 1; } Copy int[][] pascalsTriangle; pascalsTriangle = new int[4][]; int[] rowZero = pascalsTriangle[0]; pascalsTriangle[0] = new int[]{1}; pascalsTriangle[1] = new int[]{1, 1}; pascalsTriangle[2] = new int[]{1, 2, 1}; pascalsTriangle[3] = new int[]{1, 3, 3, 1}; int[] rowTwo = pascalsTriangle[2]; rowTwo[1] = -5; int[][] matrix; matrix = new int[4][]; matrix = new int[4][4]; int[][] pascalAgain = new int[][]{{1}, {1, 1}, {1, 2, 1}, {1, 3, 3, 1}}; Copy int[][] x = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; int[][] z = new int[3][]; z[0] = x[0]; z[1] = x[1]; z[2] = x[2]; z[0][0] = -z[0][0]; int[][] w = new int[3][3]; System.arraycopy(x[0], 0, w[0], 0, 3); System.arraycopy(x[1], 0, w[1], 0, 3); System.arraycopy(x[2], 0, w[2], 0, 3); w[0][0] = -w[0][0]; Copy int indexOfInterest = askUserForInteger(); int[] x = {100, 101, 102, 103}; int k = x[indexOfInterest]; System.out.println(k); Copy $ javac arrayDemo $ java arrayDemo What index do you want? 2 102 Copy String fieldOfInterest = "mass"; Planet p = new Planet(6e24, "earth"); double mass = p[fieldOfInterest]; Copy $ javac classDemo FieldDemo.java:5: error: array required, but Planet found double mass = earth[fieldOfInterest]; ^ Copy String fieldOfInterest = "mass"; Planet p = new Planet(6e24, "earth"); double mass = p.fieldOfInterest; Copy $ javac classDemo FieldDemo.java:5: error: cannot find symbol double mass = earth.fieldOfInterest; ^ symbol: variable fieldOfInterest location: variable earth of type Planet --- # 9. Inheritance I: Interface and Implementation Inheritance | CS61B Textbook Fall 2025 [9.1 The Problem of Generalitychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.1-the-problem-of-generality) [9.2 Hypernyms, Hyponyms, and the Implements Keywordchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.2-hypernyms-hyponyms-and-the-implements-keyword) [9.3 Overriding, Interface Inheritancechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.3-overriding-interface-inheritance) [9.4 Implementation Inheritance, defaultchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.4-implementation-inheritance-default) [9.5 Implementation vs. Interface Inheritancechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.5-implementation-vs.-interface-inheritance) [9.6 Abstract Data Typeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.6-abstract-data-types) [Previous8\. ArrayListchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/8.-arraylist) [Next9.1 The Problem of Generalitychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.1-the-problem-of-generality) Last updated 6 months ago --- # 17. Asymptotics III | CS61B Textbook Fall 2025 [17.1 Recursionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/17.-asymptotics-iii/17.1-recursion) [17.2 Binary Searchchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/17.-asymptotics-iii/17.2-binary-search) [17.3 Mergesortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/17.-asymptotics-iii/17.3-mergesort) [17.4 B-trees Big Ochevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/17.-asymptotics-iii/17.4-b-trees-big-o) [Previous16.6 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.6-exercises) [Next17.1 Recursionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/17.-asymptotics-iii/17.1-recursion) Last updated 4 months ago --- # 38.8 Exercises | CS61B Textbook [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.8-exercises#factual) Factual ----------------------------------------------------------------------------------------------------------------------- 1. Suppose we build a Shannon Fano or Huffman code for the text of this question including spaces and punctuation characters. Which characters would have the longest code? 2. What two ways could we represent a Huffman code for characters in Java? chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.8-exercises#problem-1) `?` and `.`, since both are only used once in the above sentence. chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.8-exercises#problem-2) A `HashMap` or a `BitSequence[]`. Note that the two are equivalent in Java because a Character is a number. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.8-exercises#procedural) Procedural ----------------------------------------------------------------------------------------------------------------------------- 1. Suppose we have a string `abcdefg` which repeats 1000 times. How many bits would be in the compressed bitstream? chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.8-exercises#problem-1-1) Since all 8 characters are equal in frequency, we get a balanced binary tree as our Huffman encoding, so all codewords are 3 bits long. 1000 \* 8 \* 3 = 24000 bits. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.8-exercises#metacognitive) Metacognitive ----------------------------------------------------------------------------------------------------------------------------------- 1. Using the idea of self-extracting bits, come up with an encoding for the sequence `abdefg` repeated 1000 times that uses less than 2000 bits. chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.8-exercises#problem-1-2) The idea of self-extracting bits includes writing code or an interpreter that can generate the original uncompressed sequence. This can be done with the following code: This code uses exactly 239 characters, or 1912 bits. This demonstrates the power of the self-extracting bits model: compare this to the 24000 bits required for a Huffman code. [Previous38.7 Summarychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.7-summary) [Next39\. Compression, Complexity, P = NPchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np) Last updated 2 years ago * [Factual](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.8-exercises#factual) * [Procedural](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.8-exercises#procedural) * [Metacognitive](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.8-exercises#metacognitive) sun-brightdesktopmoon Copy public class Sequence { public static void main(String[] args) { for (int i = 0; i < 1000; i++) { for (int j = 0; j < 8; j++) { System.out.print(String.format("%c", 'a' + j)); } } } } sun-brightdesktopmoon --- # 36.2 The Just-In-Time Compiler | CS61B Textbook Java’s Just-In-Time Compiler secretly optimizes your code when it runs. * The code you write is not necessarily the code that executes! * As your code runs, the “interpreter” is watching everything that happens. * If some segment of code is called many times, the interpreter actually studies and re-implements your code based on what it learned by watching WHILE ITS RUNNING (!!). * Example: Performing calculations whose results are unused. * See [this videoarrow-up-right](https://www.youtube.com/watch?v=oH4_unx8eJQ) if you’re curious. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FDttFIcU4rM7lsTU6dh71%252Fimage.png%3Falt%3Dmedia%26token%3D1b549d73-198a-43f5-aecf-65f471c71ba7&width=768&dpr=3&quality=100&sign=e5297fc0&sv=2) [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.2-the-just-in-time-compiler#jit-example) JIT Example ----------------------------------------------------------------------------------------------------------------------------------------------------------- The code below creates Linked Lists, 1000 at a time. * Repeating this 500 times yields an interesting result. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FDMhOIbzXpvuhvHs3g1CB%252Fimage.png%3Falt%3Dmedia%26token%3Dbffbfc80-7e98-4b3c-993c-1aa7fcf89da7&width=768&dpr=3&quality=100&sign=f7d9ec53&sv=2) * First optimization: Not sure what it does. * Second optimization: Stops creating linked lists since we’re not actually using them. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FxtUdru0AZHxHyNmHNDAn%252Fimage.png%3Falt%3Dmedia%26token%3D3a01e626-c19e-4ae3-b13c-29892aebf644&width=768&dpr=3&quality=100&sign=85277288&sv=2) [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.2-the-just-in-time-compiler#so-which-is-better-msd-or-mergesort) … So Which is Better? MSD or MergeSort? --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- The performance of the merge sort algorithm is highly dependent on the presence of just-in-time (JIT) compilation. Specifically, when JIT is enabled, merge sort is faster in cases where the strings being sorted are equal, but slower when JIT is disabled. This suggests that merge sort is generally more effective for this specific case, given that JIT is typically enabled. However, there are numerous other scenarios to consider, including the sorting of almost equal strings, randomized strings, and real-world data from specific datasets. When assessing the effectiveness of merge sort for these alternative cases, it is important to conduct careful experimentation and profiling to determine which sorting algorithm is most suitable. The lectureCode repository provides code for running these experiments, allowing for more precise assessment of algorithm performance under various conditions. Ultimately, real-world applications will require a thorough analysis of different implementations on actual data in order to select the optimal algorithm for the task at hand. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.2-the-just-in-time-compiler#bottom-line-algorithms-can-be-hard-to-compare) Bottom Line: Algorithms Can Be Hard to Compare -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Comparing algorithms that have the same order of growth can be a difficult task, as it requires conducting computational experiments to determine their relative performance. However, modern programming environments can introduce additional challenges to this process, as certain optimizations such as just-in-time (JIT) compilation in Java can impact the results of experiments. It is worth noting that even small optimizations to an algorithm can have a significant impact on its performance. For instance, a change to the Quicksort algorithm suggested by Vladimir Yaroslavskiy has been shown to provide notable improvements, as discussed briefly in the Quicksort lecture. Therefore, when comparing algorithms with similar growth rates, it is crucial to remain vigilant of potential optimizations and variations that may influence their performance. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.2-the-just-in-time-compiler#jit-compilers-are-always-evolving) JIT Compilers Are Always Evolving ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- The JIT (just-in-time) compiler is a highly complex and essential component of modern compilers, and is an active area of research and development in this field. However, the older JIT compiler known as C2 has become increasingly difficult to maintain and extend, with no major improvements implemented in recent years. The codebase for C2 is written in a specific dialect of C++, making it challenging for new engineers to understand and work with. As a result, the codebase is being abandoned in favor of newer and more maintainable alternatives. For individuals interested in this area of study, CS164 offers a course on compilers and there are opportunities for involvement in ongoing research. It is worth noting that the reasons for the improved performance of merge sort with the JIT are not yet fully understood, making it an interesting topic for further research. [Previous36.1 Radix vs. Comparison Sortingchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting) [Next36.3 Radix Sorting Integerschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.3-radix-sorting-integers) Last updated 2 years ago * [JIT Example](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.2-the-just-in-time-compiler#jit-example) * [… So Which is Better? MSD or MergeSort?](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.2-the-just-in-time-compiler#so-which-is-better-msd-or-mergesort) * [Bottom Line: Algorithms Can Be Hard to Compare](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.2-the-just-in-time-compiler#bottom-line-algorithms-can-be-hard-to-compare) * [JIT Compilers Are Always Evolving](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.2-the-just-in-time-compiler#jit-compilers-are-always-evolving) sun-brightdesktopmoon sun-brightdesktopmoon --- # 38.6 LZW Compression | CS61B Textbook [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.6-lzw-compression#key-idea) Key Idea ------------------------------------------------------------------------------------------------------------------------------- The LWZ approach is based on the idea of exploiting redundancy and patterns in the input data to achieve compression. Basically, each codeword can represent multiple symbols. For example, imagine a sequence of symbols `ABCABCA`. In traditional compression, each symbol would be mapped to a fixed-length codeword, resulting in a compressed sequence like `010001001000100100`. With the LWZ approach, the codewords can be based on patterns in the input data. In this case, the algorithm might start with a codeword table. (A --> 0, B --> 1, C --> 2). This could result in a compressed sequence looking something like `01201201`. By allowing for codewords that can represent multiple symbols, the LWZ approach can achieve more efficient compression than traditional approaches. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.6-lzw-compression#algorithm) Algorithm --------------------------------------------------------------------------------------------------------------------------------- * The algorithm starts with a simple codeword table where each codeword corresponds to a single symbol. * Whenever a codeword is used, a new codeword is created by concatenating the previous codeword with the next symbol. * The algorithm does not specify what happens when the codeword table becomes full, but there are many variants of the algorithm that handle this differently. * A neat fact about the LWZ approach is that it is possible to reconstruct the codeword table from the compressed bitstream alone, without needing to send the table along with the compressed data. * LWZ decompression [demoarrow-up-right](https://docs.google.com/presentation/d/1U8XO6CWfcU4QgrFOZmGjAgmaKxLc8HXk6qB1JQVlqrg/edit#slide=id.g53705ba95_0259) . [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.6-lzw-compression#fun-facts) Fun Facts --------------------------------------------------------------------------------------------------------------------------------- * The algorithm is named after its inventors, Lempel, Ziv, and Welch. * The LWZ algorithm is used as a component in many compression tools, including .gif files, .zip files, and more. * The LWZ algorithm was once controversial due to attempts to enforce licensing fees, but the patent expired in 2003. [Previous38.5 Compression Theorychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.5-compression-theory) [Next38.7 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.7-summary) Last updated 2 years ago * [Key Idea](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.6-lzw-compression#key-idea) * [Algorithm](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.6-lzw-compression#algorithm) * [Fun Facts](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.6-lzw-compression#fun-facts) sun-brightdesktopmoon sun-brightdesktopmoon --- # 36.4 Summary | CS61B Textbook **Radix Sort vs. Comparison Sorts.** In lecture, we used the number of characters examined as a cost model to compare radix sort and comparison sort. For MSD Radix Sort, the worst case is that each character is examined once for NM characters examined. For merge sort, MNlogN is the worst case characters examined. Thus, we can see that merge sort is slower by a factor of logN if character comparisons are an appropriate cost model. Using an empirical analysis, however, we saw that this does not hold true because of lots of background reasons such as the cache, optimized methods, extra copy operations, and overall because our cost model does not account for everything happening. **Just-In-Time Compiler.** The “interpreter” studies your code as it runs so that when a sequence of code is run many times, it studies and re-implements based on what it learns while running to optimize it. For example, if a LinkedList is created many times in a loop and left unused, it eventually learns to stop creating the LinkedLists since they are never used. With the Just-In-Time compiler disabled, merge sort, from the previous section, is indeed slower than MSD Radix Sort. **Radix Sorting Integers.** When radix sorting integers, we no longer have a charAt method. There are lots of alternative options are stilizing mods and division to write your own getDigit() method or to make each Integer into a String. However, we don’t actually have to stick to base 10 and can instead treat the numbers as base 16, 256, or even base 65536 numbers. Thus, we can reduce the number of digits, which can reduces the runtime since runtime for radix sort depends on alphabet size. [Previous36.3 Radix Sorting Integerschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.3-radix-sorting-integers) [Next36.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.5-exercises) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 14. Disjoint Sets | CS61B Textbook Fall 2025 [14.1 Introductionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.1-introduction) [14.2 Quick Findchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.2-quick-find) [14.3 Quick Unionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.3-quick-union) [14.4 Weighted Quick Union (WQU)chevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.4-weighted-quick-union-wqu) [14.5 Weighted Quick Union with Path Compressionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.5-weighted-quick-union-with-path-compression) [14.6 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.6-exercises) [Previous13.7 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.7-exercises) [Next14.1 Introductionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.1-introduction) Last updated 6 months ago --- # 38. Compression and Complexity | CS61B Textbook [38.1 Introduction to Compressionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.1-introduction-to-compression) [38.2 Prefix-free Codeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.2-prefix-free-codes) [38.3 Shannon-Fano Codeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.3-shannon-fano-codes) [38.4 Huffman Coding Conceptualschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.4-huffman-coding-conceptuals) [38.5 Compression Theorychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.5-compression-theory) [38.6 LZW Compressionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.6-lzw-compression) [38.7 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.7-summary) [38.8 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.8-exercises) [Previous37.1 The end is nearchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/37.-software-engineering-iv/37.1-the-end-is-near) [Next38.1 Introduction to Compressionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.1-introduction-to-compression) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 38.7 Summary | CS61B Textbook **Compression Model #1: Algorithms Operating on Bits.** Given a sequence of bits B, we put them through a compression algorithm C to form a new bitstream C(B). We can run C(B) through a corresponding decompression algorithm to recover B. Ideally, C(B) is less than B. **Variable Length Codewords.** Basic idea: Use variable length codewords to represent symbols, with shorter keywords going with more common symbols. For example, instead of representing every English character by a 8 bit ASCII value, we can represent more common values with shorter sequences. Morse code is an example of a system of variable length codewords. **Prefix Free Codes.** If some codewords are prefixes of others, then we have ambiguity, as seen in Morse Code. A prefix free code is a code where no codeword is a prefix of any other. Prefix free codes can be uniquely decoded. **Shannon-Fano Coding.** Shannon-Fano coding is an intuitive procedure for generating a prefix free code. First, one counts the occurrence of all symbols. Then you recursively split characters into halves over and over based on frequencies, with each half either having a 1 or a 0 appended to the end of the codeword. **Huffman Coding.** Huffman coding generates a provably optimal prefix free code, unlike Shannon-Fano, which can be suboptimal. First, one counts the occurrence of all symbols, and create a “node” for each symbol. We then merge the two lowest occurrence nodes into a tree with a new supernode as root, with each half either having a 1 or a 0 appended to the beginning of the codeword. We repeat this until all symbols are part of the tree. Resulting code is optimal. **Huffman Implementation.** To compress a sequence of symbols, we count frequencies, build an encoding array and a decoding trie, write the trie to the output, and then look up each symbol in the encoding array and write out the appropriate bit sequence to the output. To decompress, we read in the trie, then repeatedly use longest prefix matching to recover the original symbol. **General Principles Behind Compression.** Huffman coding is all about representing common symbols with a small number of bits. There are other ideas, like run length encoding where you replace every character by itself followed by its number of occurrences, and LZW which searches for common repeated patterns in the input. More generally, the goal is to exploit redundancy and existing order in the input. **Universal Compression is Impossible.** It is impossible to create an algorithm that can compress any bitstream by 50%. Otherwise, you could just compress repeatedly until you ended up with just 1 bit, which is clearly absurd. A second argument is that for an input bitstream of say, size 1000, only 1 in 2^499 is capable of being compressed by 50%, due to the pigeonhole principle. **Compression Model #2: Self Extracting Bits.** Treating the algorithm and the input bitstream separately (like we did in model #1) is a more accurate model, but it seems to leave open strange algorithms like one in which we simply hardcode our desired output into the algorithm itself. For example, we might have a .java decompression algorithm that has a giant `byte[]` array of your favorite TV show, and if the algorithm gets the input `010`, it outputs this `byte[]` array. In other words, it seems to make more sense to include not just the compressed bits when considering the size of our output, but also the algorithm used to do the decompression. One conceptual trick to make this more concrete is to imagine that our algorithm and the bits themselves are a single entity, which we can think of a self-extracting bit sequence. When fed to an interpreter, this self-extracting bit sequence generates a particular output sequence. **Hugplant Example.** If we have an image file of something like the hugplant.bmp from lecture, we can break it into 8 bit chunks and then Huffman encode it. If we give this file to someone else, they probably won’t know how to decompress it, since Huffman coding is not a standard compression algorithm supported by major operating systems. Thus, we also need to provide the Huffman decoding algorithm. We could send this as a separate .java file, but for conceptual convenience and in line with compression model #2, we’ll imagine that we have packaged our compressed bit stream into a `byte[]` array in a .java file. When passed to an interpreter, this bitstream yields the original hugplant.bmp, which is 4 times larger than the compressed bitstream + huffman interpreter. [Previous38.6 LZW Compressionchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.6-lzw-compression) [Next38.8 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.8-exercises) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 2. Defining and Using Classes | CS61B Textbook Fall 2025 If you do not have prior Java experience, we recommend that you work through the exercises in [HW0arrow-up-right](http://sp19.datastructur.es/materials/hw/hw0/hw0.html) before reading this chapter. It will cover various syntax issues that we will not discuss in the book. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/2.-defining-and-using-classes#static-vs-non-static-methods) Static vs. Non-Static Methods **Static Methods** All code in Java must be part of a class (or something similar to a class, which we'll learn about later). Most code is written inside of methods. Let's consider an example: Copy public class Dog { public static void makeNoise() { System.out.println("Bark!"); } } If we try running the `Dog` class, we'll simply get an error message: Copy $ java Dog Error: Main method not found in class Dog, please define the main method as: public static void main(String[] args) The `Dog` class we've defined doesn't do anything. We've simply defined something that `Dog` **can** do, namely make noise. To actually run the class, we'd either need to add a main method to the `Dog` class, as we saw in chapter 1.1. Or we could create a separate [`DogLauncher`arrow-up-right](https://www.youtube.com/watch?v=Q-LE-jJQLTM) class that runs methods from the `Dog` class. For example, consider the program below: Copy public class DogLauncher { public static void main(String[] args) { Dog.makeNoise(); } } A class that uses another class is sometimes called a "client" of that class, i.e. `DogLauncher` is a client of `Dog`. Neither of the two techniques is better: Adding a main method to `Dog` may be better in some situations, and creating a client class like `DogLauncher` may be better in others. The relative advantages of each approach will become clear as we gain additional practice throughout the course. **Instance Variables and Object Instantiation** Not all dogs are alike. Some dogs like to yap incessantly, while others bellow sonorously, bringing joy to all who hear their glorious call. Often, we write programs to mimic features of the universe we inhabit, and Java's syntax was crafted to easily allow such mimicry. One approach to allowing us to represent the spectrum of Dogdom would be to create separate classes for each type of Dog. As you should have seen in the past, classes can be instantiated, and instances can hold data. This leads to a more natural approach, where we create instances of the `Dog` class and make the behavior of the `Dog` methods contingent upon the properties of the specific `Dog`. To make this more concrete, consider the class below: As an example of using such a Dog, consider: When run, this program will create a `Dog` with weight 20, and that `Dog` will soon let out a nice "bark. bark.". Some key observations and terminology: * An `Object` in Java is an instance of any class. * The `Dog` class has its own variables, also known as _instance variables_ or _non-static variables_. These must be declared inside the class, unlike languages like Python or Matlab, where new variables can be added at runtime. * The method that we created in the `Dog` class did not have the `static` keyword. We call such methods _instance methods_ or _non-static methods_. * To call the `makeNoise` method, we had to first _instantiate_ a `Dog` using the `new` keyword, and then make a specific `Dog` bark. In other words, we called `d.makeNoise()` instead of `Dog.makeNoise()`. * Once an object has been instantiated, it can be _assigned_ to a _declared_ variable of the appropriate type, e.g. `d = new Dog();` * Variables and methods of a class are also called _members_ of a class. * Members of a class are accessed using _dot notation_. **Constructors in Java** As you've hopefully seen before, we usually construct objects in object oriented languages using a _constructor_: Here, the instantiation is parameterized, saving us the time and messiness of manually typing out potentially many instance variable assignments. To enable such syntax, we need only add a "constructor" to our Dog class, as shown below: The constructor with signature `public Dog(int w)` will be invoked anytime that we try to create a `Dog` using the `new` keyword and a single integer parameter. For those of you coming from Python, the constructor is very similar to the `__init__` method. **Terminology Summary** **Array Instantiation, Arrays of Objects** As we saw in HW0, arrays are also instantiated in Java using the new keyword. For example: Similarly, we can create arrays of instantiated objects in Java, e.g. Observe that new is used in two different ways: Once to create an array that can hold two `Dog` objects, and twice to create each actual `Dog`. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/2.-defining-and-using-classes#class-methods-vs-instance-methods) Class Methods vs. Instance Methods Java allows us to define two types of methods: * Class methods, a.k.a. static methods. * Instance methods, a.k.a. non-static methods. Instance methods are actions that can be taken only by a specific instance of a class. Static methods are actions that are taken by the class itself. Both are useful in different circumstances. As an example of a static method, the `Math` class provides a `sqrt` method. Because it is static, we can call it as follows: If `sqrt` had been an instance method, we would have instead the awkward syntax below. Luckily `sqrt` is a static method so we don't have to do this in real programs. Sometimes, it makes sense to have a class with both instance and static methods. For example, suppose want the ability to compare two dogs. One way to do this is to add a static method for comparing Dogs. This method could be invoked by, for example: Observe that we've invoked using the class name, since this method is a static method. We could also have implemented `maxDog` as a non-static method, e.g. Above, we use the keyword `this` to refer to the current object. This method could be invoked, for example, with: Here, we invoke the method using a specific instance variable. **Exercise 1.2.1**: What would the following method do? If you're not sure, try it out. **Static Variables** It is occasionally useful for classes to have static variables. These are properties inherent to the class itself, rather than the instance. For example, we might record that the scientific name (or binomen) for Dogs is "Canis familiaris": Static variables should be accessed using the name of the class rather than a specific instance, e.g. you should use `Dog.binomen`, not `d.binomen`. While Java technically allows you to access a static variable using an instance name, it is bad style, confusing, and in my opinion an error by the Java designers. **Exercise 1.2.2**: Complete this exercise: * Video: [linkarrow-up-right](https://youtu.be/8Gq-8mVbyFU) * Slide: [linkarrow-up-right](https://docs.google.com/presentation/d/10BFLHH8VaoYy7XaazwjaoTtLw3zvasX4HCssDruqw84/edit#slide=id.g6caa9a6fe_057) * Solution Video: [linkarrow-up-right](https://youtu.be/Osuy8UEH03M) #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/2.-defining-and-using-classes#public-static-void-mainstring-args) public static void main(String\[\] args) With what we've learned so far, it's time to demystify the declaration we've been using for the main method. Breaking it into pieces, we have: * `public`: So far, all of our methods start with this keyword. * `static`: It is a static method, not associated with any particular instance. * `void`: It has no return type. * `main`: This is the name of the method. * `String[] args`: This is a parameter that is passed to the main method. **Command Line Arguments** Since main is called by the Java interpreter itself rather than another Java class, it is the interpreter's job to supply these arguments. They refer usually to the command line arguments. For example, consider the program `ArgsDemo` below: This program prints out the 0th command line argument, e.g. In the example above, `args` will be an array of Strings, where the entries are {"these", "are", "command", "line", "arguments"}. **Summing Command Line Arguments** **Exercise 1.2.3**: Try to write a program that sums up the command line arguments, assuming they are numbers. For a solution, see the webcast or the code provided on GitHub. [Previous1.3 Basic Java Featureschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/1.-introduction/1.3-basic-java-features) [Next3\. References, Recursion, and Listschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/3.-references-recursion-and-lists) Last updated 6 months ago Copy $ java DogLauncher Bark! Copy public class TinyDog { public static void makeNoise() { System.out.println("yip yip yip yip"); } } public class MalamuteDog { public static void makeNoise() { System.out.println("arooooooooooooooo!"); } } Copy public class Dog { public int weightInPounds; public void makeNoise() { if (weightInPounds < 10) { System.out.println("yipyipyip!"); } else if (weightInPounds < 30) { System.out.println("bark. bark."); } else { System.out.println("woof!"); } } } Copy public class DogLauncher { public static void main(String[] args) { Dog d; d = new Dog(); d.weightInPounds = 20; d.makeNoise(); } } Copy public class DogLauncher { public static void main(String[] args) { Dog d = new Dog(20); d.makeNoise(); } } Copy public class Dog { public int weightInPounds; public Dog(int w) { weightInPounds = w; } public void makeNoise() { if (weightInPounds < 10) { System.out.println("yipyipyip!"); } else if (weightInPounds < 30) { System.out.println("bark. bark."); } else { System.out.println("woof!"); } } } Copy public class ArrayDemo { public static void main(String[] args) { /* Create an array of five integers. */ int[] someArray = new int[5]; someArray[0] = 3; someArray[1] = 4; } } Copy public class DogArrayDemo { public static void main(String[] args) { /* Create an array of two dogs. */ Dog[] dogs = new Dog[2]; dogs[0] = new Dog(8); dogs[1] = new Dog(20); /* Yipping will result, since dogs[0] has weight 8. */ dogs[0].makeNoise(); } } Copy x = Math.sqrt(100); Copy Math m = new Math(); x = m.sqrt(100); Copy public static Dog maxDog(Dog d1, Dog d2) { if (d1.weightInPounds > d2.weightInPounds) { return d1; } return d2; } Copy Dog d = new Dog(15); Dog d2 = new Dog(100); Dog.maxDog(d, d2); Copy public Dog maxDog(Dog d2) { if (this.weightInPounds > d2.weightInPounds) { return this; } return d2; } Copy Dog d = new Dog(15); Dog d2 = new Dog(100); d.maxDog(d2); Copy public static Dog maxDog(Dog d1, Dog d2) { if (weightInPounds > d2.weightInPounds) { return this; } return d2; } Copy public class Dog { public int weightInPounds; public static String binomen = "Canis familiaris"; ... } Copy public class ArgsDemo { public static void main(String[] args) { System.out.println(args[0]); } } Copy $ java ArgsDemo these are command line arguments these --- # 15. Asymptotics II | CS61B Textbook Fall 2025 This chapter covers various asymptotic analysis examples, which provide useful insights on how to analyze the efficiency of algorithms. [Previous14.6 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.6-exercises) [Next15.1 Big Thetachevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.1-big-theta) Last updated 6 months ago --- # 38.5 Compression Theory | CS61B Textbook [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.5-compression-theory#compression-ratios) Compression Ratios ------------------------------------------------------------------------------------------------------------------------------------------------------ * The goal of data compression is to reduce the size of a sequence of data while retaining as much information as possible. For example, the letter `e` appears more frequently in the English dictionary than `z`, so we would want to represent `e` with smaller bits. * **Compression ratio** is a measure of how much the size of the compressed data differs from the original data. * **Huffman Coding** is a compression technique that represents common symbols with smaller numbers of bits, resulting in a more efficient encoding. * **Run-length encoding** is another compression technique that replaces repeated characters with the character itself and the number of times it occurs. * **LZW** is a compression technique that searches for common repeated patterns in the input and replaces them with a shorter code. * The general idea behind most compression techniques is to exploit any existing redundancy or order within the sequence to reduce the size of the data. However, if a sequence has no existing redundancy or order, compression may not be possible. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.5-compression-theory#self-extracting-bits) Self-Extracting Bits ---------------------------------------------------------------------------------------------------------------------------------------------------------- * Self-extracting bits is a compression technique that wraps the compressed bits and the decompression algorithm into a **single sequence of bits**. * The goal is to simplify the compression and decompression process by combining the two steps into one. Self-extracting bits can be used to create executable files that can be run on any system with an interpreter (e.g. Java interpreter). ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FJemTikv95FxAqt71xrYG%252FScreenshot%25202023-04-24%2520at%25209.34.44%2520PM.png%3Falt%3Dmedia%26token%3Db0f9e455-4563-43ad-974f-63e8dd84b508&width=768&dpr=3&quality=100&sign=2221c21d&sv=2) [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.5-compression-theory#hugplant-example) HugPlant Example -------------------------------------------------------------------------------------------------------------------------------------------------- * To compress an image file like `hugplant.bmp`, we can break it into 8-bit chunks and Huffman encode each chunk. * We package the compressed data plus decoder into a single self-extracting `.java` file, represented as a `byte[]` array. * When the `byte[]` array is passed to an interpreter, the interpreter executes the Huffman decoding algorithm and produces the original `hugplant.bmp` image file. * The size of the compressed bitstream and the Huffman decoding algorithm combined is **smaller** than the original image file, making it more efficient to store and transmit. * However, the receiver of the compressed file must have access to the appropriate interpreter to decode and reconstruct the original image. [Previous38.4 Huffman Coding Conceptualschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.4-huffman-coding-conceptuals) [Next38.6 LZW Compressionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.6-lzw-compression) Last updated 2 years ago * [Compression Ratios](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.5-compression-theory#compression-ratios) * [Self-Extracting Bits](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.5-compression-theory#self-extracting-bits) * [HugPlant Example](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.5-compression-theory#hugplant-example) sun-brightdesktopmoon sun-brightdesktopmoon --- # 38.2 Prefix-free Codes | CS61B Textbook Consider the representation of English text in Java. We represent text as a sequence of characters, each taking 8 bits of memory. One easy way to compress, then, is to simply use less than 8 bits per character. To do this, we have to decide which **codewords** (bit sequences) go with each **symbol** (character). [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.2-prefix-free-codes#mapping-alphanumeric-symbols) Mapping Alphanumeric Symbols ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.2-prefix-free-codes#morse-code) Morse Code As an introductory example, consider the Morse code alphabet. Looking at the alphabet below, what does the sequence – – • – – • represent? It’s ambiguous! The same sequence of symbols can represent either MEME, or GG, depending on what you choose – – • to represent ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252F4s5DkbcGzBoxpRc8Bb3y%252FScreen%2520Shot%25202023-04-24%2520at%25205.44.37%2520PM.png%3Falt%3Dmedia%26token%3Dbf521f7d-02f5-4c7e-95bb-a5159e9231c7&width=768&dpr=3&quality=100&sign=b50700cb&sv=2) Ambiguity in morse code In real usage, operators must pause between codewords to indicate a break. The pause acts as an implicit third symbol, but we can't encode this real-time information into our code. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.2-prefix-free-codes#prefix-free-codes) Prefix-free Codes An alternate strategy to avoid the need for real-time is to use **prefix-free codes**. In a prefix-free code, no codeword is a prefix of any other. In the Morse Code example, there would be no confusion whether the – – in the pattern – – • – – • is supposed to represent M, or the start of G. Let's represent Morse code as a tree of codewords leading to symbols. As we can see from the tree, several symbols have representations that are prefixes of other symbols. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FjFQrGPOHJtKo7EvY3GaL%252Fimage.png%3Falt%3Dmedia%26token%3Dd25e6cd8-3b6f-4868-adfb-8b976526d363&width=768&dpr=3&quality=100&sign=819fd819&sv=2) Morse code is not prefix-free. As an example of an (arbitrary) prefix-free code, consider the following encoding: ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FPaawkPuLbznL6uT9XN6j%252Fimage.png%3Falt%3Dmedia%26token%3D1e5ba4ff-a85f-477b-8b38-2a8f63536a9f&width=768&dpr=3&quality=100&sign=228ed92c&sv=2) One prefix-free code. The following code is also prefix-free: ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252F9XersVXbKTaZbKbgjsFK%252Fimage.png%3Falt%3Dmedia%26token%3Ddd5dd3a3-cb16-40c3-ad25-754a3948c8f7&width=768&dpr=3&quality=100&sign=f003b6aa&sv=2) Another prefix-free code. Note that some codes are more efficient for certain strings than others: in the first representation, `I ATE` uses less bits than the second code. However, this is highly dependent on what string we're trying to encode. [Previous38.1 Introduction to Compressionchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.1-introduction-to-compression) [Next38.3 Shannon-Fano Codeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.3-shannon-fano-codes) Last updated 2 years ago * [Mapping Alphanumeric Symbols](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.2-prefix-free-codes#mapping-alphanumeric-symbols) * [Morse Code](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.2-prefix-free-codes#morse-code) * [Prefix-free Codes](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.2-prefix-free-codes#prefix-free-codes) sun-brightdesktopmoon sun-brightdesktopmoon --- # 13. Asymptotics I | CS61B Textbook Fall 2025 [13.1 An Introduction to Asymptotic Analysischevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.1-an-introduction-to-asymptotic-analysis) [13.2 Runtime Characterizationchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.2-runtime-characterization) [13.3 Checkpoint: An Exercisechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.3-checkpoint-an-exercise) [13.4 Asymptotic Behaviorchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.4-asymptotic-behavior) [13.5 Simplified Analysis Processchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.5-simplified-analysis-process) [13.6 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.6-summary) [13.7 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.7-exercises) [Previous12.6 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.6-exercises) [Next13.1 An Introduction to Asymptotic Analysischevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.1-an-introduction-to-asymptotic-analysis) Last updated 6 months ago --- # 1.2 Java Workflow | CS61B Textbook Fall 2025 Taking a program from a `.java` file into an executable has two main steps in Java: **compilation** and **interpretation**. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-704d784ff81f7d164c6b5a70f23f125d7f46be5c%252F1-2-compile-interpret.svg%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=ba7e1d43&sv=2) To run the code in `Hello.java`, we would first **compile** the code into a `.class` file using the command `javac HelloWorld.java`. Then, to run the code, we would use the command `java HelloWorld`. In your terminal, the result would look like the following: [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/1.-introduction/1.2-java-workflow#class-files) Class Files ----------------------------------------------------------------------------------------------------------------------------- There are several reasons for the usage of `.class` files, which we will only cover briefly here. First of all, `.class` files are guaranteed to have been type-checked, making the distributed code safer. They are also more efficient to execute, and protect the actual source code in cases of intellectual property. We will not go into the details of `.class` files in this textbook beyond knowing that they are created after compilation. [Previous1.1 Your First Java Programchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/1.-introduction/1.1-your-first-java-program) [Next1.3 Basic Java Featureschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/1.-introduction/1.3-basic-java-features) Last updated 6 months ago Copy $ javac HelloWorld.java $ java HelloWorld Hello World! --- # 16. ADTs and BSTs | CS61B Textbook Fall 2025 In this Chapter we will discuss: * Abstract Data Types * Binary Search Tree * BST Definitions * BST Operations * Sets vs. Maps, Summary Additionally the video playlist is one that accompanies this chapter is the following: [Previous15.6 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.6-exercises) [Next16.1 Binary Search Treeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.1-binary-search-trees) Last updated 6 months ago --- # 9.5 Implementation vs. Interface Inheritance | CS61B Textbook Fall 2025 ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.5-implementation-vs.-interface-inheritance#interface-inheritance-vs-implementation-inheritance) Interface Inheritance vs Implementation Inheritance How do we differentiate between "interface inheritance" and "implementation inheritance"? Well, you can use this simple distinction: * Interface inheritance (what): Simply tells what the subclasses are able to do. * EX) all lists should be able to print themselves, how they do it is up to them. * Implementation inheritance (how): Tells the subclasses how to implement their behavior. * EX) Lists should (by default) print themselves exactly this way: by getting each element in order and then printing them. [Previous9.4 Implementation Inheritance, defaultchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.4-implementation-inheritance-default) [Next9.6 Abstract Data Typeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.6-abstract-data-types) Last updated 6 months ago --- # 21. Hashing II | CS61B Textbook Fall 2025 [21.1 Hash Table Recap, Default Hash Functionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/21.-hashing-ii/21.1-hash-table-recap-default-hash-function) [21.2 Distribution By Other Hash Functionschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/21.-hashing-ii/21.2-distribution-by-other-hash-functions) [21.3 Contains & Duplicate Itemschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/21.-hashing-ii/21.3-contains-and-duplicate-items) [21.4 Mutable vs. Immutable Typeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/21.-hashing-ii/21.4-mutable-vs.-immutable-types) [Previous20.7 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.7-exercises) [Next21.1 Hash Table Recap, Default Hash Functionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/21.-hashing-ii/21.1-hash-table-recap-default-hash-function) Last updated 4 months ago --- # 18. B-Trees | CS61B Textbook Fall 2025 In this section, we build off our knowledge of binary search trees to understand a new self-balancing search tree structure: B-Trees. [Previous17.4 B-trees Big Ochevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/17.-asymptotics-iii/17.4-b-trees-big-o) [Next18.1 BST Performancechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.1-bst-performance) Last updated 4 months ago --- # 19. Red Black Trees | CS61B Textbook Fall 2025 This chapter will continue our discussion on self-balancing trees through a more colorful lens. [Previous18.7 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.7-exercises) [Next19.1 Rotating Treeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.1-rotating-trees) Last updated 4 months ago --- # 38.3 Shannon-Fano Codes | CS61B Textbook Shannon-Fano codes are an approach to create prefix-free codes based on a set of symbols/characters and their probabilities. The main idea is that we want shorter prefix-free codes for more popular characters, and longer codes for lesser used characters. The algorithm is: * Count relative frequencies of all characters in a text. * Split into ‘left’ and ‘right halves’ of roughly equal frequency. * Left half gets a leading zero. Right half gets a leading one. * Repeat. At the end, you will get a tree as shown below, with shorter paths for characters with a higher frequency, and longer paths for characters with a lower frequency. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252F9D5OVxQfZUCbTv0pBzif%252FScreen%2520Shot%25202023-04-24%2520at%25206.06.15%2520PM.png%3Falt%3Dmedia%26token%3D1ca46da8-df1e-4bff-ba42-2f564d571a21&width=768&dpr=3&quality=100&sign=f7bfc503&sv=2) However, Shannon-Fano coding is NOT optimal, so it is not used very often. [Previous38.2 Prefix-free Codeschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.2-prefix-free-codes) [Next38.4 Huffman Coding Conceptualschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.4-huffman-coding-conceptuals) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 24. Graph Traversals and Implementations | CS61B Textbook Fall 2025 [24.1 BFS & DFSchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.1-bfs-and-dfs) [24.2 Representing Graphschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.2-representing-graphs) [24.3 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.3-summary) [24.4 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.4-exercises) [Previous23.4 Graph Problemschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.4-graph-problems) [Next24.1 BFS & DFSchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.1-bfs-and-dfs) Last updated 4 months ago --- # 38.1 Introduction to Compression | CS61B Textbook As an introduction to compression, consider the processes of creating and unzipping a zip file. Copy $ zip mobydick.zip mobydick.txt adding: mobydick.txt (deflated 59%) $ ls -l -rw-rw-r-- 1 jug jug 643207 Apr 24 10:55 mobydick.txt -rw-rw-r-- 1 jug jug 261375 Apr 24 10:55 mobydick.zip Note that before and after unzipping, the file size changes! [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.1-introduction-to-compression#compression-model-1-algorithms-on-bits) Compression Model 1: Algorithms on Bits -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- In our first model of compression, we consider compression as applying a _compression algorithm_ on a sequence of bits. To reverse the compression, we apply the inverse _decompression algorithm._ ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FFG0MFuVNVZ3wL3buC33v%252Fimage.png%3Falt%3Dmedia%26token%3D4a5cc8eb-2d02-40e9-ad2f-32f039943edd&width=768&dpr=3&quality=100&sign=b3c5478e&sv=2) Compression and decompression. Say you had a text file called `example.txt`. If you were to zip that text file, you'd get `example.zip`, a zip file with a size much lesser than the original `example.txt` file. This is the main idea behind compression--a technique used to reduce file size. Then, if you were to unzip `example.zip` into a file called `unzippedexample.txt`, you would notice no difference between `example.txt` and `unzippedexample.txt.` This is an indicator of **lossless** compression, where no information is lost. [Previous38\. Compression and Complexitychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity) [Next38.2 Prefix-free Codeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.2-prefix-free-codes) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 23. Tree Traversals and Graphs | CS61B Textbook Fall 2025 [23.1 Tree Recapchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.1-tree-recap) [23.2 Tree Traversalschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.2-tree-traversals) [23.3 Graphschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.3-graphs) [23.4 Graph Problemschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.4-graph-problems) [Previous22.5 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.5-exercises) [Next23.1 Tree Recapchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.1-tree-recap) Last updated 4 months ago --- # 9.4 Implementation Inheritance, default | CS61B Textbook Fall 2025 Previously, we had an interface List61B that only had method headers identifying **what** List61B's should do. But, now we will see that we can write methods in List61B that already have their implementation filled out. These methods identify **how** hypernyms of List61B should behave. In order to do this, you must include the `default` keyword in the method signature. If we define this method in List61B Copy default public void print() { for (int i = 0; i < size(); i += 1) { System.out.print(get(i) + " "); } System.out.println(); } Then everything that implements the List61B class can use the method! However, there is one small inefficiency in this method. Can you catch it? For an SLList, the `get` method needs to jump through the entirety of the list. during each call. It's much better to just print while jumping through! We want SLList to print a different way than the way specified in its interface. To do this, we need to override it. In SLList, we implement this method; Copy @Override public void print() { for (Node p = sentinel.next; p != null; p = p.next) { System.out.print(p.item + " "); } } Now, whenever we call print() on an SLList, it will call this method instead of the one in List61B. [Previous9.3 Overriding, Interface Inheritancechevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.3-overriding-interface-inheritance) [Next9.5 Implementation vs. Interface Inheritancechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.5-implementation-vs.-interface-inheritance) Last updated 6 months ago --- # 10.4 Summary | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.4-higher-order-functions-in-java#id-10.4-summary) 10.4 Summary ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.4-higher-order-functions-in-java#natural-order-python-vs.-java) Natural Order (Python vs. Java) Today we’ve seen **polymorphism** and **function passing**. For comparing two objects using an intrinsic order (a.k.a. a natural order), Python uses a form of polymorphism called **operator overloading**. Java instead uses subtype polymorphism. Copy class Dog: def __init__(self, name, size): self.name = name self.size = size def __gt__(self, other): return self.size > other.size Copy public class Dog implements Comparable { ... @Override public int compareTo(Dog uddaDog) { return size - other.size; } } Note: Python is duck typed: Do not have to specify if > is available or not. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.4-higher-order-functions-in-java#other-orders-python-vs.-java) Other Orders (Python vs. Java) For comparing two objects using an alternate order, Python uses **function passing**, i.e. you provide a key function. By contrast, Java uses subtype polymorphism, just like for intrinsic orders. We package the comparison function in a `Comparator` object. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.4-higher-order-functions-in-java#writing-library-functions) Writing Library Functions If we want to write the code that actually uses a `Comparable` or `Comparator`, then we'll find the method specifications get a little vexing, for example: Luckily for you, you won't have to deal with this much, other than at the end of Project 1B. [Previous10.3 Writing a Max Functionchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.3-casting) [Next11\. There is no chapter 11.chevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/11.-inheritance-iii-subtype-polymorphism-comparators-comparable) Last updated 6 months ago * [10.4 Summary](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.4-higher-order-functions-in-java#id-10.4-summary) * [Natural Order (Python vs. Java)](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.4-higher-order-functions-in-java#natural-order-python-vs.-java) * [Other Orders (Python vs. Java)](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.4-higher-order-functions-in-java#other-orders-python-vs.-java) Copy class Dog: def __init__(self, name, size): self.name = name self.size = size def name_len(dog): return len(dog.name) max_dog=max(doglist, key=name_len) Copy public static class NameComparator implements Comparator { @Override public int compare(Dog a, Dog b) { return a.name.compareTo(b.name); } } Copy public class Maximizer { public static > T max(T[] items) { ... } } --- # 9.6 Abstract Data Types | CS61B Textbook Fall 2025 An Abstract Data Type (ADT) is defined only by its operations, not by its implementation. For example in Project 1A, we developed an `ArrayDeque` and a `LinkedListDeque` that had the same methods, but how those methods were written was very different. In this case, we say that `ArrayDeque` and `LinkedListDeque` are _implementations_ of the `Deque` ADT. From this description, we see that ADT's and interfaces are somewhat related. Conceptually, `Deque` is an interface for which `ArrayDeque` and `LinkedListDeque` are its implementations. In code, in order to express this relationship, we have the `ArrayDeque` and `LinkedListDeque` classes inherit from the `Deque` interface. Some commonly used ADT's are: * Stacks: Structures that support last-in first-out retrieval of elements * `push(int x)`: puts x on the top of the stack * `int pop()`: takes the element on the top of the stack * **Lists**: an ordered set of elements * `add(int i)`: adds an element * `int get(int i)`: gets element at index i * **Sets**: an unordered set of unique elements (no repeats) * `add(int i)`: adds an element * `contains(int i)`: returns a boolean for whether or not the set contains the value * **Maps**: set of key/value pairs * `put(K key, V value)`: puts a key value pair into the map * `V get(K key)`: gets the value corresponding to the key Note: the bolded ADT's are a subinterfaces of a bigger overarching interface called `Collections.` Below we show the relationships between the interfaces and classes. Interfaces are in white, classes are in blue. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-0cee0ae714c1ea8495a6774984f30563a8980200%252Fimage%2520%28133%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=dd42754&sv=2) Common interfaces in Java and their implementations ADT's allow us to make use of object oriented programming in an efficient and elegant way. For example, you saw in Project 1C how you can use an ArrayDeque or a LinkedListArrayDeque interchangeably because they are both part of the Deque ADT. In the following chapters, we will work on defining some more ADT's and enumerating their different implementations. [Previous9.5 Implementation vs. Interface Inheritancechevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.5-implementation-vs.-interface-inheritance) [Next10\. Inheritance II: Subtype Polymorphism, Comparators, Comparables, Generic Functionschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions) Last updated 6 months ago --- # 16.5 Summary | CS61B Textbook Fall 2025 Abstract data types (ADTs) are defined in terms of operations, not implementation.Several useful ADTs: * Disjoint Sets, Map, Set, List. * Java provides Map, Set, List interfaces, along with several implementations. We’ve seen two ways to implement a Set (or Map): * ArraySet: Θ(N) operations in the worst case. * BST: Θ(logN) operations if tree is balanced. BST Implementations: * Search and insert are straightforward (but insert is a little tricky). * Deletion is more challenging. Typical approach is “Hibbard deletion”. [Previous16.4 BSTs as Sets and Mapschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.4-bsts-as-sets-and-maps) [Next16.6 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.6-exercises) Last updated 4 months ago --- # 12.2 Exceptions | CS61B Textbook Fall 2025 In this section, we will learn how to throw exceptions to effectively handle errors that may arise in our code. Our `ArraySet` implementation from the previous section has a small error. When we add `null` to our ArraySet, we get a NullPointerException. The probelm lies in the `contains` method where we check `items[i].equals(x)`. If the value at `items[i]` is null, then we are calling `null.equals(x)` -> NullPointerException. Exceptions cause normal flow of control to stop. We can in fact choose to throw our own exceptions. In python you may have seen this with the `raise` keyword. In Java, Exceptions are objects and we throw exceptions using the following format: `throw new ExceptionObject(parameter1, ...)` Let's throw an exception when a user tries to add null to our `ArraySet`. We'll throw an `IllegalArgumentException` which takes in one parameter (a `String` message). Our updated `add` method: We get an Exception either way - why does this better? 1. We have control of our code: we consciously decide at what point to stop the flow of our program 2. More useful Exception type and helpful error message for those using our code However, it would be better if the program doesn't crash at all. There are different things we could do in this case. Here are some below: **Approach 1**: Don't add `null` to the array if it is passed into `add` **Approach 2**: Change the `contains` method to account for the case if `items[i] == null`. Whatever you decide, it is important that users know what to expect. That is why documentation (such as comments about your methods) is very important.\\ [Previous12.1 Lists and Sets in Javachevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.1-lists-and-sets-in-java) [Next12.3 Iterationchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.3-iteration) Last updated 6 months ago Copy /* Associates the specified value with the specified key in this map. Throws an IllegalArgumentException if the key is null. */ public void add(T x) { if (x == null) { throw new IllegalArgumentException("can't add null"); } if (contains(x)) { return; } items[size] = x; size += 1; } --- # 38.4 Huffman Coding Conceptuals | CS61B Textbook [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.4-huffman-coding-conceptuals#core-idea) Core Idea -------------------------------------------------------------------------------------------------------------------------------------------- Huffman coding takes a bottom-up approach to prefix-free codes, as opposed to the top-down approach taken by Shannon-Fano codes. The algorithm is as follows: * Calculate relative frequencies. * Assign each symbol to a node with weight = relative frequency. * Take the two smallest nodes and merge them into a super node with weight equal to sum of weights. * Repeat until everything is part of a tree. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FHdCgQXuYkkNEwyzj9eRQ%252FScreen%2520Shot%25202023-04-24%2520at%25206.12.04%2520PM.png%3Falt%3Dmedia%26token%3D5310b79d-899a-4a9f-a708-9d049f968c74&width=768&dpr=3&quality=100&sign=860934a4&sv=2) Huffman Coding: step by step example (Part 1) ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FSgwaQBso7fWo4Smc2EX9%252FScreen%2520Shot%25202023-04-24%2520at%25206.12.56%2520PM.png%3Falt%3Dmedia%26token%3Df36bd7a2-c9a4-4af3-bd47-61d4ad4e5a7d&width=768&dpr=3&quality=100&sign=9ef68633&sv=2) Huffman Coding: step by step example (Part 2) [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.4-huffman-coding-conceptuals#data-structures) Data Structures -------------------------------------------------------------------------------------------------------------------------------------------------------- Let's now think about the data structures we would use for the encoding and decoding processes of the Huffman Coding process. Recall that encoding will translate symbols to code words and decoding will do the opposite. An example is as follows: * Encoding translates `I ATE` into `0000011000100101.` * Decoding translates `0000011000100101` into `I ATE`. chevron-rightFor encoding (bitstream to compressed bitstream), what data structure would we use? [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.4-huffman-coding-conceptuals#for-encoding-bitstream-to-compressed-bitstream-what-data-structure-would-we-use) There are two options! 1. **HashMap/TreeMap**: create a map from character to bit sequence, calling the `get()` method to look up each character 2. **Array**: Each index of the array would represent the character, with the bit sequence in that slot of the array. Recall that each character is just an integer. For example, the letter `A` is `65`). What's the difference? Arrays are faster than maps but might use more memory if indices are unused. chevron-rightFor decoding (compressed bitstream back to bitstream), what data structure would we use?[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.4-huffman-coding-conceptuals#for-decoding-compressed-bitstream-back-to-bitstream-what-data-structure-would-we-use) There is really only one good data structure that can help us find longest prefixes of bit streams. **Trie**: Here, we can use a Binary Trie with numbers 0 and 1. When we get the bitstream, the trie allows easy lookup to the longest prefix. Below is an image that demonstrates how the trie could look. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252F6rPCfIWcycHM5KgUHE7u%252FScreenshot%25202023-04-24%2520at%25208.50.11%2520PM.png%3Falt%3Dmedia%26token%3D65d1a466-d3f0-4841-9686-a319061ab6ab&width=300&dpr=3&quality=100&sign=7e902dc&sv=2) [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.4-huffman-coding-conceptuals#in-practice) In Practice ------------------------------------------------------------------------------------------------------------------------------------------------ Now that we have talked about what Huffman Coding is, let's talk about some practical issues that arise when we want to use it. There are two main philosophies we can use. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.4-huffman-coding-conceptuals#corpus) Corpus * For each input type (English text, Chinese text, images), assemble huge numbers of sample inputs for each category. Use corpus to create a standard code for English, Chinese, etc. * A corpus is a collection of pieces of language to be used as a sample of the language. Below is an example where we specify that we want to use the `ENGLISH` corpus to compress `mobydick.txt` **Problem:** Suboptimal encoding, which means our corpus does not exactly match our input. What this means in the context of this example is that `mobydick.txt` might not match up well with the general frequencies of `ENGLISH` texts and instead might have some other quirks or specifications from the author. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.4-huffman-coding-conceptuals#unique-code) Unique Code * For every possible input file, create a unique code just for that file. Then, when someone receives that file, they will know how to decode it using the code we send along the compressed file. * As seen in the example below, we do not specify a corpus and we send along another file to help the decoding process for that specific file. **Problem**: This approach requires us to use extra space for the codeword table in the compressed bitstream. However, this generally works better than the corpus philosophy so is used commonly in the real world. [Previous38.3 Shannon-Fano Codeschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.3-shannon-fano-codes) [Next38.5 Compression Theorychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.5-compression-theory) Last updated 2 years ago * [Core Idea](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.4-huffman-coding-conceptuals#core-idea) * [Data Structures](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.4-huffman-coding-conceptuals#data-structures) * [In Practice](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.4-huffman-coding-conceptuals#in-practice) * [Corpus](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.4-huffman-coding-conceptuals#corpus) * [Unique Code](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.4-huffman-coding-conceptuals#unique-code) sun-brightdesktopmoon Copy $ java HuffmanEncodePh1 ENGLISH mobydick.txt Copy $ java HuffmanEncodePh2 mobydick.txt sun-brightdesktopmoon --- # 10.1 Polymorphism vs. Function Passing | CS61B Textbook Fall 2025 **Operator Overloading** Suppose we define a Dog class: Copy class Dog: def __init__(self, name, size): self.name = name self.size = size def __gt__(self, other): return self.size > other.size The Python code below can be used to find the maximum Dog in a list of Dogs. Copy def get_the_max(x): max_value = x[0] for item in x: if item > max_value: max_value = item return max_value max_dog = get_the_max(doglist) The get\_the\_max function in Python is general and can work on any type. It achieves this generality by harnessing "operator overloading". More generally, this ability to handle any type is sometimes called "polymorphism", which I'll define via wikipedia as “the ability in programming to present the same programming interface for differing underlying forms” \[[wikiarrow-up-right](https://en.wikipedia.org/wiki/Polymorphism)\ \] In this case, the > operator allows to compare anything (i.e. "any underlying form") in Python. In turn the definition of > is given by `__gt__`. [Previous10\. Inheritance II: Subtype Polymorphism, Comparators, Comparables, Generic Functionschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions) [Next10.2 Comparables and Comparatorschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.2-encapsulation) Last updated 6 months ago --- # 17.4 B-trees Big O | CS61B Textbook Fall 2025 Consider the following statements about BSTs. Which of the following are true? 1. The worst-case height of a BST is Θ(N)\\Theta(N)Θ(N). 2. BST height is O(N)O(N)O(N). 3. BST height is O(N2)O(N^2)O(N2). The answer is that all three statements are true. BSTs always have a height that is linear or better, and a linear height is obviously "less than" the quadratic upper bound in the last point. However, a more tricky question is which of the three statements is _the most informative_. The answer here is the first statement: it gives an _exact_ upper and lower bound unlike the other statements. O(N)O(N)O(N) could mean linear, logarithmic, square-root, or constant, but Θ(N)\\Theta(N)Θ(N) can only mean linear. For an analogy, consider the following statements about the worst-case cost of a hotel room: 1. The most expensive room is $639/night. 2. The most expensive room is less than or equal to $2000/night. Here, we see that the first statement gives us exact information, whereas the second statement does not. In the second statement, the most expensive room could be $2000, $10, or anywhere in between. However, _both are statements about the worst case_. Applying this to asymptotic notation, this means that we can refer to the worst case with Θ\\ThetaΘ, OOO, or even Ω\\OmegaΩ. **Big O is not the same as the worst case!** [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/17.-asymptotics-iii/17.4-b-trees-big-o#using-big-o) Using Big O ---------------------------------------------------------------------------------------------------------------------------------- If Θ\\ThetaΘ is always more informative than OOO, then why do we bother using Big O notation at all? There are several reasons: * We can make broader statements. For example, saying "binary search is O(log⁡N)O(\\log N)O(logN) is correct, but saying "binary search tree is Θ(logN)\\Theta(log N)Θ(logN)" would not be correct, since it can be constant in certain scenarios. * Sometimes, it is not possible or extremely difficult to determine the exact runtime. In such cases, we would still like to provide a generalized upper bound. [Previous17.3 Mergesortchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/17.-asymptotics-iii/17.3-mergesort) [Next18\. B-Treeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees) Last updated 4 months ago --- # 9.2 Hypernyms, Hyponyms, and the Implements Keyword | CS61B Textbook Fall 2025 ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.2-hypernyms-hyponyms-and-the-implements-keyword#hypernyms-hyponyms-and-interface-inheritance) Hypernyms, Hyponyms, and Interface Inheritance In the English language and life in general, there exist logical hierarchies to words and objects. Dog is what is called a _hypernym of_ poodle, malamute, husky, etc. In the reverse direction, poodle, malamute, and husky, are _hyponyms_ of dog. These words form a hierarchy of "is-a" relationships: * a poodle "is-a" dog * a dog "is-a" canine * a canine "is-a" carnivore * a carnivore "is-an" animal ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fassets%2Fhierarchy.png&width=768&dpr=3&quality=100&sign=d6573856&sv=2) hierarchy The same hierarchy goes for SLLists and ALists! SLList and AList are both hyponyms of a more general list. We will formalize this relationship in Java: if a SLList is a hyponym of List61B, then the SLList class is a **subclass** of the List61B class and the List61B class is a **superclass** of the SLList class. **Figure 4.1.1** ![subclass](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fassets%2Fsubclass.png&width=300&dpr=3&quality=100&sign=739416bd&sv=2) In Java, in order to _express_ this hierarchy, we need to do **two things**: * Step 1: Define a type for the general list hypernym -- we will choose the name List61B. * Step 2: Specify that SLList and AList are hyponyms of that type. The new List61B is what Java calls an **interface**. It is essentially a contract that specifies what a list must be able to do, but it doesn't provide any implementation for those behaviors. Can you think of why? Here is our List61B interface. At this point, we have satisfied the first step in establishing the relationship hierarchy: creating a hypernym. Now, to complete step 2, we need to specify that AList and SLList are hyponyms of the List61B class. In Java, we define this relationship in the class definition. We will add to `public class AList {...}` a relationship-defining word: implements. `public class AList implements List61B{...}` `implements List61B` is essentially a promise. AList is saying "I promise I will have and define all the attributes and behaviors specified in the List61B interface" Now we can edit our `longest` method in `WordUtils` to take in a List61B. Because AList and SLList share an "is-a" relationship. [Previous9.1 The Problem of Generalitychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.1-the-problem-of-generality) [Next9.3 Overriding, Interface Inheritancechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.3-overriding-interface-inheritance) Last updated 6 months ago Copy public interface List61B { public void addFirst(Item x); public void add Last(Item y); public Item getFirst(); public Item getLast(); public Item removeLast(); public Item get(int i); public void insert(Item x, int position); public int size(); } --- # 9.3 Overriding, Interface Inheritance | CS61B Textbook Fall 2025 ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.3-overriding-interface-inheritance#overriding) Overriding We promised we would implement the methods specified in List61B in the AList and SLList classes, so let's go ahead and do that. When implementing the required functions in the subclass, it's useful (and actually required in 61B) to include the `@Override` tag right on top of the method signature. Here, we have done that for just one method. Copy @Override public void addFirst(Item x) { insert(x, 0); } It is good to note that even if you don’t include this tag, you _are_ still overriding the method. So technically, you don't _have_ to include it. However, including the tag acts as a safeguard for you as the programmer by alerting the compiler that you intend to override this method. Why would this be helpful you ask? Well, it's kind of like having a proofreader! The compiler will tell you if something goes wrong in the process. Say you want to override the `addLast` method. What if you make a typo and accidentally write `addLsat`? If you don't include the @Override tag, then you might not catch the mistake, which could make debugging a more difficult and painful process. Whereas if you include @Override, the compiler will stop and prompt you to fix your mistakes before your program even runs. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.3-overriding-interface-inheritance#interface-inheritance) Interface Inheritance Interface Inheritance refers to a relationship in which a subclass inherits all the methods/behaviors of the superclass. As in the List61B class we defined in the **Hyponyms and Hypernyms** section, the interface includes all the method signatures, but not implementations. It's up to the subclass to actually provide those implementations. This inheritance is also multi-generational. This means if we have a long lineage of superclass/subclass relationships like in **Figure 4.1.1**, AList not only inherits the methods from List61B but also every other class above it all the way to the highest superclass AKA AList inherits from Collection. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.3-overriding-interface-inheritance#groe) GRoE ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Recall the Golden Rule of Equals we introduced in the first chapter. This means whenever we make an assignment `a = b` , we copy the bits from b into a, with the requirement that b is the same type as a. You can't assign `Dog b = 1` or `Dog b = new Cat()` because 1 is not a Dog and neither is Cat. Let's try to apply this rule to the `longest` method we wrote previously in this chapter. `public static String longest(List61B list)` takes in a List61B. We said that this could take in AList and SLList as well, but how is that possible since AList and List61B are different classes? Well, recall that AList shares an "is-a" relationship with List61B, Which means an AList should be able to fit into a List61B box! **Exercise 4.1.2** Do you think the code below will compile? If so, what happens when it runs? Here are possible answers: * Will not compile. * Will compile, but will cause an error on the **new** line * When it runs, an SLList is created and its address is stored in the someList variable, but it crashes on someList.addFirst() since the List class doesn't implement addFirst; * When it runs, and SLList is created and its address is stored in the someList variable. Then the string "elk" is inserted into the SLList referred to by addFirst. [Previous9.2 Hypernyms, Hyponyms, and the Implements Keywordchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.2-hypernyms-hyponyms-and-the-implements-keyword) [Next9.4 Implementation Inheritance, defaultchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.4-implementation-inheritance-default) Last updated 6 months ago * [Overriding](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.3-overriding-interface-inheritance#overriding) * [Interface Inheritance](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.3-overriding-interface-inheritance#interface-inheritance) * [GRoE](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.3-overriding-interface-inheritance#groe) Copy public static void main(String[] args) { List61B someList = new SLList(); someList.addFirst("elk"); } --- # 22. Heaps and Priority Queues | CS61B Textbook Fall 2025 [22.1 Priority Queueschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.1-priority-queues) [22.2 Heapschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.2-heaps) [22.3 PQ Implementationchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.3-pq-implementation) [22.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.4-summary) [22.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.5-exercises) [Previous21.4 Mutable vs. Immutable Typeschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/21.-hashing-ii/21.4-mutable-vs.-immutable-types) [Next22.1 Priority Queueschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.1-priority-queues) Last updated 4 months ago --- # 29. Reductions and Decomposition | CS61B Textbook Fall 2025 [29.1 Topological Sorts and DAGschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/29.-reductions-and-decomposition/29.1-topological-sorts-and-dags) [29.2 Shortest Paths on DAGschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/29.-reductions-and-decomposition/29.2-shortest-paths-on-dags) [29.3 Longest Pathchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/29.-reductions-and-decomposition/29.3-longest-path) [29.4 Reductions and Decompositionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/29.-reductions-and-decomposition/29.4-reductions-and-decomposition) [29.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/29.-reductions-and-decomposition/29.5-exercises) [Previous28.5 Summary, Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.5-summary-exercises) [Next29.1 Topological Sorts and DAGschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/29.-reductions-and-decomposition/29.1-topological-sorts-and-dags) Last updated 4 months ago --- # 22.4 Summary | CS61B Textbook Fall 2025 **Priority Queue.** A Max Priority Queue (or PQ for short) is an ADT that supports at least the insert and delete-max operations. A MinPQ supposert insert and delete-min. **Heaps.** A max (min) heap is an array representation of a binary tree such that every node is larger (smaller) than all of its children. This definition naturally applies recursively, i.e. a heap of height 5 is composed of two heaps of height 4 plus a parent. **Tree Representations.** Know that there are many ways to represent a tree, and that we use Approach 3b (see lecture slides) for representing heaps, since we know they are complete. **Running times of various PQ implementations.** Know the running time of the three primary PQ operations for an unordered array, ordered array, and heap implementation. [Previous22.3 PQ Implementationchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.3-pq-implementation) [Next22.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.5-exercises) Last updated 4 months ago --- # 24.3 Summary | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.3-summary#graph-traversals-overview) Graph Traversals Overview ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- * The same traversals that we used on trees can be generalized to graphs. Given a source vertex, we can visit vertices in: * DFS preorder: the order in which DFS is called on each vertex. * DFS postorder: the order in which DFS returns from each vertex. * BFS: the order of distance from the source node (this is level-order in trees). [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.3-summary#bfs) BFS --------------------------------------------------------------------------------------------------------------------------------- * Unlike DFS, BFS has a natural solution that is iterative, not recursive. BFS visits a source vertex `s`, then every vertex at distance `1` from `s`, then every vertex at distance `2` from `s`, and so on. * BFS uses a _fringe_ of vertices that are next to be explored. In BFS, this fringe is a queue. We enqueue new vertices at the end, and dequeue vertices to visit from the front. * BFS can be used to solve the shortest paths problem, given that we want to minimize the number of edges from source to each other vertex. If we want to recover the shortest path from BFS, we need to track the `edgeTo` each vertex during our traversal. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.3-summary#graph-implementation) Graph Implementation ------------------------------------------------------------------------------------------------------------------------------------------------------------------- * The choice of API for a graph determines how clients must write their code. Certain APIs make some tasks easier and other tasks harder. The choice of API can also affect runtime and memory. * Choice of graph implementations include adjacency matrices, lists of edges, and adjacency lists. An adjacency matrix is a 2D boolean array indicating whether any pair of vertices are adjacent. A list of edges is a collection of all edges in the graph. * The most common approach to graph representation is an adjacency list. In this representation, we maintain a array of lists indexed by vertex number; each index stores all vertices connected to the given vertex. [Previous24.2 Representing Graphschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.2-representing-graphs) [Next24.4 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.4-exercises) Last updated 4 months ago * [Graph Traversals Overview](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.3-summary#graph-traversals-overview) * [BFS](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.3-summary#bfs) * [Graph Implementation](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.3-summary#graph-implementation) --- # 9.1 The Problem of Generality | CS61B Textbook Fall 2025 Recall the two list classes we created last week: SLList and AList. If you take a look at their documentation, you'll notice that they are very similar. In fact, all of their supporting methods are the same! Suppose we want to write a class `WordUtils`that includes functions we can run on lists of words, including a method that calculates the longest string in an SLList. **Exercise 4.1.1.** Try writing this method by yourself. The method should take in an SLList of strings and return the longest string in the list. Here is the method that we came up with. Copy public static String longest(SLList list) { int maxDex = 0; for (int i = 0; i < list.size(); i += 1) { String longestString = list.get(maxDex); String thisString = list.get(i); if (thisString.length() > longestString.length()) { maxDex = i; } } return list.get(maxDex); } How do we make this method work for AList as well? All we really have to do is change the method's signature: the parameter Copy SLList list should be changed to Now we have two methods in our `WordUtils` class with exactly the same method name. and This is actually allowed in Java! It's something called _method overloading_. When you call WordUtils.longest, Java knows which one to run according to what kind of parameter you supply it. If you supply it with an AList, it will call the AList method. Same with an SLList. It's nice that Java is smart enough to know how to deal with two of the same methods for different types, but overloading has several downsides: * It's super repetitive and ugly, because you now have two virtually identical blocks of code. * It's more code to maintain, meaning if you want to make a small change to the `longest` method such as correcting a bug, you need to change it in the method for each type of list. * If we want to make more list types, we would have to copy the method for every new list class. [Previous9\. Inheritance I: Interface and Implementation Inheritancechevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance) [Next9.2 Hypernyms, Hyponyms, and the Implements Keywordchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/9.-inheritance-i-interface-and-implementation-inheritance/9.2-hypernyms-hyponyms-and-the-implements-keyword) Last updated 6 months ago Copy AList list Copy public static String longest(SLList list) Copy public static String longest(AList list) --- # 12.5 Chapter Summary | CS61B Textbook Fall 2025 You can find the code from this lecture [herearrow-up-right](https://github.com/Berkeley-CS61B/lectureCode-sp23/tree/main/lec12_inheritance4) . ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.5-chapter-summary#exceptions) Exceptions Most likely you have encountered an exception in your code such as a `NullPointerException` or an `IndexOutOfBoundsException`. Now we will learn about how we can “throw” exceptions ourselves. Here is an example of an exception that we throw: Copy throw new RuntimeException("For no reason."); This is useful to ensure reasonable functioning of our code, even when facing unexpected behavior. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.5-chapter-summary#iteration) Iteration #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.5-chapter-summary#difference-between-iterators-and-iterables) Difference between Iterators and Iterables These two words are very closely related, but have two different meanings that are often easy to confuse. The first thing to know is that these are both Java interfaces, with different methods that need to be implemented. Here is a simplified interface for Iterator: Copy public interface Iterator { boolean hasNext(); T next(); } Here is a simplified interface for Iterable: Copy public interface Iterable { Iterator iterator(); } Notice that in order for an object (for example an ArrayList or LinkedList) to be _iterable_, it must include a method that returns an _iterator_. The iterator is the object that actively steps through an iterable object. Keep this relationship and distinction in mind as you work with these two interfaces. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.5-chapter-summary#object-methods) Object Methods #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.5-chapter-summary#tostring) toString The `toString()` method returns a string representation of objects. For example, `System.out.println(someObject)` calls the `toString()` method of `someObject`, and prints to console whatever string it returns. This is most helpful when we are debugging, as it allows us to much more easily understand the current state of our Objects. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.5-chapter-summary#vs-.equals) \== vs .equals We have two concepts of equality in Java- “==” and the “.equals()” method. The key difference is that when using ==, we are checking if two objects have the same address in memory (that they point to the same instance or object). On the other hand, .equals() is a method that can be overridden by a class and can be used to define some custom way of determining equality. This permits the class to utilize the additional knowledge it has about itself to more accurately answer questions of equality. For example, say we wanted to check if two stones are equal: If we want to consider s and r equal because they have the same weight. If we do check equality using ==, these Stones would not be considered equal because they do not have the same memory address. On the other hand, if you override the equals method of Stone as follows We would have that the stones would be considered equal because they have the same weight. [Previous12.4 Object Methodschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.4-object-methods) [Next12.6 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.6-exercises) Last updated 6 months ago * [Exceptions](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.5-chapter-summary#exceptions) * [Iteration](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.5-chapter-summary#iteration) * [Object Methods](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.5-chapter-summary#object-methods) Copy public class Stone{ int weight; public Stone(int weight){ this.weight = weight; } } Stone s = new Stone(100); Stone r = new Stone(100); Copy public boolean equals(Object o){ return this.weight == ((Stone) o).weight } --- # 25.1 Introduction | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.1-introduction#shortest-paths) Shortest Paths -------------------------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.1-introduction#recalls) Recalls So far, we have methods to do the following * find a path from a given vertex, `s`, to every reachable vertex in the graph. * find a **shortest** path from a given vertex, `s`, to every reachable vertex in the graph. (...or do we?) Before we answer the mysterious question posed below, let's further recall the two types of searches we could use to do the above two things: BFS or DFS. Are both going to always be correct? Yes. Does one give better results? BFS finds you the **shortest** paths whereas DFS does not. Is one more efficient than the other, runtime-wise? No. Is one more efficient than the other, space-wise? * DFS is worse for spindly graphs. Imagine a graph with 10000 nodes all spindly. We'll end up making 10000 recursive calls, which is bad for space. * BFS is worse for "bushy" graphs, because our queue gets used a lot. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.1-introduction#answering-the-mysterious-question) Answering the mysterious question Did we develop an algorithm to find the **shortest** path from a given vertex to every other reachable vertex? Well, kind of. We developed an algorithm that works well on graphs with no edge labels. Here's what we did: we developed an algorithm that finds us the shortest (**where shortest means the fewest number of edges**) paths from a given source vertex. But that's not always the correct definition of shortest. Sometimes, our graph edges might have 'weights', and A-B is considered farther than A-C if the A-B edge has weight, say, 5 and the A-C edge only has weight, say, 3. That is why we need a different algorithm for finding the shortest path when we have the edge weights in the graph. [Previous25\. Shortest Pathschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths) [Next25.2 Dijkstra's Algorithmchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.2-dijkstras-algorithm) Last updated 4 months ago * [Shortest Paths](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.1-introduction#shortest-paths) * [Recalls](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.1-introduction#recalls) * [Answering the mysterious question](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.1-introduction#answering-the-mysterious-question) --- # 1.1 Your First Java Program | CS61B Textbook Fall 2025 The classic first program when introducing any new language is **Hello World**, or a program that prints `Hello World` to the console. In Java, **Hello World** can be written as such: Copy public class HelloWorld { public static void main(String[] args) { System.out.println("Hello world!"); } } As compared to other languages like Python, this may seem needlessly verbose. However, there are several reasons for the verbosity of Java, which will be covered in the next few chapters. For now, notice some key syntatical features of the code snippet above: * The **class declaration** `public class HelloWorld`: in Java, all code lives within classes. * The `**main**` function: all the code that runs must be inside of a method declared as `public static void main(String[] args)`. Future chapters will cover the exact meaning of this declaration. * **Curly braces** `{}` enclose sections of code (functions, classes, and other types of code that will be covered in future chapters). * All statements must end with a **semi-colon**. For fun, see [Hello world! in other languagesarrow-up-right](https://www.rosettacode.org/wiki/Hello_world/Text) . [Previous1\. Introductionchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/1.-introduction) [Next1.2 Java Workflowchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/1.-introduction/1.2-java-workflow) Last updated 6 months ago --- # 16.4 BSTs as Sets and Maps | CS61B Textbook Fall 2025 We can use a BST to implement the `Set` ADT. If we use a BST, we can decrease the runtime of `contains` to log⁡(n)\\log(n)log(n) because of the BST property which enables us to use binary search! We can also make a binary tree into a map by having each BST node hold `(key,value)` pairs instead of singular values. We will compare each element's key in order to determine where to place it within our tree. [Previous16.3 BST Operationschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.3-bst-operations) [Next16.5 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.5-summary) Last updated 4 months ago --- # 15.5 Summary | CS61B Textbook Fall 2025 ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.5-summary#takeaways) **Takeaways** * There are no magic shortcuts for analyzing code runtime. * In our course, it’s OK to do exact counting or intuitive analysis. * Know how to sum 1 + 2 + 3 + ... + N and 1 + 2 + 4 + ... + N. * We won’t be writing mathematical proofs in this class. * Many runtime problems you’ll do in this class resemble one of the five problems from today. * This topic has one of the highest skill ceilings of all topics in the course. All the tools are here, but **practice** is your friend! * Different solutions to the same problem, e.g. sorting, may have different runtimes (with big enough differences for the runtime to go from impractical to practical!). * N2N^2N2​​ vs. Nlog(N)Nlog(N)Nlog(N) is an enormous difference. * Going from Nlog(N)Nlog(N)Nlog(N) to NNN is nice, but not a radical change. Hopefully, this set of examples has provided some good practice with the techniques and patterns of runtime analysis. You can also find extra practice problems in the next section. Remember, there are no magic shortcuts, but you have to tools to approach the problems. Go forth and analyze!! [Previous15.4 For Loops Print Partychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.4-for-loops-print-party) [Next15.6 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.6-exercises) Last updated 4 months ago --- # 21.1 Hash Table Recap, Default Hash Function | CS61B Textbook Fall 2025 Hash Table Recap, Default Hash Function Let's continue understanding Hashing. We've now seen implementations for sets and maps. 1. Red-Black Based Tree Approach: TreeSet/TreeMap • requires the items to be comparable (the notion of less or greater than) • logarithmic time complexity 2. HashTable based approach: HashSet/HashMap • constant time operations if the hashCode spreads the item nicely (few collisions) Recall that with a hash table, the idea is that for any piece of data, like a String (or any other type of object) we want to store in our hash table, we need to turn this into a number called a hash code. So a string like "Mihir" can be converted to -2101281024. circle-info Fun Fact: And that integer is anything between about negative two billion and about two billion, the space of all Java Integers. This range yields about 4 billion integers or 2^32 integers. If you are interested in why this is the case, please take CS 61C! Once we have our number, we want to convert this number to our bucket number (i.e. which of the many linked lists I want to add this new entry to). In the first example, we will use the`Math.floorMod(x, 4)`, since the length of the underlying buckets array has length 4. If we take the converted hash code for "Mihir", -2101281024, and then mod this by 4, we get 0. This reduces our hash code, -2101281024 to a valid index, 0. This means that we would use the 0th bucket to place our data, "Mihir", in our LinkedList at the 0th bucket. > You can essentially think of Java HashTables as just an array of LinkedLists categorized under bucket labels and hopefully have better performance by utilizing these LinkedLists. But how many LinkedLists/Buckets should we use? This is an important question, because as we insert more and more items into our buckets, the length of the LinkedLists will inevitably grow, which in turn compromises the runtime for the hash table's operations. For example, let's say we inserted strings like "Mihir", "Mirchandani", "loves", and "61B", all of which yielded hash codes that were divisible by 4. In this case, all strings would be put into the 0th index bucket and all strings would be inserted into the same LinkedList. What happens to the runtime for a search operation? Well, we would have to search through an entire LinkedList to look up our data! This runs in linear time and is too slow for HashMap's famous title of holding fast lookup times. Last time, we saw that we can have a variable number of LinkedLists. The idea is that we resize that array of linked lists whenever the load factor (N/M, where N is the number of elements in our table and M is the number of buckets) exceeds some constant. Java picks 0.75 as we'll see later. This prevents collisions from happening too frequently. So long as our items are spread out nicely, between the buckets, the LinkedLists at each bucket for the most part have a very small size, which means we'll on average get constant run time! Example: If the HashTable has load factor 3, and our `hashCode()` function spreads out the entries evenly, we are going to end up with just 3 items per bucket, and our search operation takes Θ(1)\\Theta(1)Θ(1)time. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/21.-hashing-ii/21.1-hash-table-recap-default-hash-function#comparing-data-structure-run-times) Comparing Data Structure Run Times! contains(x) add(x) Bushy BSTs Θ(log N) Θ(log N) Separate Chaining Hash Table with NO resizing Θ(N) Θ(N) Separate Chaining Hash Table with resizing Θ(1) Θ(1) [Previous21\. Hashing IIchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/21.-hashing-ii) [Next21.2 Distribution By Other Hash Functionschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/21.-hashing-ii/21.2-distribution-by-other-hash-functions) Last updated 4 months ago --- # 21.4 Mutable vs. Immutable Types | CS61B Textbook Fall 2025 [Previous21.3 Contains & Duplicate Itemschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/21.-hashing-ii/21.3-contains-and-duplicate-items) [Next22\. Heaps and Priority Queueschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues) Last updated 4 months ago --- # 12.1 Lists and Sets in Java | CS61B Textbook Fall 2025 In this section, we will learn about how to use Java's built-in `List` and `Set` data structures as well as build our own `ArraySet`. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.1-lists-and-sets-in-java#getting-started) Getting Started In this course, we've already built two kinds of lists: `AList` and `SLList`. We also built an interface `List61B` to enforce specific list methods `AList` and `SLList` had to implement. You can find the code at the following links: * [`List61B`arrow-up-right](https://github.com/Berkeley-CS61B/lectureCode-sp23/blob/main/lec9_inheritance2/List61B.java) * [`AList`arrow-up-right](https://github.com/Berkeley-CS61B/lectureCode-sp23/blob/main/lec8_inheritance1/AList.java) * [`SLList`arrow-up-right](https://github.com/Berkeley-CS61B/lectureCode-sp23/blob/main/lec9_inheritance2/SLList.java) This is how we might use `List61B` type: ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.1-lists-and-sets-in-java#lists-in-real-java-code) Lists in Real Java Code We built a list from scratch, but Java provides a built-in `List` interface and several implementations, e.g. `ArrayList`. Remember, since `List` is an interface we can't instantiate it! We must instantiate one of its implementations. To access this, we can use the full name ('canonical name') of classes/interfaces: However, this is a bit verbose. Instead, we can import java libraries: ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.1-lists-and-sets-in-java#sets) Sets Sets are a collection of unique elements - you can only have one copy of each element. Unlike Lists, there is also no sense of order: you can't index into a set, nor can you control where each element is inserted into the set. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.1-lists-and-sets-in-java#java-sets) Java Sets Java has the `Set` interface along with implementations, e.g. `HashSet`. Remember to import them if you don't want to use the full name! Example use: #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.1-lists-and-sets-in-java#python-equivalent) Python Equivalent In python, we simply call `set()`. To check for `contains` we don't use a method but the keyword `in`. Here's an example: ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.1-lists-and-sets-in-java#diy-arrayset) DIY: ArraySet Our goal is to make our own set, `ArraySet`, with the following methods: * `add(value)`: add the value to the set if not already present * `contains(value)`: check to see if ArraySet contains the key * `size()`: return number of values If you would like to try it yourself, find 'Do It Yourself' `ArraySet starter code` [herearrow-up-right](https://github.com/Berkeley-CS61B/lectureCode-sp23/blob/main/lec11_inheritance4/DIY/ArraySet.java) . In the lecture clip below, Professor Hug goes develops the solution: [Previous12\. Inheritance III: Iterators, Object Methodschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods) [Next12.2 Exceptionschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.2-exceptions) Last updated 6 months ago * [Getting Started](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.1-lists-and-sets-in-java#getting-started) * [Lists in Real Java Code](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.1-lists-and-sets-in-java#lists-in-real-java-code) * [Sets](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.1-lists-and-sets-in-java#sets) * [DIY: ArraySet](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.1-lists-and-sets-in-java#diy-arrayset) Copy List61B L = new AList<>(); L.addLast(5); L.addLast(10); L.addLast(15); L.print(); Copy java.util.List L = new java.util.ArrayList<>(); Copy import java.util.List; import java.util.ArrayList; public class Example { public static void main(String[] args) { List L = new ArrayList<>(); L.add(5); L.add(10); System.out.println(L); } } Copy import java.util.Set; import java.util.HashSet; Copy Set s = new HashSet<>(); s.add("Tokyo"); s.add("Lagos"); System.out.println(s.contains("Tokyo")); // true Copy s = set() s.add("Tokyo") s.add("Lagos") print("Tokyo" in s) // True --- # 13.6 Summary | CS61B Textbook Fall 2025 To summarize this chapter: * Given a piece of code, we can express its runtime as a function R(N)R(N)R(N) * NNN is a **property** of the input of the function often representing the **size** of the input * Rather than finding the exact value of R(N)R(N)R(N), we only worry about finding the **order of growth** of R(N)R(N)R(N). * One approach (not universal): * Choose a representative operation * Let C(N)C(N)C(N) be the count of how many times that operation occurs as a function of NNN. * Determine order of growth f(N)f(N)f(N) for C(N)C(N)C(N), i.e. C(N)∈Θ(f(N))C(N)\\in \\Theta(f(N))C(N)∈Θ(f(N)) * Often (but not always) we consider the worst case count. * If operation takes constant time, then R(N)∈Θ(f(N))R(N)\\in \\Theta(f(N))R(N)∈Θ(f(N)). [Previous13.5 Simplified Analysis Processchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.5-simplified-analysis-process) [Next13.7 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.7-exercises) Last updated 4 months ago --- # 14.5 Weighted Quick Union with Path Compression | CS61B Textbook Fall 2025 Professor Hug's explanation on Weighted Quick Union with Path Compression The clever insight is realizing that whenever we call `find(x)` we have to traverse the path from `x` to root. So, along the way we can connect all the items we visit to their root at no extra asymptotic cost. Connecting all the items along the way to the root will help make our tree shorter with each call to `find`. Recall that **both** `**connect(x, y)**` **and** `**isConnected(x, y)**` **always call** `**find(x)**` **and** `**find(y)**`**.** Thus, after calling `connect` or `isConnected` enough, essentially all elements will point directly to their root. By extension, the average runtime of `connect` and `isConnected` becomes **almost constant** in the long term! This is called the _amortized runtime_. More specifically, for M operations on N elements, WQU with Path Compression is in O(N+M(lg∗N))O(N + M (lg\* N))O(N+M(lg∗N)). lg\* is the [iterated logarithmarrow-up-right](https://en.wikipedia.org/wiki/Iterated_logarithm) which is less than 5 for any real-world input. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.5-weighted-quick-union-with-path-compression#summary) Summary N: number of elements in Disjoint Set Implementation `isConnected` `connect` Quick Find Θ(1) Θ(N) Quick Union O(N) O(N) Weighted Quick Union (WQU) O(log N) O(log N) WQU with Path Compression O(α(N))\* O(α(N))\* \*behaves as constant in long term. [Previous14.4 Weighted Quick Union (WQU)chevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.4-weighted-quick-union-wqu) [Next14.6 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.6-exercises) Last updated 6 months ago --- # 36.1 Radix vs. Comparison Sorting | CS61B Textbook [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#intuitive-analysis) Intuitive Analysis ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#merge-sort-runtime) Merge Sort Runtime Merge Sort requires Θ(N log N) compares. A key concept here is that Merge Sort’s runtime on strings of length W is Θ(N log N) if each comparison takes constant time or Θ(WN log N) if each comparison takes Θ(W) time. Example of Θ(N log N): Strings are all different in top character. Example of Θ(WN log N): Strings are all equal. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#lsd-vs.-merge-sort) LSD vs. Merge Sort The facts: Treating alphabet size as constant, LSD Sort has runtime Θ(WN). Merge Sort has runtime between Θ(N log N) and Θ(WN log N). Which is better? It depends. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#when-might-lsd-sort-be-faster) When might LSD sort be faster? * When W is sufficiently smaller than N (or equivalently, N is really really big, or as you get to larger and larger numbers of strings, we expect LSD to pull ahead). * Worst case strings for mergesort, the more similar they are, the more we expect LSD sort to do better. * Sufficiently large N. * If strings are very similar to each other. * Each Merge Sort comparison costs Θ(W) time. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252Fl8Xcm0AGxxMcn8aUSjeF%252Fimage.png%3Falt%3Dmedia%26token%3D8cd61b4b-9e5b-4825-8a02-44cb21b0ecc3&width=768&dpr=3&quality=100&sign=e921a8df&sv=2) #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#when-might-merge-sort-be-faster) When might Merge Sort be faster? * Strings are different, especially at the beginning. * If strings are highly dissimilar from each other * Each merge sort comparison is very fast ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252Fv3m69FnKEbP1PUhgxB8U%252Fimage.png%3Falt%3Dmedia%26token%3Dbdd6e36a-d46d-410a-a62c-d865704aef77&width=768&dpr=3&quality=100&sign=3993b59c&sv=2) [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#cost-model-analysis) Cost Model Analysis ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#alternate-approach-picking-a-cost-model) Alternate Approach: Picking a Cost Model An alternate approach is to pick a cost model. * We’ll use number of characters examined. * By “examined”, we mean: * Radix Sort: Calling charAt in order to count occurrences of each character. * Merge Sort: Calling charAt in order to compare two Strings. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#msd-vs.-mergesort) MSD vs. Mergesort Suppose we have 100 strings of 1000 characters each. For MSD Radix Sort, in the worst case (all strings equal), every character is examined exactly once. Thus, we have exactly 100,000 total character examinations. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FPMp2WMZlfbOKobA69PIu%252Fimage.png%3Falt%3Dmedia%26token%3Dcf82e97f-6b3b-45b9-8327-6f620e9794c3&width=768&dpr=3&quality=100&sign=a3dc8dd1&sv=2) For Merge Sort, estimate the total number of characters examined if all strings are equal. Merging 100 items, assuming equal items results in always picking left: * Comparing A\[0\] to A\[50\]: 2000 character examinations. * Comparing A\[1\] to A\[50\]: 2000 character examinations. * … Comparing A\[49\] to A\[50\]: 2000 character examinations. * Total characters examined: 50 \* 2000 = 100000. * Merging N strings of 1000 characters requires N/2 \* 2000 = 1000N examinations. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FicsTCVNOHVY4LnqihEuT%252Fscreenshot%25202023-04-19%2520at%252012.46.58%2520AM.png%3Falt%3Dmedia%26token%3D25ee1bd7-b153-4a80-9fc4-fc78385b86a6&width=768&dpr=3&quality=100&sign=8359dfa5&sv=2) In total, we must examine approximately 1000N log2 N total characters. * 100000 + 50000\*2 + 25000 \* 4 + … = ~660,000 characters. ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FypgoaHyQK7t05ozMEdEv%252Fimage.png%3Falt%3Dmedia%26token%3D2435aca3-60f9-4340-a57e-d6497c871bfe&width=768&dpr=3&quality=100&sign=e466a64d&sv=2) ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#msd-vs.-mergesort-character-examinations) MSD vs. Mergesort Character Examinations For N equal strings of length 1000, we found that: * MSD radix sort will examine ~1000N characters (For N= 100: 100,000). * Merge sort will examine ~1000Nlog2(N) characters (For N=100: 660,000). If character examination are an appropriate cost model, we’d expect Merge Sort to be slower by a factor of log2N. To see if we’re right, we’ll need to do a computational experiment. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#empirical-study-radix-sort-vs.-comparison-sorting) Empirical Study: Radix Sort vs. Comparison Sorting ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#computational-experiment-results-your-answers) Computational Experiment Results (Your Answers) Computational experiment for W = 100. * [MSDarrow-up-right](https://algs4.cs.princeton.edu/51radix/MSD.java.html) and [merge sortarrow-up-right](https://algs4.cs.princeton.edu/22mergesort/MergeX.java.html) implementations are highly optimized versions taken from our optional algorithms textbook. * Does our data match our runtime hypothesis? No! Why not? * Maybe when MSD makes partitions, the divide and conquer cost is not accounted for. * Is mergesort optimized to do the timesort optimization? Unclear. * Maybe this is caching performance, Josh mentioned MSD has bad cache performance. * Maybe string comparison is not linear time somehow (keep in mind every string compare does the SAME thing. Repeated work on real machines these days tends to get optimized away). * Our cost model isn’t representative of everything that is happening. * One particularly thorny issue: **The “Just In Time” Compiler.** ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FeKflgE7iGuAEK7xOr5hT%252Fimage.png%3Falt%3Dmedia%26token%3D12a32b94-38e1-409b-84b9-333491483a3e&width=768&dpr=3&quality=100&sign=b7338b3e&sv=2) ![](https://cs61b-2.gitbook.io/cs61b-textbook/~gitbook/image?url=https%3A%2F%2F2316889115-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FCLYj7ccqvV6l4Pt9R0w5%252Fuploads%252FrZ95YFXDJJVh0pXRLXNL%252Fscreenshot%25202023-04-19%2520at%25201.01.54%2520AM.png%3Falt%3Dmedia%26token%3D7c1e0b96-1e17-4311-aa30-21839e4aa56c&width=768&dpr=3&quality=100&sign=a3642fbe&sv=2) [Previous36\. Sorting and Data Structures Conclusionchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion) [Next36.2 The Just-In-Time Compilerchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.2-the-just-in-time-compiler) Last updated 2 years ago * [Intuitive Analysis](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#intuitive-analysis) * [Merge Sort Runtime](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#merge-sort-runtime) * [LSD vs. Merge Sort](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#lsd-vs.-merge-sort) * [Cost Model Analysis](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#cost-model-analysis) * [Alternate Approach: Picking a Cost Model](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#alternate-approach-picking-a-cost-model) * [MSD vs. Mergesort](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#msd-vs.-mergesort) * [MSD vs. Mergesort Character Examinations](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#msd-vs.-mergesort-character-examinations) * [Empirical Study: Radix Sort vs. Comparison Sorting](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#empirical-study-radix-sort-vs.-comparison-sorting) * [Computational Experiment Results (Your Answers)](https://cs61b-2.gitbook.io/cs61b-textbook/36.-sorting-and-data-structures-conclusion/36.1-radix-vs.-comparison-sorting#computational-experiment-results-your-answers) sun-brightdesktopmoon sun-brightdesktopmoon --- # 18.4 B-Tree Invariants | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.4-b-tree-invariants#b-tree-invariants) B-Tree Invariants ------------------------------------------------------------------------------------------------------------------------------------------ Because of the way B-Trees are constructed, they have two invariants: 1. All leaves are the same distance from the root. 2. A non-leaf node with k items must have exactly k + 1 children. These two invariants guarantee a "bushy" tree with log⁡N\\log NlogN height. [Previous18.3 B-Tree Operationschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.3-b-tree-operations) [Next18.5 B-Tree Performancechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.5-b-tree-performance) Last updated 4 months ago --- # 16.2 BST Definitions | CS61B Textbook Fall 2025 Trees are composed of nodes and edges that connect those nodes. **Constraint**: there is only one path between any two nodes. In some trees, we select a **root** node which is a node that has no parents. A tree also has **leaves**, which are nodes with no children. In the picture below, the green structures are valid trees, while the pink structure is not (since it has a cycle). ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-b26745ce30a804dbe64fca53a540f095fe0fdc74%252Fimage%2520%2858%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=12ec73fe&sv=2) Examples of valid and invalid trees Relating this to the original tree structure we came up with earlier, we can now introduce new constraints to the already existing constraints. This creates more specific types of trees, two examples being Binary Trees and Binary Search Trees. * **Binary Trees**: in addition to the above requirements, also hold the binary property constraint. That is, each node has either 0, 1, or 2 children. * **Binary Search Trees**: in addition to all of the above requirements, also hold the property that For every node X in the tree: * Every key in the left subtree is less than X’s key. * Every key in the right subtree is greater than X’s key. \*\*Remember this property!! We will reference it a lot throughout the duration of this module and 61B. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-0b3b5e9591426831eac397d9c2b339ed290b4bbe%252Fimage%2520%28139%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=c7355719&sv=2) A valid BST: every key in the left subtree is smaller than its parent, and every key in the right subtree is larger. Here is the BST module we'll be using for the rest of this chapter: [Previous16.1 Binary Search Treeschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.1-binary-search-trees) [Next16.3 BST Operationschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.3-bst-operations) Last updated 4 months ago Copy private class BST { private Key key; private BST left; private BST right; public BST(Key key, BST left, BST Right) { this.key = key; this.left = left; this.right = right; } public BST(Key key) { this.key = key; } } --- # 16.1 Binary Search Trees | CS61B Textbook Fall 2025 Linked Lists are great, but it takes a long time to search for an item, even if the list is sorted! What if the item is at the end of the list? That would take linear time! We know that for an array, we can use binary search to find an element faster. Specifically, in log⁡n\\log nlogn time. For a short explanation of binary search, check out this [linkarrow-up-right](https://www.geeksforgeeks.org/binary-search/) . In binary search, we know the list is sorted, so we can use this information to narrow our search. First, we look at the middle element. If it is bigger than the element we are searching for, we look to the left of it. If it is smaller than the element we are searching for, we look to the right. We then look at the middle element of the respective halves and repeat the process until we find the element we are looking for (or don't find it because the list doesn't contain it). But how do we run binary search for a linked list? We would have to traverse all the way to the middle in order to check the element there, which takes linear time just on its own! One optimization we can implement is to have a reference to the middle node. This way, we can get to the middle in constant time. Then, if we flip the nodes' pointers, which allows us to traverse to both the left and right halves, we can decrease our runtime by half! ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-275bd1b28190ccbb8e10c5c0f110f2e444e6b17f%252Fimage%2520%2857%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=c8d998b7&sv=2) A linked list with a middle pointer But we can further optimize by adding pointers to the middle of each recursive half like so. Now, if you stretch this structure vertically, you will see a tree! This specific tree is called a **binary tree** because each juncture splits in 2. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-5d67e78ca5faad981cae7bf9f308570ca7087f78%252Fimage%2520%2811%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=cbf75fb7&sv=2) A linked list with recursive middle pointers is a binary tree! [Previous16\. ADTs and BSTschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts) [Next16.2 BST Definitionschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.2-bst-definitions) Last updated 4 months ago --- # 23.1 Tree Recap | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.1-tree-recap#trees-and-traversals) Trees and Traversals ------------------------------------------------------------------------------------------------------------------------------------------------------------ Professor Hug's review of 17.1 and 17.2 ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.1-tree-recap#what-is-a-tree) What is a Tree? Recall that a tree consists of: * A set of connected nodes (or vertices). We use both terms interchangeably. * A set of edges that connect those nodes. No edges can form a cycle. * **Constraint:** There is exactly one path between any two nodes. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-901e1c8fadfc11c49544b8439b7bcb444df8eadb%252FScreen%2520Shot%25202023-02-26%2520at%25206.04.39%2520AM.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=8efc83cf&sv=2) Graph Examples (Some Trees, some not...) Graph #1 is a tree. It has a node. It has no edges. That's _OK_! The second and third structures are trees. Notice that the third is a LinkedList. A LinkedList is still a tree as it is a connected acyclic graph. The fourth is not a tree. Why? There are two paths from the top node to the bottom node, and so this does not obey our constraint. **Exercise 17.1.1.** Determine the reason why the fifth structure is not a tree. Also, modify the invalid trees above so that they are valid. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.1-tree-recap#what-is-a-rooted-tree) What is a rooted tree? Recall that a rooted tree is a tree with a designated root (typically drawn as the top most node.) This gives us the notion of two more definitions * A parent. Every node except the root has exactly one parent. * What if a node had 2 parents? Would it be a tree? (Hint: No.) * A child. A node can have 0 or more children. * What if a node has 0 children? It's called a leaf. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.1-tree-recap#what-are-trees-useful-for) What are trees useful for? So far, we've looked at Search Trees, Tries, Heaps, Disjoint Sets, etc. These were extremely useful in our journey to create efficient algorithms: speeding up searching for items, allowing prefixing, checking connectedness, and so on. But the fact of the matter is that they are even more ubiquitous than we realize. Consider an organization chart. Here, the President is the 'root'. The 'VP's are children of the root, and so on. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-2b749d87e9b51ccfe28c985d4c1deb57e4e78ac7%252Fimage%2520%28110%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=b884cb23&sv=2) Organization Chart Another tree structure is the `61b/` directory on your Desktop (it is on your Desktop, isn't it?). As we can see, when you traverse to a subfolder it goes to subsequent subfolders and so on. This is exactly tree-like! **Exercise 17.1.2.** Think of other common uses of trees that weren't mentioned above. Try and determine possible implementations or designs of these trees. [arrow-up-right](https://joshhug.gitbooks.io/hug61b/content/chap17/) [Previous23\. Tree Traversals and Graphschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs) [Next23.2 Tree Traversalschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.2-tree-traversals) Last updated 4 months ago * [Trees and Traversals](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.1-tree-recap#trees-and-traversals) * [What is a Tree?](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.1-tree-recap#what-is-a-tree) * [What is a rooted tree?](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.1-tree-recap#what-is-a-rooted-tree) * [What are trees useful for?](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.1-tree-recap#what-are-trees-useful-for) --- # 3. References, Recursion, and Lists | CS61B Textbook Fall 2025 ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/3.-references-recursion-and-lists#lists) Lists You've no doubt used a list data structure at some point in the past. For example, in Python: Copy L = [3, 5, 6] L.append(7) While Java does have a built-in List type, we're going to eschew using it for now. In this chapter, we'll build our own list from scratch, along the way learning some key features of Java. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/3.-references-recursion-and-lists#the-mystery-of-the-walrus) The Mystery of the Walrus To begin our journey, we will first ponder the profound Mystery of the Walrus. Try to predict what happens when we run the code below. Does the change to b affect a? Hint: If you're coming from Python, Java has the same behavior. Copy Walrus a = new Walrus(1000, 8.3); Walrus b; b = a; b.weight = 5; System.out.println(a); System.out.println(b); Now try to predict what happens when we run the code below. Does the change to x affect y? Copy int x = 5; int y; y = x; x = 2; System.out.println("x is: " + x); System.out.println("y is: " + y); The answer can be found [herearrow-up-right](http://cscircles.cemc.uwaterloo.ca/java_visualize/#code=public+class+PollQuestions+%7B%0A+++public+static+void+main%28String%5B%5D+args%29+%7B%0A++++++Walrus+a+%3D+new+Walrus%281000,+8.3%29%3B%0A++++++Walrus+b%3B%0A++++++b+%3D+a%3B%0A++++++b.weight+%3D+5%3B%0A++++++System.out.println%28a%29%3B%0A++++++System.out.println%28b%29%3B++++++%0A%0A++++++int+x+%3D+5%3B%0A++++++int+y%3B%0A++++++y+%3D+x%3B%0A++++++x+%3D+2%3B%0A++++++System.out.println%28%22x+is%3A+%22+%2B+x%29%3B%0A++++++System.out.println%28%22y+is%3A+%22+%2B+y%29%3B++++++%0A+++%7D%0A+++%0A+++public+static+class+Walrus+%7B%0A++++++public+int+weight%3B%0A++++++public+double+tuskSize%3B%0A++++++%0A++++++public+Walrus%28int+w,+double+ts%29+%7B%0A+++++++++weight+%3D+w%3B%0A+++++++++tuskSize+%3D+ts%3B%0A++++++%7D%0A%0A++++++public+String+toString%28%29+%7B%0A+++++++++return+String.format%28%22weight%3A+%25d,+tusk+size%3A+%25.2f%22,+weight,+tuskSize%29%3B%0A++++++%7D%0A+++%7D%0A%7D&mode=edit) . While subtle, the key ideas that underlie the Mystery of the Walrus will be incredibly important to the efficiency of the data structures that we'll implement in this course, and a deep understanding of this problem will also lead to safer, more reliable code. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/3.-references-recursion-and-lists#bits) Bits All information in your computer is stored in _memory_ as a sequence of ones and zeros. Some examples: * 72 is often stored as 01001000 * 205.75 is often stored as 01000011 01001101 11000000 00000000 * The letter H is often stored as 01001000 (same as 72) * The true value is often stored as 00000001 In this course, we won't spend much time talking about specific binary representations, e.g. why on earth 205.75 is stored as the seemingly random string of 32 bits above. Understanding specific representations is a topic of [CS61Carrow-up-right](http://www-inst.eecs.berkeley.edu/~cs61c/) , the followup course to 61B. Though we won't learn the language of binary, it's good to know that this is what is going on under the hood. One interesting observation is that both 72 and H are stored as 01001000. This raises the question: how does a piece of Java code know how to interpret 01001000? The answer is through types! For example, consider the code below: If we run this code, we get: In this case, both the x and c variables contain the same bits (well, almost...), but the Java interpreter treats them differently when printed. In Java, there are 8 primitive types: byte, short, int, long, float, double, boolean, and char. Each has different properties that we'll discuss throughout the course, with the exception of short and float, which you'll likely never use. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/3.-references-recursion-and-lists#declaring-a-variable-simplified) Declaring a Variable (Simplified) You can think of your computer as containing a vast number of memory bits for storing information, each of which has a unique address. Many billions of such bits are available to the modern computer. When you declare a variable of a certain type, Java finds a contiguous block with exactly enough bits to hold a thing of that type. For example, if you declare an int, you get a block of 32 bits. If you declare a byte, you get a block of 8 bits. Each data type in Java holds a different number of bits. The exact number is not terribly important to us in this class. For the sake of having a convenient metaphor, we'll call one of these blocks a "box" of bits. In addition to setting aside memory, the Java interpreter also creates an entry in an internal table that maps each variable name to the location of the first bit in the box. For example, if you declared `int x` and `double y`, then Java might decide to use bits 352 through 384 of your computer's memory to store x, and bits 20800 through 20864 to store y. The interpreter will then record that int x starts at bit 352 and y starts at bit 20800. For example, after executing the code: We'd end up with boxes of size 32 and 64 respectively, as shown in the figure below: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2Fx_and_y_empty_bitwise.png&width=768&dpr=3&quality=100&sign=643b9c06&sv=2) x\_and\_y\_empty\_bitwise The Java language provides no way for you to know the location of the box, e.g. you can't somehow find out that x is in position 352. In other words, the exact memory address is below the level of abstraction accessible to us in Java. This is unlike languages like C where you can ask the language for the exact address of a piece of data. For this reason, I have omitted the addresses from the figure above. This feature of Java is a tradeoff! Hiding memory locations from the programmer gives you less control, which prevents you from doing certain [types of optimizationsarrow-up-right](http://www.informit.com/articles/article.aspx?p=2246428&seqNum=5) . However, it also avoids a [large class of very tricky programming errorsarrow-up-right](http://www.informit.com/articles/article.aspx?p=2246428&seqNum=1) . In the modern era of very low cost computing, this tradeoff is usually well worth it. As the wise Donald Knuth once said: "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil". As an analogy, you do not have direct control over your heartbeat. While this restricts your ability to optimize for certain situations, it also avoids the possibility of making stupid errors like accidentally turning it off. Java does not write anything into the reserved box when a variable is declared. In other words, there are no default values. As a result, the Java compiler prevents you from using a variable until after the box has been filled with bits using the `=` operator. For this reason, I have avoided showing any bits in the boxes in the figure above. When you assign values to a memory box, it is filled with the bits you specify. For example, if we execute the lines: Then the memory boxes from above are filled as shown below, in what I call **box notation**. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2Fx_and_y_empty_filled.png&width=768&dpr=3&quality=100&sign=32022993&sv=2) x\_and\_y\_empty\_filled.png The top bits represent -1431195969, and the bottom bits represent 567213.112. Why these specific sequences of bits represent these two numbers is not important, and is a topic covered in CS61C. However, if you're curious, see [integer representationsarrow-up-right](https://en.wikipedia.org/wiki/Two's_complement) and [double representationsarrow-up-right](https://en.wikipedia.org/wiki/IEEE_floating_point) on wikipedia. Note: Memory allocation is actually somewhat more complicated than described here, and is a topic of CS 61C. However, this model is close enough to reality for our purposes in 61B. **Simplified Box Notation** While the box notation we used in the previous section is great for understanding approximately what's going on under the hood, it's not useful for practical purposes since we don't know how to interpret the binary bits. Thus, instead of writing memory box contents in binary, we'll write them in human readable symbols. We will do this throughout the rest of the course. For example, after executing: We can represent the program environment using what I call **simplified box notation**, shown below: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2Fx_and_y_simplified_box_notation.png&width=768&dpr=3&quality=100&sign=d12dfa7c&sv=2) x\_and\_y\_simplified\_box\_notation.png #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/3.-references-recursion-and-lists#the-golden-rule-of-equals-groe) The Golden Rule of Equals (GRoE) Now armed with simplified box notation, we can finally start to resolve the Mystery of the Walrus. It turns out our Mystery has a simple solution: When you write `y = x`, you are telling the Java interpreter to copy the bits from x into y. This Golden Rule of Equals (GRoE) is the root of all truth when it comes to understanding our Walrus Mystery. This simple idea of copying the bits is true for ANY assignment using `=` in Java. To see this in action, click [this linkarrow-up-right](http://cscircles.cemc.uwaterloo.ca/java_visualize/#code=public+class+PollQuestions+%7B%0A+++public+static+void+main(String%5B%5D+args%29+%7B%0A++++++int+x+%3D+5%3B%0A++++++int+y%3B%0A++++++y+%3D+x%3B%0A++++++x+%3D+2%3B%0A++++++System.out.println(%22x+is%3A+%22+%2B+x%29%3B%0A++++++System.out.println(%22y+is%3A+%22+%2B+y%29%3B++++++%0A+++%7D%0A%7D&mode=display&curInstr=0) . #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/3.-references-recursion-and-lists#reference-types) Reference Types Above, we said that there are 8 primitive types: byte, short, int, long, float, double, boolean, char. Everything else, including arrays, is not a primitive type but rather a `reference type`. **Object Instantiation** When we _instantiate_ an Object using `new` (e.g. Dog, Walrus, Planet), Java first allocates a box for each instance variable of the class, and fills them with a default value. The constructor then usually (but not always) fills every box with some other value. For example, if our Walrus class is: And we create a Walrus using `new Walrus(1000, 8.3);`, then we end up with a Walrus consisting of two boxes of 32 and 64 bits respectively: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2Fanonymous_walrus.png&width=768&dpr=3&quality=100&sign=6ff7c6ae&sv=2) anonymous\_walrus.png In real implementations of the Java programming language, there is actually some additional overhead for any object, so a Walrus takes somewhat more than 96 bits. However, for our purposes, we will ignore such overhead, since we will never interact with it directly. The Walrus we've created is anonymous, in the sense that it has been created, but it is not stored in any variable. Let's now turn to variables that store objects. **Reference Variable Declaration** When we _declare_ a variable of any reference type (Walrus, Dog, Planet, array, etc.), Java allocates a box of 64 bits, no matter what type of object. At first glance, this might seem to lead to a Walrus Paradox. Our Walrus from the previous section required more than 64 bits to store. Furthermore, it may seem bizarre that no matter the type of object, we only get 64 bits to store it. However, this problem is easily resolved with the following piece of information: the 64 bit box contains not the data about the walrus, but instead the address of the Walrus in memory. As an example, suppose we call: The first line creates a box of 64 bits. The second line creates a new Walrus, and the address is returned by the `new` operator. These bits are then copied into the `someWalrus` box according to the GRoE. If we imagine our Walrus weight is stored starting at bit `5051956592385990207` of memory, and tuskSize starts at bit `5051956592385990239`, we might store `5051956592385990207` in the Walrus variable. In binary, `5051956592385990207` is represented by the 64 bits `0100011000011100001001111100000100011101110111000001111000111111`, giving us in box notation: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2FsomeWalrus_bit_notation.png&width=768&dpr=3&quality=100&sign=2d8bded7&sv=2) someWalrus\_bit\_notation.png We can also assign the special value `null` to a reference variable, corresponding to all zeros. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2FsomeWalrus_bit_notation_null.png&width=768&dpr=3&quality=100&sign=1051ad19&sv=2) someWalrus\_bit\_notation\_null.png **Box and Pointer Notation** Just as before, it's hard to interpret a bunch of bits inside a reference variable, so we'll create a simplified box notation for reference variable as follows: * If an address is all zeros, we will represent it with null. * A non-zero address will be represented by an **arrow** pointing at an object instantiation. This is also sometimes called "box and pointer" notation. For the examples from the previous section, we'd have: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2FsomeWalrus_simplified_bit_notation.png&width=768&dpr=3&quality=100&sign=61d12b86&sv=2) someWalrus\_simplified\_bit\_notation.png ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2FsomeWalrus_simplified_bit_notation_null.png&width=768&dpr=3&quality=100&sign=5157330e&sv=2) someWalrus\_simplified\_bit\_notation\_null.png **Resolving the Mystery of the Walrus** We're now finally ready to resolve, fully and completely, the Mystery of the Walrus. After the first line is executed, we have: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2Fmystery_of_the_walrus_resolved_step1.png&width=768&dpr=3&quality=100&sign=e13fd4cf&sv=2) mystery\_of\_the\_walrus\_resolved\_step1.png After the second line is executed, we have: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2Fmystery_of_the_walrus_resolved_step2.png&width=768&dpr=3&quality=100&sign=62e93de2&sv=2) mystery\_of\_the\_walrus\_resolved\_step2.png Note that above, b is undefined, not null. According to the GRoE, the final line simply copies the bits in the `a` box into the `b` box. Or in terms of our visual metaphor, this means that b will copy exactly the arrow in a and now show an arrow pointing at the same object. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2Fmystery_of_the_walrus_resolved_step3.png&width=768&dpr=3&quality=100&sign=d699b4f9&sv=2) mystery\_of\_the\_walrus\_resolved\_step3.png And that's it. There's no more complexity than this. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/3.-references-recursion-and-lists#parameter-passing) Parameter Passing When you pass parameters to a function, you are also simply copying the bits. In other words, the GRoE also applies to parameter passing. Copying the bits is usually called "pass by value". In Java, we **always** pass by value. For example, consider the function below: Suppose we invoke this function as shown below: After executing the first two lines of this function, the main method will have two boxes labeled `x` and `y` containing the values shown below: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2Fmain_x_y.png&width=768&dpr=3&quality=100&sign=2072dbfc&sv=2) main\_x\_y.png When the function is invoked, the `average` function has its **own** scope with two new boxes labeled as `a` and `b`, and the bits are simply _copied_ in. This copying of bits is what we refer to when we say "pass by value". ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig21%2Faverage_a_b.png&width=768&dpr=3&quality=100&sign=8834e37c&sv=2) average\_a\_b.png If the `average` function were to change `a`, then `x` in main would be unchanged, since the GRoE tells us that we'd simply be filling in the box labeled `a` with new bits. **Test Your Understanding** **Exercise 2.1.1**: Suppose we have the code below: Does the call to `doStuff` have an effect on walrus and/or x? Hint: We only need to know the GRoE to solve this problem. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/3.-references-recursion-and-lists#instantiation-of-arrays) Instantiation of Arrays As mentioned above, variables that store arrays are reference variables just like any other. As an example, consider the declarations below: Both of these declarations create memory boxes of 64 bits. `x` can only hold the address of an `int` array, and `planets` can only hold the address of a `Planet` array. Instantiating an array is very similar to instantiating an object. For example, if we create an integer array of size 5 as shown below: Then the `new` keyword creates 5 boxes of 32 bits each and returns the address of the overall object for assignment to x. Objects can be lost if you lose the bits corresponding to the address. For example if the only copy of the address of a particular Walrus is stored in `x`, then `x = null` will cause you to permanently lose this Walrus. This isn't necessarily a bad thing, since you'll often decide you're done with an object, and thus it's safe to simply throw away the reference. We'll see this when we build lists later in this chapter. **The Law of the Broken Futon** You might ask yourself why we spent so much time and space covering what seems like a triviality. This is probably especially true if you have prior Java experience. The reason is that it is very easy for a student to have a half-cocked understanding of this issue, allowing them to write code, but without true comprehension of what's going on. While this might be fine in the short term, in the long term, doing problems without full understanding may doom you to failure later down the line. There's a blog post about this so-called [Law of the Broken Futonarrow-up-right](https://mathwithbaddrawings.com/2015/04/08/the-math-ceiling-wheres-your-cognitive-breaking-point/) that you might find interesting. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/3.-references-recursion-and-lists#instantiation-of-arrays-1) \== vs. Arrays.equals Similar to =, we can think of the == operator in terms of bits. Whenever we write `x==y` we are asking Jafa to compare the literal bits in memory boxes `x` and `y`. For example, suppose we have the code: This code will print false, since `x` and `y` each contain the 64 bit address of two different arrays in memory, albeit two arrays which happen to contain the same information. If we want to compare the two content of the two arrays, we can use Arrays.equals instead, e.g. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/3.-references-recursion-and-lists#intlists) IntLists Now that we've truly understood the Mystery of the Walrus, we're ready to build our own list class. It turns out that a very basic list is trivial to implement, as shown below: You may remember something like this from 61a called a "Linked List". Such a list is ugly to use. For example, if we want to make a list of the numbers 5, 10, and 15, we can either do: Alternately, we could build our list backwards, yielding slightly nicer but harder to understand code: While you could in principle use the IntList to store any list of integers, the resulting code would be rather ugly and prone to errors. We'll adopt the usual object oriented programming strategy of adding helper methods to our class to perform basic tasks. **size and iterativeSize** We'd like to add a method `size` to the `IntList` class so that if you call `L.size()`, you get back the number of items in `L`. Consider writing a `size` and `iterativeSize` method before reading the rest of this chapter. `size` should use recursion, and `iterativeSize` should not. You'll probably learn more by trying on your own before seeing how I do it. The two videos provide a live demonstration of how one might implement these methods. My `size` method is as shown below: The key thing to remember about recursive code is that you need a base case. In this situation, the most reasonable base case is that rest is `null`, which results in a size 1 list. Exercise: You might wonder why we don't do something like `if (this == null) return 0;`. Why wouldn't this work? Answer: Think about what happens when you call size. You are calling it on an object, for example L.size(). If L were null, then you would get a NullPointer error! My `iterativeSize` method is as shown below. I recommend that when you write iterative data structure code that you use the name `p` to remind yourself that the variable is holding a pointer. You need that pointer because you can't reassign "this" in Java. The followups in [this Stack Overflow Postarrow-up-right](https://stackoverflow.com/questions/23021377/reassign-this-in-java-class) offer a brief explanation as to why. **get** While the `size` method lets us get the size of a list, we have no easy way of getting the ith element of the list. Exercise: Write a method `get(int i)` that returns the ith item of the list. For example, if `L` is 5 -> 10 -> 15, then `L.get(0)` should return 5, `L.get(1)` should return 10, and `L.get(2)` should return 15. It doesn't matter how your code behaves for invalid `i`, either too big or too small. For a solution, see the lecture video above or the lectureCode repository. Note that the method we've written takes linear time! That is, if you have a list that is 1,000,000 items long, then getting the last item is going to take much longer than it would if we had a small list. We'll see an alternate way to implement a list that will avoid this problem in a future lecture. [Previous2\. Defining and Using Classeschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/2.-defining-and-using-classes) [Next4\. SLListschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/4.-sllists) Last updated 6 months ago Copy char c = 'H'; int x = c; System.out.println(c); System.out.println(x); Copy H 72 Copy int x; double y; Copy x = -1431195969; y = 567213.112; Copy int x; double y; x = -1431195969; y = 567213.112; Copy int x = 5; int y; y = x; x = 2; System.out.println("x is: " + x); System.out.println("y is: " + y); Copy public static class Walrus { public int weight; public double tuskSize; public Walrus(int w, double ts) { weight = w; tuskSize = ts; } } Copy Walrus someWalrus; someWalrus = new Walrus(1000, 8.3); Copy Walrus a = new Walrus(1000, 8.3); Walrus b; b = a; Copy public static double average(double a, double b) { return (a + b) / 2; } Copy public static void main(String[] args) { double x = 5.5; double y = 10.5; double avg = average(x, y); } Copy public class PassByValueFigure { public static void main(String[] args) { Walrus walrus = new Walrus(3500, 10.5); int x = 9; doStuff(walrus, x); System.out.println(walrus); System.out.println(x); } public static void doStuff(Walrus W, int x) { W.weight = W.weight - 100; x = x - 5; } } Copy int[] x; Planet[] planets; Copy x = new int[]{0, 1, 2, 95, 4}; Copy int[] x = new int[]{0, 1, 2, 95, 4}; int[] y = new int[]{0, 1, 2, 95, 4}; System.out.println(x == y); #false Copy int[] x = new int[]{0, 1, 2, 95, 4}; int[] y = new int[]{0, 1, 2, 95, 4}; System.out.println(Arrays.equals(x, y)); #true Copy public class IntList { public int first; public IntList rest; public IntList(int f, IntList r) { first = f; rest = r; } } Copy IntList L = new IntList(5, null); L.rest = new IntList(10, null); L.rest.rest = new IntList(15, null); Copy IntList L = new IntList(15, null); L = new IntList(10, L); L = new IntList(5, L); Copy /** Return the size of the list using... recursion! */ public int size() { if (rest == null) { return 1; } return 1 + this.rest.size(); } Copy /** Return the size of the list using no recursion! */ public int iterativeSize() { IntList p = this; int totalSize = 0; while (p != null) { totalSize += 1; p = p.rest; } return totalSize; } --- # 20. Hashing I | CS61B Textbook Fall 2025 [20.1 Introduction to Hashing: Data Indexed Arrayschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.1-introduction-to-hashing-data-indexed-arrays) [20.2 Hash Codechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.2-hash-code) [20.3 "Valid" & "Good" Hashcodeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.3-valid-and-good-hashcodes) [20.4 Handling Collisions: Linear Probing and External Chainingchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.4-handling-collisions-linear-probing-and-external-chaining) [20.5 Resizing & Hash Table Performancechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.5-resizing-and-hash-table-performance) [20.6 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.6-summary) [20.7 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.7-exercises) [Previous19.6 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.6-exercises) [Next20.1 Introduction to Hashing: Data Indexed Arrayschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.1-introduction-to-hashing-data-indexed-arrays) Last updated 4 months ago --- # 13.1 An Introduction to Asymptotic Analysis | CS61B Textbook Fall 2025 Previously, we have focused on how to save time _writing_ the program. Now, we will learn how to make the best use of our computer's time and memory. We can consider the process of writing efficient programs from two different perspectives: 1. Programming Cost _(everything in the course up to this date)_ 1. How long does it take for you to develop your programs? 2. How easy is it to read or modify your code? 3. How maintainable is your code? (very important — much of the cost comes from maintenance and scalability, not development!) 2. Execution Cost _(everything in the course from this point on)_ 1. **Time complexity**: How much time does it take for your program to execute? 2. **Space complexity**: How much memory does your program require? To give a sense of what is coming up, consider a **sorted** array. Our goal is to determine if there is a duplicate element in the list. A **naïve algorithm** would be to compare every pair of elements. In the above example, we would compare -3 with every element in the list, then -1, then 2, etc. A **better algorithm** would be to take advantage of the sorted nature of the list! Instead of comparing every pair of elements, we can compare each element with just the element next to it. We can see that the **naïve algorithm** seems like it’s doing a lot more unnecessary, redundant work than the **better algorithm**. But how much more work is it doing? How do we quantify how efficient a program is? This chapter will provide you the formal techniques and tools to compare the efficiency of various algorithms! [Previous13\. Asymptotics Ichevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i) [Next13.2 Runtime Characterizationchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.2-runtime-characterization) Last updated 6 months ago Copy List example = [-3, -1, 2, 4, 4, 8, 10, 12]; --- # 14.4 Weighted Quick Union (WQU) | CS61B Textbook Fall 2025 Improving on Quick Union relies on a key insight: whenever we call `find`, we have to climb to the root of a tree. Thus, the shorter the tree the faster it takes! **New rule:** whenever we call `connect`, we always link the root of the smaller tree to the larger tree. Following this rule will give your trees a maximum height of logNlog NlogN, where N is the number of elements in our Disjoint Sets. How does this affect the runtime of `connect` and `isConnected`? Professor Hug's explanation on Weighted Quick Union Let's illustrate the benefit of this with an example. Consider connecting the two sets T1 and T2 below: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap9%2F9.4.1.png&width=768&dpr=3&quality=100&sign=f7571ff&sv=2) We have two options for connecting them: The first option we link T1 to T2. In the second, we link T2 to T1. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap9%2F9.4.2.png&width=768&dpr=3&quality=100&sign=dd5386d2&sv=2) The **second option is preferable** as it only has a height of 2, rather than 3. By our new rule, we would choose the second option as well because T2 is smaller than T1 (size of 3 compared to 6). We determine smaller / larger by the number of items in a tree. Thus, when connecting two trees we need to know their size (or weight). We can store this information in the root of the tree by replacing the `-1`'s with `-(size of tree)`. **Maximum height: Log N** Following the above rule ensures that the _maximum_ height of any tree is Θ(log N). N is the number of elements in our Disjoint Sets. **By extension, the runtimes of** `**connect**` **and** `**isConnected**` **are bounded by O(log N).** Why logNlogNlogN? The video above presents a more visual explanation. Here's an optional mathematical explanation why the maximum height is log2Nlog\_{2}Nlog2​N. Imagine any element xxx in tree T1T1T1. The depth of xxx increases by 1 only when T1T1T1 is placed below another tree T2T2T2. When that happens, the size of the resulting tree will be at least double the size of T1T1T1 because size(T2)≥size(T1)size(T2)\\geq size(T1)size(T2)≥size(T1). The tree with xxx can double at most log2Nlog\_{2}Nlog2​N times until we've reached a total of N items (2log2N\=N2^{log\_{2}N} = N2log2​N\=N). So we can double up to log2Nlog\_{2}Nlog2​N​​ times and each time, our tree adds a level → maximum log2Nlog\_{2}Nlog2​N levels. You may be wondering why we don't link trees based off of height instead of weight. It turns out this is more complicated to implement and gives us the same Θ(log N) height limit. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.4-weighted-quick-union-wqu#summary-and-code) Summary Implementation Constructor `connect` `isConnected` QuickUnion Θ(N) O(N) O(N) QuickFind Θ(N) Θ(N) Θ(1) QuickUnion Θ(N) O(N) O(N) Weighted Quick Union Θ(N) O(log N) O(log N) N = number of elements in our DisjointSets data structure [Previous14.3 Quick Unionchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.3-quick-union) [Next14.5 Weighted Quick Union with Path Compressionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.5-weighted-quick-union-with-path-compression) Last updated 6 months ago --- # 27. Prefix Operations and Tries | CS61B Textbook Fall 2025 ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries#the-search-problem) The Search Problem To motivate this next section, let us consider the **search problem**. In this problem, we are given a stream of data, and our goal is to retrieve the information of interest. For example, a website which allows users to post content to their personal page could want to serve that content only to friends. Another example is if a new station receives logs from thousands of weather stations, and it wants to display a weather map for a specified date and time. Both of these are examples of the search problem, just in different flavors! The data structures we have built so far have been to solve the search problems for various domains of interest. Let us review the data structures we have seen so far: Name Storage Operations Primary Retrieval Operation Retrieve By List `add(key)`, `insert(key, index)` `get(index)` index Map `put(key, value)` `get(key)` key identity Set `add(key)` `containsKey(key)` key identity Priority Queue `add(key)` `getSmallest()` key order (smallest to largest) Disjoint Sets `connect(int_a, int_b` `isConnected(int_a, int_b)` two integer values All of these data structures are used to solve different instances of the search problem. They all have their own applications depending on how the data of interest needs to be retrieved. One important thing to note is that these are **abstract** data types (ADTs), which means that we define the behavior of the data structure, not the implementation. There are multiple possible implementations for each of the data structures, and we can even use one data structure in the implementation of another! We often use these ADTs to create even more complex data structures. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries#abstraction) Abstraction Abstraction is the idea of only being concerned with the behavior of something and not the underlying implementation. This concept is not as foreign as you might think! We apply principles of abstraction in our day to day lives without even realizing it. For example, using a keyboard can be considered an abstraction of writing text onto a computer. There can be multiple implementations of a keyboard's circuitry depending on what company produced it, but we do not worry about what is going on under the hood, we just care that it can allow us to type text onto a computer. Abstraction is often applied in _layers_ when creating data structures. When implementing a Priority Queue, we could choose to use a Heap Ordered Tree to store the elements of the priority queue. We do not worry about the implementation of the Heap Ordered Tree--we just care about the methods that a Heap Ordered Tree provides. In the same vein, the Heap Ordered Tree does not care about the implementation of the Tree data structure that it uses in it's underlying implementation. Finally, whoever ends up using the Priority Queue we create is also unconcerned with how we made the Priority Queue. They would only care that our Priority Queue is able to support adding elements and returning the smallest element efficiently. In summary, we can often think of an ADT by the use of another ADT. ADTs have layers of abstraction, each defining behavior that is more specific than the idea that came before [Previous26.5 MST Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.5-mst-exercises) [Next27.1 Introduction to Trieschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.1-introduction-to-tries) Last updated 4 months ago * [The Search Problem](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries#the-search-problem) * [Abstraction](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries#abstraction) --- # 25.5 Exercises | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.5-exercises#procedural) Procedural --------------------------------------------------------------------------------------------------------------------------- Questions 1 and 2, suppose we run Dijkstra's from A. Break ties alphabetically. Breaking ties alphabetically means that if we insert B into the fringe, and C is in the fringe with the same priority, we have a tie! To break this tie, we will do so alphabetically, putting B before C. 1. What is the order that vertices are visited? Please separate vertices with spaces, e.g. A B C .... 2. Select all the edges in the shortest paths tree. \* ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-fcfbf561a90f3034027077ad70dca56daa0272e4%252Fimage%2520%28146%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=d91f2cb9&sv=2) chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.5-exercises#problem-1) A B E C F D G For this, and all questions on this check in, refer to this walkthrough video for an explanation: [https://youtu.be/5MFOu8bNvd8arrow-up-right](https://www.google.com/url?q=https://youtu.be/5MFOu8bNvd8&sa=D&source=editors&ust=1679291837950894&usg=AOvVaw3zdQsT2ly7PQQGYjid7E6m) chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.5-exercises#problem-2) AE AB CE DF EG EF For questions 3-5, suppose we run A\* from A to D. Break ties alphabetically 1. What is the order that vertices are visited? Note that not all vertices need be visited. Please separate vertices with spaces, e.g. A B C .... 2. Select all the edges in the shortest path from A -> D, as determined by A\*. Note that this may not be the correct shortest path. Also note that we are NOT finding the shortest path tree, but just the edges on the shortest path! 3. In the previous question, you probably noticed that the actual shortest path from A -> D was not found! The reason for this is because the value of one heuristic is too high. Determine which heuristic this is, and what is the maximum its value can be so that A\* returns the correct shortest path. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-c579d524315bde3af44e37529333c85caab40cde%252Fimage%2520%2828%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=76c182b3&sv=2) chevron-rightProblem 3[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.5-exercises#problem-3) A E C B D chevron-rightProblem 4[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.5-exercises#problem-4) AE CD CE chevron-rightProblem 5[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.5-exercises#problem-5) F:2 [Previous25.4 Summarychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.4-summary) [Next26\. Minimum Spanning Treeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees) Last updated 4 months ago --- # 4. SLLists | CS61B Textbook Fall 2025 In Chapter 3, we built the `IntList` class, a list data structure that can technically do all the things a list can do. However, in practice, the `IntList` suffers from the fact that it is fairly awkward to use, resulting in code that is hard to read and maintain. Fundamentally, the issue is that the `IntList` is what I call a **naked recursive** data structure. In order to use an `IntList` correctly, the programmer must understand and utilize recursion even for simple list related tasks. This limits its usefulness to novice programmers, and potentially introduces a whole new class of tricky errors that programmers might run into, depending on what sort of helper methods are provided by the `IntList` class. Inspired by our experience with the `IntList`, we'll now build a new class `SLList`, which much more closely resembles the list implementations that programmers use in modern languages. We'll do so by iteratively adding a sequence of improvements. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/4.-sllists#improvement-1-rebranding) Improvement #1: Rebranding Our `IntList` class from last time was as follows, with helper methods omitted: Copy public class IntList { public int first; public IntList rest; public IntList(int f, IntList r) { first = f; rest = r; } ... Our first step will be to simply rename everything and throw away the helper methods. This probably doesn't seem like progress, but trust me, I'm a professional. Copy public class IntNode { public int item; public IntNode next; public IntNode(int i, IntNode n) { item = i; next = n; } } #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/4.-sllists#improvement-2-bureaucracy) Improvement #2: Bureaucracy Knowing that `IntNodes` are hard to work with, we're going to create a separate class called `SLList` that the user will interact with. The basic class is simply: Already, we can get a vague sense of why a `SLList` is better. Compare the creation of an `IntList` of one item to the creation of a `SLList` of one item. The `SLList` hides the detail that there exists a null link from the user. The `SLList` class isn't very useful yet, so let's add an `addFirst` and `getFirst` method as simple warmup methods. Consider trying to write them yourself before reading on. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/4.-sllists#addfirst-and-getfirst) addFirst and getFirst `addFirst` is relatively straightforward if you understood chapter 2.1. With `IntLists`, we added to the front with the line of code `L = new IntList(5, L)`. Thus, we end up with: `getFirst` is even easier. We simply return `first.item`: The resulting `SLList` class is much easier to use. Compare: to the `IntList` equivalent: Comparing the two data structures visually, we have: (with the `IntList` version on top and `SLList` version below it) ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig22%2FIntList_vs_SLList.png&width=768&dpr=3&quality=100&sign=7ecc82c6&sv=2) IntList\_vs\_SLList.png Essentially, the `SLList` class acts as a middleman between the list user and the naked recursive data structure. As suggested above in the `IntList` version, there is a potentially undesireable possibility for the `IntList` user to have variables that point to the middle of the `IntList`. As Ovid said: [Mortals cannot look upon a god without dyingarrow-up-right](https://en.wikipedia.org/wiki/Semele) , so perhaps it is best that the `SLList` is there to act as our intermediary. **Exercise 2.2.1**: The curious reader might object and say that the `IntList` would be just as easy to use if we simply wrote an `addFirst` method. Try to write an `addFirst` method to the `IntList` class. You'll find that the resulting method is tricky as well as inefficient. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/4.-sllists#improvement-3-public-vs-private) Improvement #3: Public vs. Private Unfortunately, our `SLList` can be bypassed and the raw power of our naked data structure (with all its dangers) can be accessed. A programmer can easily modify the list directly, without going through the kid-tested, mother-approved `addFirst` method, for example: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig22%2Fbad_SLList.png&width=768&dpr=3&quality=100&sign=f018c1c6&sv=2) bad\_SLList.png This results in a malformed list with an infinite loop. To deal with this problem, we can modify the `SLList` class so that the `first` variable is declared with the `private` keyword. Private variables and methods can only be accessed by code inside the same `.java` file, e.g. in this case `SLList.java`. That means that a class like `SLLTroubleMaker` below will fail to compile, yielding a `first has private access in SLList` error. By contrast, any code inside the `SLList.java` file will be able to access the `first` variable. It may seem a little silly to restrict access. After all, the only thing that the `private` keyword does is break programs that otherwise compile. However, in large software engineering projects, the `private` keyword is an invaluable signal that certain pieces of code should be ignored (and thus need not be understood) by the end user. Likewise, the `public` keyword should be thought of as a declaration that a method is available and will work **forever** exactly as it does now. As an analogy, a car has certain `public` features, e.g. the accelerator and brake pedals. Under the hood, there are `private` details about how these operate. In a gas powered car, the accelerator pedal might control some sort of fuel injection system, and in a battery powered car, it may adjust the amount of battery power being delivered to the motor. While the private details may vary from car to car, we expect the same behavior from all accelerator pedals. Changing these would cause great consternation from users, and quite possibly terrible accidents. **When you create a** `**public**` **member (i.e. method or variable), be careful, because you're effectively committing to supporting that member's behavior exactly as it is now, forever.** #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/4.-sllists#improvement-4-nested-classes) Improvement #4: Nested Classes At the moment, we have two `.java` files: `IntNode` and `SLList`. However, the `IntNode` is really just a supporting character in the story of `SLList`. Java provides us with the ability to embed a class declaration inside of another for just this situation. The syntax is straightforward and intuitive: Having a nested class has no meaningful effect on code performance, and is simply a tool for keeping code organized. For more on nested classes, see [Oracle's official documentationarrow-up-right](https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html) . If the nested class has no need to use any of the instance methods or variables of `SLList`, you may declare the nested class `static`, as follows. Declaring a nested class as `static` means that methods inside the static class can not access any of the members of the enclosing class. In this case, it means that no method in `IntNode` would be able to access `first`, `addFirst`, or `getFirst`. This saves a bit of memory, because each `IntNode` no longer needs to keep track of how to access its enclosing `SLList`. Put another way, if you examine the code above, you'll see that the `IntNode` class never uses the `first` variable of `SLList`, nor any of `SLList`'s methods. As a result, we can use the static keyword, which means the `IntNode` class doesn't get a reference to its boss, saving us a small amount of memory. If this seems a bit technical and hard to follow, try Exercise 2.2.2. A simple rule of thumb is that _if you don't use any instance members of the outer class, make the nested class static_. **Exercise 2.2.2** Delete the word `static` as few times as possible so that [this programarrow-up-right](https://joshhug.gitbooks.io/hug61b/content/chap2/exercises/Government.java) compiles (Refresh the page after clicking the link and making sure the url changed). Make sure to read the comments at the top before doing the exercise. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/4.-sllists#addlast-and-size) addLast() and size() To motivate our remaining improvements and also demonstrate some common patterns in data structure implementation, we'll add `addLast(int x)` and `size()` methods. You're encouraged to take the [starter codearrow-up-right](https://github.com/Berkeley-CS61B/lectureCode/blob/master/lists2/DIY/addLastAndSize/SLList.java) and try it yourself before reading on. I especially encourage you to try to write a recursive implementation of `size`, which will yield an interesting challenge. I'll implement the `addLast` method iteratively, though you could also do it recursively. The idea is fairly straightforward, we create a pointer variable `p` and have it iterate through the list to the end. By contrast, I'll implement `size` recursively. This method will be somewhat similar to the `size` method we implemented in section [2.1arrow-up-right](https://joshhug.gitbooks.io/hug61b/content/chap2/chap21.html) for `IntList`. The recursive call for `size` in `IntList` was straightforward: `return 1 + this.rest.size()`. For a `SLList`, this approach does not make sense. A `SLList` has no `rest` variable. Instead, we'll use a common pattern that is used with middleman classes like `SLList` -- we'll create a private helper method that interacts with the underlying naked recursive data structure. This yields a method like the following: Using this method, we can easily compute the size of the entire list: Here, we have two methods, both named `size`. This is allowed in Java, since they have different parameters. We say that two methods with the same name but different signatures are **overloaded**. For more on overloaded methods, see Java's [official documentationarrow-up-right](https://docs.oracle.com/javase/tutorial/java/javaOO/methods.html) . An alternate approach is to create a non-static helper method in the `IntNode` class itself. Either approach is fine, though I personally prefer not having any methods in the `IntNode` class. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/4.-sllists#improvement-5-caching) Improvement #5: Caching ---------------------------------------------------------------------------------------------------------------------------- Consider the `size` method we wrote above. Suppose `size` takes 2 seconds on a list of size 1,000. We expect that on a list of size 1,000,000, the `size` method will take 2,000 seconds, since the computer has to step through 1,000 times as many items in the list to reach the end. Having a `size` method that is very slow for large lists is unacceptable, since we can do better. It is possible to rewrite `size` so that it takes the same amount of time, no matter how large the list. To do so, we can simply add a `size` variable to the `SLList` class that tracks the current size, yielding the code below. This practice of saving important data to speed up retrieval is sometimes known as **caching**. This modification makes our `size` method incredibly fast, no matter how large the list. Of course, it will also slow down our `addFirst` and `addLast` methods, and also increase the memory of usage of our class, but only by a trivial amount. In this case, the tradeoff is clearly in favor of creating a cache for size. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/4.-sllists#improvement-6-the-empty-list) Improvement #6: The Empty List Our `SLList` has a number of benefits over the simple `IntList` from chapter 2.1: * Users of a `SLList` never see the `IntList` class. * Simpler to use. * More efficient `addFirst` method (exercise 2.2.1). * Avoids errors or malfeasance by `IntList` users. * Faster `size` method than possible with `IntList`. Another natural advantage is that we will be able to easily implement a constructor that creates an empty list. The most natural way is to set `first` to `null` if the list is empty. This yields the constructor below: Unfortunately, this causes our `addLast` method to crash if we insert into an empty list. Since `first` is `null`, the attempt to access `p.next` in `while (p.next != null)` below causes a null pointer exception. **Exercise 2.2.3** Fix the `addLast` method. Starter code [herearrow-up-right](https://github.com/Berkeley-CS61B/lectureCode/blob/master/lists2/DIY/fixAddLast/SLList.java) . #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/4.-sllists#improvement-6b-sentinel-nodes) Improvement #6b: Sentinel Nodes One solution to fix `addLast` is to create a special case for the empty list, as shown below: This solution works, but special case code like that shown above should be avoided when necessary. Human beings only have so much working memory, and thus we want to keep complexity under control wherever possible. For a simple data structure like the `SLList`, the number of special cases is small. More complicated data structures like trees can get much, much uglier. A cleaner, though less obvious solution, is to make it so that all `SLLists` are the "same", even if they are empty. We can do this by creating a special node that is always there, which we will call a **sentinel node**. The sentinel node will hold a value, which we won't care about. For example, the empty list created by `SLList L = new SLList()` would be as shown below: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig22%2Fempty_sentinelized_SLList.png&width=768&dpr=3&quality=100&sign=a15f445&sv=2) empty\_sentinelized\_SLList.png And a `SLList` with the items 5, 10, and 15 would look like: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap2%2Ffig22%2Fthree_item_sentenlized_SLList.png&width=768&dpr=3&quality=100&sign=86d264b5&sv=2) three\_item\_sentenlized\_SLList.png In the figures above, the lavender ?? value indicates that we don't care what value is there. Since Java does not allow us to fill in an integer with question marks, we just pick some abitrary value like -518273 or 63 or anything else. Since a `SLList` without a sentinel has no special cases, we can simply delete the special case from our `addLast` method, yielding: As you can see, this code is much much cleaner! #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/4.-sllists#invariants) Invariants An invariant is a fact about a data structure that is guaranteed to be true (assuming there are no bugs in your code). A `SLList` with a sentinel node has at least the following invariants: * The `sentinel` reference always points to a sentinel node. * The front item (if it exists), is always at `sentinel.next.item`. * The `size` variable is always the total number of items that have been added. Invariants make it easier to reason about code, and also give you specific goals to strive for in making sure your code works. A true understanding of how convenient sentinels are will require you to really dig in and do some implementation of your own. You'll get plenty of practice in Project 1. However, we recommend that you wait until after you've finished the next section of this book before beginning Project 1. [Previous3\. References, Recursion, and Listschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/3.-references-recursion-and-lists) [Next5\. DLListschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/5.-dllists) Last updated 6 months ago Copy public class SLList { public IntNode first; public SLList(int x) { first = new IntNode(x, null); } } Copy IntList L1 = new IntList(5, null); SLList L2 = new SLList(5); Copy public class SLList { public IntNode first; public SLList(int x) { first = new IntNode(x, null); } /** Adds an item to the front of the list. */ public void addFirst(int x) { first = new IntNode(x, first); } } Copy /** Retrieves the front item from the list. */ public int getFirst() { return first.item; } Copy SLList L = new SLList(15); L.addFirst(10); L.addFirst(5); int x = L.getFirst(); Copy IntList L = new IntList(15, null); L = new IntList(10, L); L = new IntList(5, L); int x = L.first; Copy SLList L = new SLList(15); L.addFirst(10); L.first.next.next = L.first.next; Copy public class SLList { private IntNode first; ... Copy public class SLLTroubleMaker { public static void main(String[] args) { SLList L = new SLList(15); L.addFirst(10); L.first.next.next = L.first.next; } } Copy public class SLList { public class IntNode { public int item; public IntNode next; public IntNode(int i, IntNode n) { item = i; next = n; } } private IntNode first; public SLList(int x) { first = new IntNode(x, null); } ... Copy public class SLList { public static class IntNode { public int item; public IntNode next; public IntNode(int i, IntNode n) { item = i; next = n; } } private IntNode first; ... Copy /** Adds an item to the end of the list. */ public void addLast(int x) { IntNode p = first; /* Advance p to the end of the list. */ while (p.next != null) { p = p.next; } p.next = new IntNode(x, null); } Copy /** Returns the size of the list starting at IntNode p. */ private static int size(IntNode p) { if (p.next == null) { return 1; } return 1 + size(p.next); } Copy public int size() { return size(first); } Copy public class SLList { ... /* IntNode declaration omitted. */ private IntNode first; private int size; public SLList(int x) { first = new IntNode(x, null); size = 1; } public void addFirst(int x) { first = new IntNode(x, first); size += 1; } public int size() { return size; } ... } Copy public SLList() { first = null; size = 0; } Copy public void addLast(int x) { size += 1; IntNode p = first; while (p.next != null) { p = p.next; } p.next = new IntNode(x, null); } Copy public void addLast(int x) { size += 1; if (first == null) { first = new IntNode(x, null); return; } IntNode p = first; while (p.next != null) { p = p.next; } p.next = new IntNode(x, null); } Copy public void addLast(int x) { size += 1; IntNode p = sentinel; while (p.next != null) { p = p.next; } p.next = new IntNode(x, null); } --- # 30.5 Summary | CS61B Textbook Fall 2025 ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-7611ef51209da34b5be33f47be62ddce3fffdcf0%252Fimage%2520%2869%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=738d2635&sv=2) A summary of the sorts covered so far. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.5-summary#overview) Overview **Inversions.** The number of pairs of elements in a sequence that are out of order. An array with no inversions is ordered. **Selection sort.** One way to sort is by selection: Repeatedly identifying the most extreme element and moving it to the end of the unsorted section of the array. The naive implementation of such an algorithm is in place. **Naive Heapsort.** A variant of selection sort is to use a heap based PQ to sort the items. To do this, insert all items into a MaxPQ and then remove them one by one. The first such item removed is placed at the end of the array, the next item right before the end, and so forth until that last item deleted is placed in position 0 of the array. Each insertion and deletion takes O(log N) time, and there are N insertions and deletions, resulting in a O(N log N) runtime. With some more work, we can show that heapsort is θ(N log N) in the worst case. This naive version of heapsort uses θ(N) for the PQ. Creation of the MaxPQ requires O(N) memory. It is also possible to use a MinPQ instead. **In place heapsort.** When sorting an array, we can avoid the θ(N) memory cost by treating the array itself as a heap. To do this, we first heapify the array using bottom-up heap construction (taking θ(N) time). We then repeatedly delete the max item, swapping it with the last item in the heap. Over time, the heap shrinks from N items to 0 items, and the sorted list from 0 items to N items. The resulting version is also θ(N log N). **Mergesort.** We can sort by merging, as discussed in an earlier lecture. Mergesort is θ(N log N) and uses θ(N) memory. **Insertion Sort.** For each item, insert into the output sequence in the appropriate place. Naive solution involves creation of a separate data structure. The memory efficient version of this algorithm swaps items one-by-one towards the left until they land in the right place. The invariant for this type of insertion sort is that every item to the left of position i is in sorted order, and everything to the right has not yet been examined. Every swap fixes exactly one inversion. **Insertion Sort Runtime.** In the best case, insertion sort takes θ(N) time. In the worst case, θ(N^2) time. More generally, runtime is no worse than the number of inversions. [Previous30.4 Insertion Sortchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.4-insertion-sort) [Next30.6 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.6-exercises) Last updated 4 months ago --- # 19.2 Creating LLRB Trees | CS61B Textbook Fall 2025 We said in the previous section that we really like 2-3 trees because they always remain balanced, but they are very hard to implement. On the other hand, BSTs can be unbalanced, but we like how simple and intuitive they are. Is there a way to combine the best of two worlds? Why not create a tree that is implemented using a BST, but is structurally identical to a 2-3 tree and thus stays balanced? (Note that in this chapter we will be honing in on 2-3 Trees specifically, not 2-3-4 trees) [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.2-creating-llrb-trees#entering-red-black-trees) Entering Red Black Trees ------------------------------------------------------------------------------------------------------------------------------------------------------------------ ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.2-creating-llrb-trees#from-2-3-tree-to-what) From 2-3 Tree to What? We are going to create this tree by looking at a 2-3 tree and asking ourselves what kind of modifications we can make in order to convert it into a BST. For a 2-3 tree that only has 2-nodes (nodes with 2 children), we already have a BST, so we don't need to make any modifications! However, what happens when we get a 3-node? One thing we could do is create a "glue" node that doesn't hold any information and only serves to show that its 2 children are actually a part of one node. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-8daca7879aae1c9de7e13c0971063601fc57e7ab%252FScreen%2520Shot%25202023-02-27%2520at%25208.28.55%2520PM.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=2b03b3c4&sv=2) Naive Solution: Dummy "glue" node However, this is not an elegant solution because we are taking up more space and the code will be ugly. So, instead of using glue nodes we will use **glue links** instead! ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.2-creating-llrb-trees#to-llrb-tree) To LLRB Tree To transform dummy glue nodes to glue links, we choose arbitrarily to make the left element a child of the right one. This results in a **left-leaning** tree. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-19f792060395613e94df61c49ac2932de02b5c9b%252FScreen%2520Shot%25202023-02-27%2520at%25208.31.57%2520PM.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=ece8f0d5&sv=2) Left Leaning Red-Black Tree We show that a link is a glue link by making it red. Normal links are black. Because of this, we call these structures **left-leaning red-black trees (LLRB)**. We will be using left-leaning trees in this course. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.2-creating-llrb-trees#one-to-one-correspondence) One-to-One Correspondence: Left-Leaning Red-Black trees have a **1-1 correspondence with 2-3 trees.** Every 2-3 tree has a **unique** LLRB red-black tree associated with it. As for 2-3-4 trees, they maintain correspondence with standard Red-Black trees. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.2-creating-llrb-trees#properties-of-llrbs) Properties of LLRBs -------------------------------------------------------------------------------------------------------------------------------------------------------- Below is a summary of the properties/invariants of LLRB Trees: * 1-1 correspondence with 2-3 trees. * No node has 2 red links. * There are no red right-links. * Every path from root to leaf has the same number of black links (because 2-3 trees have the same number of links to every leaf). * Height is no more than 2x height + 1 of the corresponding 2-3 tree. * The height of a red-black tree is proportional to the log of the number of entries. [Previous19.1 Rotating Treeschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.1-rotating-trees) [Next19.3 Inserting LLRB Treeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.3-inserting-llrb-trees) Last updated 4 months ago * [Entering Red Black Trees](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.2-creating-llrb-trees#entering-red-black-trees) * [From 2-3 Tree to What?](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.2-creating-llrb-trees#from-2-3-tree-to-what) * [To LLRB Tree](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.2-creating-llrb-trees#to-llrb-tree) * [One-to-One Correspondence:](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.2-creating-llrb-trees#one-to-one-correspondence) * [Properties of LLRBs](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.2-creating-llrb-trees#properties-of-llrbs) --- # 33. More Quick Sort, Sorting Summary | CS61B Textbook Fall 2025 [33.1 Quicksort Flavors vs. MergeSortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.1-quicksort-flavors-vs.-mergesort) [33.2 Quick Selectchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.2-quick-select) [33.3 Stability, Adaptiveness, and Optimizationchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.3-stability-adaptiveness-and-optimization) [33.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.4-summary) [33.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.5-exercises) [Previous32.5 Exeriseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.5-exerises) [Next33.1 Quicksort Flavors vs. MergeSortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.1-quicksort-flavors-vs.-mergesort) Last updated 4 months ago --- # 24.1 BFS & DFS | CS61B Textbook Fall 2025 Professor Hug's Lecture on Graphs In [Chapter 22.4arrow-up-right](https://github.com/Berkeley-CS61B/fa25-gitbook/blob/main/22.-tree-traversals-and-graphs/22.4-graph-problems.md) , we developed DFS (Depth First Search) Traversal for graphs. In DFS, we visit down the entire lineage of our first child before we even begin to look at our second child - we literally search _**depth first**_. Here, we will talk about BFS (Breadth First Search) (also known as Level Order Traversal). In BFS, we visit all of our immediate children before continuing on to any of our grandchildren. In other words, we visit all nodes 1 edges from our source. Then, all nodes 2 edges from our source, etc. The pseudocode for BFS is as follows: A _fringe_ is just a term we use for the data structure we are using to store the nodes on the frontier of our traversal's discovery process (the next nodes it is waiting to look at). For BFS, we use a queue for our fringe. `edgeTo[...]` is a map that helps us track how we got to node `n`; we got to it by following the edge from `v` to to `n`. `distTo[...]` is a map that helps us track how far `n` is from the starting vertex. Assuming that each edge is worth a distance of `1`, then the distance to `n` is just one more than the distance to get to `v`. Why? We can use the way we know how to get to `v`, then pay one more to arrive at `n` via the edge that necessarily exists between `v` and `n` (it must exist since in the `for` loop header, `n` is defined as a neighbor of `v`). This [slide deckarrow-up-right](https://docs.google.com/presentation/d/1JoYCelH4YE6IkSMq_LfTJMzJ00WxDj7rEa49gYmAtc4/edit#slide=id.g76e0dad85_2_380) illustrates how this pseudocode can be carried out on an example graph. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.1-bfs-and-dfs#dfs-vs-bfs) DFS vs BFS chevron-right**Question 18.1**: What graph traversal algorithm uses a stack rather than a queue for its fringe?[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.1-bfs-and-dfs#question-18.1-what-graph-traversal-algorithm-uses-a-stack-rather-than-a-queue-for-its-fringe) **Answer 18.1**: DFS traversal. Note however that DFS and BFS differ in more than just their fringe data structure. They differ in the order of marking nodes. For DFS we mark nodes only once we visit a node - aka pop it from the fringe. As a result, it's possible to have multiple instances of the same node on the stack at a time if that node has been queued but not visited yet. With BFS we mark nodes as soon as we add them to the fringe so this is not possible. Recursive DFS implements this naturally via the recursive stack frames; iterative DFS implements it manually: \\ [Previous24\. Graph Traversals and Implementationschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations) [Next24.2 Representing Graphschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.2-representing-graphs) Last updated 4 months ago Copy Initialize the fringe, an empty queue add the starting vertex to the fringe mark the starting vertex while fringe is not empty: remove vertex v from the fringe for each neighbor n of vertex v: if n is not marked: add n to fringe mark n set edgeTo[n] = v set distTo[n] = distTo[v] + 1 Copy Initialize the fringe, an empty stack push the starting vertex on the fringe while fringe is not empty: pop a vertex off the fringe if vertex is not marked: mark the vertex visit vertex for each neighbor of vertex: if neighbor not marked: push neighbor to fringe --- # 10.3 Writing a Max Function | CS61B Textbook Fall 2025 We've seen that Java provides a nice `Collections.max` function for us. `Collections` also includes other handy functions like `min`, `sort`, and `reverse`. In this chapter we'll see how we can write such functions ourselves. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.3-casting#generic-functions-warumup) Generic Functions (Warumup) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ As a warmup suppose we want a function which picks a random item from an array. Some code is shown below: Copy public class RandomPicker { public static T randomItem(T[] items) { Random random = new Random(); int randomIndex = random.nextInt(x.length); return x[randomIndex]; } } And a sample program that uses this function is given below: Copy public class RandomPickerDemo { public static void main(String[] args) { String[] x = {"hi", "little", "cat"}; System.out.println(RandomPicker.pickRandom(x)); } } Next, we'll try to make it so that the `pickRandom` function can work on any type of array. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.3-casting#approach-one-generics) Approach One: Generics We can try to use Generics, as shown in the code below: However, this code doesn't really make sense as written. The `pickRandom` function is static, but we have to instantiate a `RandomPicker` object to specify the generic type. That is, we'd need to do something like: but we can't call static methods using an instance. A fix for this is to make the `pickRandom` function non-static, as shown below: If we do this our code will work fine, but it's still very awkward to have to instantate a picker object. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.3-casting#approach-two-generic-static-methods) Approach Two: Generic Static Methods The correct and better approach is to make our static method generic. This is shown below: This syntax is confusing (and it'll get worse later!). The way you can read `public static T pickRandom(T[] x)` is: "I am declaring a public static function that works on objects of type , it returns a T, it is called pickRandom, and it takes an array of Ts as its input. After noting to Java that our static method is generic, we can call it without instantiating a `RandomPicker` object as shown below: Note that unlike generic classes, we don't need to specify the generic type when calling a generic static method (i.e. there's no `` after `pickRandom`). The type is automatically inferred. One minor annoyance is that our pickRnadom function will not work with arrays of primitive types, i.e. we can use `int[]`, `double[]`, `float[]`. There's no way around this, and in fact real Java libraries often have multiple functions, one for each primitive type. For example, there exists `Arrays.sort(int[])`, `Arrays.sort(double[])`, `Arrays.sort(float[])`, etc. One day maybe Project Valhalla will fix this. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.3-casting#writing-our-max-function-type-bounds) Writing our Max Function, Type Bounds --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Using the idea of a generic static function, we can write our max function as follows: There's one last problem, Java doesn't know that `T` has a `compareTo` method. We can fix this by adding a **type bound** to our generic type, as shown below: This tells Java that `T`, whatever it is, has to implement `Comparable`. If you try to pass an arary of objects into max that does not implement `Comparable`, you'll get a compile-time error. The way to read `public static > T max(T[] items)` is: "I am declaring a public static function that works on objects of type , it returns a T, it is called max, and it takes an array of Ts as its input. Additionally, T has to implement Comparable." ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.3-casting#bonus-an-even-better-type-bound) Bonus: An Even Better Type Bound In real industrial strength Java code, the correct way to specify our method is: [Previous10.2 Comparables and Comparatorschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.2-encapsulation) [Next10.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.4-higher-order-functions-in-java) Last updated 6 months ago * [Generic Functions (Warumup)](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.3-casting#generic-functions-warumup) * [Approach One: Generics](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.3-casting#approach-one-generics) * [Approach Two: Generic Static Methods](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.3-casting#approach-two-generic-static-methods) * [Writing our Max Function, Type Bounds](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.3-casting#writing-our-max-function-type-bounds) * [Bonus: An Even Better Type Bound](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.3-casting#bonus-an-even-better-type-bound) Copy public class RandomPicker { public static T pickRandom(T[] x) { Random random = new Random(); int randomIndex = random.nextInt(x.length); return x[randomIndex]; } } Copy String[] x = {"hi", "little", "cat"}; RandomPicker stringPicker = new RandomPicker<>(); System.out.println(stringPicker.pickRandom(x)); Copy public class RandomPicker { public T pickRandom(T[] x) { Random random = new Random(); int randomIndex = random.nextInt(x.length); return x[randomIndex]; } } Copy public class RandomPicker { public static T pickRandom(T[] x) { Random random = new Random(); int randomIndex = random.nextInt(x.length); return x[randomIndex]; } } Copy public class RandomPickerDemo { public static void main(String[] args) { String[] x = {"hi", "little", "cat"}; System.out.println(RandomPicker.pickRandom(x)); } } Copy public class RandomPickerDemo { public static void main(String[] args) { int[] x = {1, 2, 3, 4, 5, 6, 7, 8}; System.out.println(RandomPicker.pickRandom(x)); } } Copy public class Maximizer { public static T max(T[] items) { T maxItem = items[0]; for (int i = 0; i < items.length; i += 1) { int cmp = items[i].compareTo(maxItem); if (cmp > 0) { maxItem = items[i]; } } return maxItem; } } Copy public class Maximizer { public static > T max(T[] items) { T maxItem = items[0]; for (int i = 0; i < items.length; i += 1) { int cmp = items[i].compareTo(maxItem); if (cmp > 0) { maxItem = items[i]; } } return maxItem; } } Copy public class Maximizer { public static > T max(T[] items) { ... } } --- # 20.3 "Valid" & "Good" Hashcodes | CS61B Textbook Fall 2025 Professor Hug's Lecture on Valid/Good Hashcodes [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.3-valid-and-good-hashcodes#valid-hashcodes) Valid Hashcodes! ------------------------------------------------------------------------------------------------------------------------------------------------ You may see this term in discussions and potentially on exams. What exactly makes a hash code "valid"? There are two properties: 1. _**Deterministic**_**:** The `hashCode()` function of two objects A and B who are equal to each other (`A.equals(B) == true`) have the same hashcode. _This also means the hash function cannot rely on attributes of the object that are not reflected in the_ `.equals()` _method._ 2. _**Consistent**_**:** The `hashCode()` function returns the same integer every time it is called on the same instance of an object. This means the `hashCode()` function must be independent of time/stopwatches, random number generators, or any methods that would not give us a consistent `hashCode()` across multiple `hashCode()` function calls on the same object instance. Note that there are no requirements that state that unequal objects should have different hash function values. One could argue that these two requirements are in fact the same requirement. We can restate the requirement of consistency. Imagine we make a pointer named `A` to an object `O` at 12:00 pm and a pointer named `B` to this same object `O` at 1:00 pm. We know that the hash code should return the same integer for both objects, due to the consistency requirement. However, how do we formally define our statement “this same object `O`” above? Technically, the only reason we consider `B` to be pointing to the same thing as `A` is because of the `.equals()`method! This is starting to sound a lot like the determinism requirement. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.3-valid-and-good-hashcodes#good-hashcodes) Good Hashcodes! ---------------------------------------------------------------------------------------------------------------------------------------------- You'll probably see this term a lot as well. But what makes a hashcode "good"? There are a few properties that can make a good `hashCode()`: 1. The `hashCode()` function must be valid. 2. The `hashCode()` function values should be spread as uniformly as possible over the set of all integers. 3. The `hashCode()` function should be relatively quick to compute \[ideally O(1) constant time mathematical operations\] Now let’s think more specifically about the impact of the hashing function. In general, we assume most hash functions will be “relatively quick”. Why do we make this assumption? Given how intrinsic the hashing function is to our data structure, the runtime of this function will have a significant effect on the overall runtime of our data structure. This means we want our hash code to be “easily” computable (ideally constant time), so that we may maintain the O(1) runtime characteristic that makes hashing so special and efficient! [Previous20.2 Hash Codechevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.2-hash-code) [Next20.4 Handling Collisions: Linear Probing and External Chainingchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.4-handling-collisions-linear-probing-and-external-chaining) Last updated 4 months ago * [Valid Hashcodes!](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.3-valid-and-good-hashcodes#valid-hashcodes) * [Good Hashcodes!](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.3-valid-and-good-hashcodes#good-hashcodes) --- # 15.2 Big O | CS61B Textbook Fall 2025 O (pronounced "Big-Oh") is similar to Θ\\ThetaΘ. Instead of being an "equality" on the order of growth, it can be though of as "less than or equal." For example, the following statements are all true: * N3+3N4∈Θ(N4)N^3 + 3N^4 \\in \\Theta (N^4)N3+3N4∈Θ(N4) * N3+3N4∈O(N4)N^3 + 3N^4 \\in \\text{O}(N^4)N3+3N4∈O(N4) * N3+3N4∈O(N6)N^3 + 3N^4 \\in \\text{O}(N^6)N3+3N4∈O(N6) * N3+3N4∈O(NN!)N^3 + 3N^4 \\in \\text{O}(N^{N!})N3+3N4∈O(NN!) ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.2-big-o#formal-definition) **Formal Definition** R(N)∈O(f(N))R(N) \\in \\text{O}(f(N))R(N)∈O(f(N)) means that there exists positive constant k2k\_2k2​ such that: R(N)≤k2⋅f(N)R(N) \\leq k\_2 \\cdot f(N)R(N)≤k2​⋅f(N) for all values of NNN greater than some N0N\_0N0​ (a very large NNN). Observe that this is a looser condition than Θ\\ThetaΘ since O does not care about the lower bound. [Previous15.1 Big Thetachevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.1-big-theta) [Next15.3 For Loopschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.3-for-loops) Last updated 4 months ago --- # 25. Shortest Paths | CS61B Textbook Fall 2025 [25.1 Introductionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.1-introduction) [25.2 Dijkstra's Algorithmchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.2-dijkstras-algorithm) [25.3 A\* Algorithmchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.3-a-algorithm) [25.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.4-summary) [25.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.5-exercises) [Previous24.4 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.4-exercises) [Next25.1 Introductionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.1-introduction) Last updated 4 months ago --- # 28.1 Introduction to Software Engineering | CS61B Textbook Fall 2025 ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.1-introduction-to-software-engineering#scale) Scale CS 61A is an introductory course that focuses on the correctness of a program. CS 61B, however, focuses on _engineering_ software projects. These projects are larger but now require decisions between valid options by considering tradeoffs. The choice between using a LinkedList and an ArrayList, for example, is a tradeoff of runtime and implementation decisions. Working on smaller scale projects isn't the same as larger scale projects that place more emphasis on design. These tasks are often ambiguous. In the real world, there are no specs, no hints to guide you through your project; there's also no end date where you can submit your code and be done with it. This lecture features some light theory and then some real-world examples to illustrate these concepts. Project 3 (Build Your Own World) will allow you to embrace the challenge of large scale projects yourself. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.1-introduction-to-software-engineering#further-reading) Further Reading Please read **A Philosophy of Software Design Paperback by John Ousterhout** if you are very interested in this topic. [Previous28\. Software Engineering Ichevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i) [Next28.2 Complexitychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.2-complexity) Last updated 4 months ago * [Scale](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.1-introduction-to-software-engineering#scale) * [Further Reading](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.1-introduction-to-software-engineering#further-reading) --- # 22.5 Exercises | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.5-exercises#procedural) Procedural -------------------------------------------------------------------------------------------------------------------------------------- 1. Represent the following heap as an array. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-77b9a1980c8369a197c0b584fd1d8f2373f85f82%252Fimage%2520%2883%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=49ae590e&sv=2) 1. Consider the above heap. What range of values, when inserted, will cause the maximum number of swaps? chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.5-exercises#problem-1) To convert a heap into an array, we simply read the values top-to-bottom and left-to-right. As such, the correct representation is `[0, 5, 1, 8, 8, 6, 2]`. chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.5-exercises#problem-2) Since `0` is currently the root, any value `< 0` when inserted will have to swapped to from the bottom of the tree to the top, resulting in the maximum number of swaps. Thus, the range is \[−∞,0)\[-\\infty, 0)\[−∞,0).\ \ [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.5-exercises#metacognitive)\ \ Metacognitive\ \ \ --------------------------------------------------------------------------------------------------------------------------------------------\ \ 1. Consider an externally-chained hashmap. If there are many hash collisions, what data structure would be most efficient in storing the key-value pairs at each bucket?\ \ 2. Consider an array sorted in descending order. Is this a valid heap? If so, which type: min-heap, or max-heap?\ \ 3. Suppose that we store the root of a heap at the `0`th index in our array. For an arbitrary index `k`, compute the indices of its children and parent.\ \ 4. Explain how you would implement a stack using a priority queue.\ \ \ chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.5-exercises#problem-1-1)\ \ An LLRB would be the best choice. Even if all items end up in same bucket, the LLRB will be balanced, resulting in a O(log⁡n)O(\\log n)O(logn) search time where nnn is the number of items in the bucket. Any other choice of data structure (linked list, array, binary search tree) could have linear search time in the word case within the bucket.\ \ Note: Using an LLRB to store buckets requires that the keys be comparable.\ \ chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.5-exercises#problem-2-1)\ \ An array sorted in descending order is a max-heap (the root is the maximum value, and values decrease in level order).\ \ chevron-rightProblem 3[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.5-exercises#problem-3)\ \ The children would be `2k + 1` and `2k + 2`. The parent would be `(k - 1) / 2` (note that this assumes integer division).\ \ chevron-rightProblem 4[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.5-exercises#problem-4)\ \ To implement a stack using a priority queue, keep a counter that increments each time you insert an item. Recently inserted items will always have higher priority, so they will be removed first.\ \ [Previous22.4 Summarychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.4-summary)\ [Next23\. Tree Traversals and Graphschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs)\ \ Last updated 4 months ago\ \ * [Procedural](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.5-exercises#procedural)\ \ * [Metacognitive](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.5-exercises#metacognitive) --- # 28.5 Summary, Exercises | CS61B Textbook Fall 2025 ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.5-summary-exercises#summary) Summary Good code is more than just working code. Code (and complexity) scales with functionality. Practice good design principles in your classes! The real world is ambiguous; you must define the problem and select the solution given tradeoffs of options. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.5-summary-exercises#exercises) Exercises Note that since this chapter is more about design principles than actual content, the exercises merely check factual understanding of lecture material. We encourage you to reflect on the software engineering principles discussed as you work on your own projects. 1. What are two ways to manage complexity? 2. What is the difference between strategic and tactical programming? Which is better for managing complexity? chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.5-summary-exercises#problem-1) **Make code simpler and more obvious**. Eliminate special cases and avoid repetition. Make code as general and parsimonious as possible. **Encapsulate code into modules.** Every module should have a specific purpose. Programmers and users can subsequently use other modules in their design, without having to understand how these modules work. chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.5-summary-exercises#problem-2) Tactical programming focuses on getting something to work, instead of focusing on overall design. In contrast, strategic programming involves planning ahead and selecting from multiple possible implmentations for the cleanest possible solution. Strategic programming requires thinking about possible future changes and emphasizing code quality. For managing complexity, strategic programming is more effective. Tactical programming introduces complexity with small fixes and patches over time, whereas strategic programming aims to eliminate these complexities by changing the underlying design. [Previous28.4 Real World Exampleschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.4-real-world-examples) [Next29\. Reductions and Decompositionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/29.-reductions-and-decomposition) Last updated 4 months ago * [Summary](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.5-summary-exercises#summary) * [Exercises](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.5-summary-exercises#exercises) --- # 16.6 Exercises | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.6-exercises#factual) Factual -------------------------------------------------------------------------------------------------------------------- 1. What is the best and worst-case height of a BST? chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.6-exercises#problem-1) If we insert everything in order, the worst-case height of Θ(N)\\Theta(N)Θ(N) results. In the best case of a perfectly balanced BST, the best-case height is Θ(log⁡N)\\Theta(\\log N)Θ(logN). [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.6-exercises#procedural) Procedural -------------------------------------------------------------------------------------------------------------------------- 1. Suppose that a certain BST has keys that are integers between 1 and 10. During the search for 5, which of the following sequences of keys are possible? * 10, 9, 8, 7, 6, 5 * 4, 10, 8, 7, 5, 3 * 1, 10, 2, 9, 3, 8, 4, 7, 6, 5 * 1, 2, 6, 8, 9, 5 2. Consider the below BST. What is the result after deleting 4 using Hibbard deletion, choosing the sucessor as the replacement? ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-05c62f35f7777289d0568e4bd98292ce71756528%252Fimage%2520%2814%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=8229a6d2&sv=2) 1. Suppose we implement the Stack ADT using an array. What is the worst case runtime of a `push` operation with this underlying data structure? chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.6-exercises#problem-1-1) * check `10, 9, 8, 7, 6, 5`: possible; this is the situation where we have a worst-case linear BST. * `4, 10, 8, 7, 5, 3`: not possible; we terminate our search once we reach the desired node. * check `1, 10, 2, 9, 3, 8, 4, 7, 6, 5`: possible; the idea is that we should always search in the correct "direction" of our target node. If our target node is greater than our current node, then we should go to the right, and our next node should be larger. If our target node is less than our current node, then we should go to the left, and our next node should be smaller. * `1, 2, 6, 8, 9, 5`: not possible; note that this violates the constraint described above. When we reach `8`, we should move to its left branch since our target node `5` is smaller, so we would never search `9`. chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.6-exercises#problem-2) ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-7bee4988111ad70345df603e000aa14e73f43ab6%252Fimage%2520%2844%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=4038be34&sv=2) chevron-rightProblem 3[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.6-exercises#problem-3) The worst-case runtime is Θ(N)\\Theta(N)Θ(N), since a `push` might cause us to resize the underlying array. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.6-exercises#metacognitive) Metacognitive -------------------------------------------------------------------------------------------------------------------------------- 1. If inserting our data into a BST in random order yields log⁡N\\log NlogN height with high probability, why don't we just shuffle our data before inserting into the BST? 2. When we do Hibbard deletion from a BST, we always choose the successor as a replacement. The successor is guaranteed to only have zero or one child--why? chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.6-exercises#problem-1-2) Often in real-world applications, we don't have all our data at once. For example, imagine you're collecting time-based data that you insert into a BST each time a new value is reported. There is no easy way to shuffle your data when you only get one or a few points at a time. chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.6-exercises#problem-2-1) By definition, the successor is the maximum value in the subtree. Suppose, for the sake of contradiction, that the sucessor had two children. Then, it is not the maximum, since it is less than its right child. This is a contradiction, since we said the sucessor is the maximum value in the subtree. As such, the successor is guaranteed to have one child or less (if it has one child, it is its left child). Previous 16.6 Summary Next 17. Tree Traversals and Graphs [Previous16.5 Summarychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.5-summary) [Next17\. Asymptotics IIIchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/17.-asymptotics-iii) Last updated 4 months ago * [Factual](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.6-exercises#factual) * [Procedural](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.6-exercises#procedural) * [Metacognitive](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.6-exercises#metacognitive) --- # 27.4 Summary | CS61B Textbook Fall 2025 The _search problem_ is to store a collection of objects such that they can be rapidly retrieved (i.e. how do we implement a Map or Set). In particular, we are interested in searching by letter or digit. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.4-summary#terminology) Terminology ---------------------------------------------------------------------------------------------------------------------------------------- * Length of string key usually represented by L. * Alphabet size usually represented by R. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.4-summary#tries) Tries ---------------------------------------------------------------------------------------------------------------------------- Know how to insert and search for an item in a Trie. Know that Trie nodes typically do not contain letters, and that instead letters are stored implicitly on edge links. Know that there are many ways of storing these links, and that the fastest but most memory hungry way is with an array of size R. We call such tries R-way tries. **Advantages of Tries.** Tries have very fast lookup times, as we only ever look at as many characters as they are in the data we’re trying to retrieve. However, their chief advantage is the ability to efficiently support various operations not supported by other map/set implementations including: * longestPrefixOf * prefixMatches * spell checking [Previous27.3 Trie String Operationschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.3-trie-string-operations) [Next27.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.5-exercises) Last updated 4 months ago * [Terminology](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.4-summary#terminology) * [Tries](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.4-summary#tries) --- # 31. Quicksort | CS61B Textbook Fall 2025 [31.1 Partitioningchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/31.-quicksort/31.1-partitioning) [31.2 Quicksort Algorithmchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/31.-quicksort/31.2-quicksort-algorithm) [31.3 Quicksort Performance Caveatschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/31.-quicksort/31.3-quicksort-performance-caveats) [31.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/31.-quicksort/31.4-summary) [31.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/31.-quicksort/31.5-exercises) [Previous30.6 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.6-exercises) [Next31.1 Partitioningchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/31.-quicksort/31.1-partitioning) Last updated 4 months ago --- # 25.3 A* Algorithm | CS61B Textbook Fall 2025 Professor Hug's Lecture on Shortest Paths [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.3-a-algorithm#a) A\* ------------------------------------------------------------------------------------------------------------- We ended the section on Dijkstra's by discussing a possible way to make Dijkstra's short-circuit and just stop once it hits a given target. Is this good enough? To answer the above question, we need to sit down and think about how dijkstra's really works. Pictorially, Dijkstra's starts at the source node (imagine the source node being the center of a circle.) And Dijkstra's algorithm now makes concentric circles around this point, in increasing radii, and 'sweeps' these circles, capturing points. So... the first node Dijkstra's visits is the city closest to the source, then the city next-closest, then the city next-closest, and so on. This sounds like a good idea. What Dijkstra's is doing is first visiting all the cities that are 1-unit distance away, then 2 unit-distance away, and so on. In concentric circles. Now imagine the following: on a map of the US, start somewhere in the center, say, Denver. Now I want you to find me a path to New York using Dijkstra's. You'll end up traversing nodes in 'closest concentric circle' order. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fassets%2FScreen%2520Shot%25202019-03-23%2520at%25208.29.56%2520PM.png&width=768&dpr=3&quality=100&sign=b2ad348&sv=2) You'll make a small circle first, just around Denver, visiting all the cities in that circle. Eventually, your circles will get bigger, and you'll make a circle that passes through Las Vegas (and would have visited, by now, all the other cities that fall within the circle.) Then, your circle will be big enough to engulf Los Angeles and Dallas... but you're nowhere close to New York yet. All this effort, all these circles, but still... so far from the target. Short-circuiting helps, but only if you actually hit the target node fast. If only there existed a way to use your prior knowledge: the fact that new-york was eastwards, so you could "hint" your algorithm to prefer nodes that are on the east instead of those that are on the west. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-d1135f811f06add9fe76b97060ed3b32639c1ad9%252Fimage%2520%2891%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=7471c704&sv=2) A\* ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.3-a-algorithm#introducing-a-star) Introducing: A Star No, not the sun. It's an algorithm called A\*. Observe the following: Dijkstra's is a "true" (i.e., not an estimate) measure of the distance **to** a node from the source. So, say, you visit a city in Illinois and your source was Denver, then by that time, you have a true measure of the distance **to** Denver. What we're missing is: some janky, rough estimate of the distance from a node **to** the target node, New York. That would complete the picture. Because then, if you sum these two things up (the measure from the source to the node + the estimate from the node to the target), you get (an estimate from the source to the target.) Of course, the better your original estimate from the node to the target, the better your estimate from the source to the target, the better your A\* algorithm runs. So, let's modify our Dijkstra's algorithm slightly. In Dijkstra's, we used **bestKnownDistToV** as the priority in our algorithm. This time, we'll use **bestKnownDistToV+estimateFromVToGoal** as our heuristic. Here is a [demoarrow-up-right](https://docs.google.com/presentation/d/177bRUTdCa60fjExdr9eO04NHm0MRfPtCzvEup1iMccM/edit#slide=id.g771336078_0_180) ! ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.3-a-algorithm#chicken-and-egg) Chicken And Egg We have a problem. How do we know what the estimate is? I mean, the estimate itself is a **distance**, and we're using A\* to **find** the distance from some node to some other node. It seems like we're in an instance of the classic chicken and egg problem. "What came first? The chicken or the egg?" Aside, FYI, one reddit user had an [ideaarrow-up-right](https://www.reddit.com/r/dadjokes/comments/97768x/i_ordered_a_chicken_and_an_egg_from_amazon_ill/) about this. Well, it's called an estimate because it's exactly that. We use A\* to get the **true** shortest path from a source to a target, but the estimate is something we approximate. Coming up with good estimates is hard sometimes. But to give you an example in our Denver - New York case. What we might do is just look up the GPS Coordinates of these cities, and calculate the straight line distance between those somehow. Of course, this wouldn't be correct because there's probably no straight line that one could take from Denver to NYC, but it's a fairly good estimate! ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.3-a-algorithm#bad-heuristics) Bad Heuristics Suppose that the shortest path from Denver to New York goes through some city CCC. Suppose that my GPS is broken, and so I think that this city CCC is infinity far away from everything, and I set the estimated distance to CCC from every other node in the graph to ∞. What will happen? Well A\* will basically never want to visit this city. (Remember what our priorities are in the priority queue; for this city, the priority will always be ∞∞, even if I visit the immediate neighbors of this city. The estimated distances from the immediate neighbors of this city to this city were set to ∞∞ after all.) So... now what? We lose. A\* breaks. We get the wrong answer back. Oops. The takeaway here is that heuristics need to be good. There are two definitions required for goodness. 1. Admissibility. heuristic(v, target) ≤≤ trueDistance(v, target). (Think about the problem above. The true distance from the neighbor of CCC to CCC wasn't infinity, it was much, much smaller. But our heuristic said it was ∞∞, so we broke this rule.) 2. Consistency. For each neighbor vvv of www: * heuristic(v, target) ≤≤ dist(v, w) + heuristic(w, target) * where dist(v, w) is the weight of the edge from v to w. [Previous25.2 Dijkstra's Algorithmchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.2-dijkstras-algorithm) [Next25.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.4-summary) Last updated 4 months ago * [A\*](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.3-a-algorithm#a) * [Introducing: A Star](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.3-a-algorithm#introducing-a-star) * [Chicken And Egg](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.3-a-algorithm#chicken-and-egg) * [Bad Heuristics](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.3-a-algorithm#bad-heuristics) --- # 22.3 PQ Implementation | CS61B Textbook Fall 2025 PQ Implementation Considerations The actual implementation, which we will use and the book uses, is quite similar to the representation we discussed at the end of the previous chapter. The one difference is that we will leave one empty spot at the beginning of the array to simplify computation. * `leftChild(k)`\= k∗2k \* 2k∗2 * `rightChild(k)`\= k∗2+1k \* 2 + 1k∗2+1 * `parent(k)`\= k/2k / 2k/2 ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.3-pq-implementation#comparing-to-alternative-implementations) Comparing to alternative implementations Methods Ordered Array Bushy BST Hash Table Heap `add` Θ(N)Θ(N)Θ(N) Θ(logN)Θ(logN)Θ(logN) Θ(1)Θ(1)Θ(1) Θ(logN)Θ(logN)Θ(logN) `getSmallest` Θ(1)Θ(1)Θ(1) Θ(logN)Θ(logN)Θ(logN) Θ(N)Θ(N)Θ(N) Θ(1)Θ(1)Θ(1) `removeSmallest` Θ(N)Θ(N)Θ(N) Θ(logN)Θ(logN)Θ(logN) Θ(N)Θ(N)Θ(N) Θ(logN)Θ(logN)Θ(logN) Awesome! We can see that we have improved our runtime and we have also solved the problem of duplicate elements. Couple notes: * Heap operations are **amortized** analysis, since the array will have to resize (not a big deal) * BST's can have constant time `getSmallest` if pointer is stored to smallest element * Array-based heaps take around 1/3rd the memory it takes to represent a heap using approach 1A (direct pointers to children) **Exercise 13.3.1** Some lingering implementation questions: 1. How will the PQ know how to order the items? Say we had a PQ of dogs, would it order by weight or breed? 2. Is there a way to allow for a flexibility of orderings? 3. What could we do to make a MinPQ into a MaxPQ? [Previous22.2 Heapschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.2-heaps) [Next22.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.4-summary) Last updated 4 months ago --- # 31.2 Quicksort Algorithm | CS61B Textbook Fall 2025 The figure below shows a partition performed on the pivot of 5. Notice how 5 in its “proper place” (in other words, it’s exactly where it should be if the entire array was sorted). This suggests that the partition procedure can be used recursively on the two halves to the left and to the right of the pivot element. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Flh6.googleusercontent.com%2FZMiWPSUmOc95h-fBaxMB4I42Djyk0uAL_s8J9XwwqW4KHizpn1xf0wuwciRKa033jyMKK78Vul99b1xYa_fLUOVYFnFhLNZDZstdWSLv-M6Z84H_QD2YOmHeJGjsPOaxIlGWojDANKOPLZ3tVORvZQQ&width=768&dpr=3&quality=100&sign=7ec6986c&sv=2) After partitioning on the pivot element of 5, 5 is in the “proper place” and there are two halves left to be sorted. The left half are the four elements of \[3, 2, 1, 4\], and the right half consists of the three elements of \[7, 8, 6\]. The properties that partitioning provides inspires Tony Hoare’s Quicksort algorithm. **Quicksort Algorithm** To quicksort N items: 1. Partition on the leftmost item as the pivot. 2. Recursively quicksort the left half. 3. Recursively quicksort the right half. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Flh6.googleusercontent.com%2FsrbltrmdQThh9xwjjq-smTXOAVhav1ISWHf4eKX2yZlHr50kHlvZDmH36nNMwLWuMkOlzyApaJWvUdEA1ydHDYKbulYtVuqU6kOn9QbMWAraIOcQae7ymka3zCJKcgzd0u9SeubYP7as_PnnRnEU6Ak&width=768&dpr=3&quality=100&sign=3d7dd202&sv=2) [Demo of quicksort is linked herearrow-up-right](https://docs.google.com/presentation/d/1QjAs-zx1i0_XWlLqsKtexb-iueao9jNLkN-gW9QxAD0/edit?usp=sharing) \\ [Previous31.1 Partitioningchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/31.-quicksort/31.1-partitioning) [Next31.3 Quicksort Performance Caveatschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/31.-quicksort/31.3-quicksort-performance-caveats) Last updated 4 months ago --- # 23.4 Graph Problems | CS61B Textbook Fall 2025 Graph Problems There are many questions we can ask about a graph. For example, * **s-t Path**: Is there a path between vertices s and t? * **Connectivity**: Is the graph connected, i.e. is there a path between all vertices? * **Biconnectivity**: Is there a vertex whose removal disconnects the graph? * **Shortest s-t Path**: What is the shortest path between vertices s and t? * **Cycle Detection**: Does the graph contain any cycles? * **Euler Tour**: Is there a cycle that uses every edge exactly once? * **Hamilton Tour**: Is there a cycle that uses every vertex exactly once? * **Planarity**: Can you draw the graph on paper with no crossing edges? * **Isomorphism**: Are two graphs isomorphic (the same graph in disguise)? What's cool and also weird about graph problems is that it's very hard to _tell_ which problems are very hard, and which ones aren't all that hard. For example, consider the Euler Tour and the Hamilton Tour problems. The former... is a solved problem. It was solved as early as 1873. The solution runs in O(E) where E is the number of edges in the graph. The latter? If you were to solve it efficiently today, you would win every Math award there was, become one of the most famous computer scientists, win a million dollars, etc. No one has been able to solve this **efficiently**. The best known algorithms run in exponential times. People have been working on it for many decades! ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.4-graph-problems#one-step-at-a-time) One step at a time! Alright, well, before we solve the million dollar problem, let's solve the first one on the list. Given a source vertex s and a target vertex t, is there a path between s and t? Graph Connectivity In other words, write a function `connected(s, t)` that takes in two vertices and returns whether there exists a path between the two. To begin, let's guess that we have the following code: **Exercise 17.4.1.** Before you move on, please read the code above, and spend time thinking about it. Does it work? Is it efficient? Run through a couple scenarios. Alright, so, let's try it out. We start with `connected(0, 7)`? That recursively calls `connected(1, 7)`, which then recursively calls `connected(0, 7)`. Uh-oh. Infinite looping has occurred. **Exercise 17.4.2.** How could we fix this? Once again, thinking about this. What was the problem? We visited `s` again... but did we need to? Alright, let's try a "remember what we visited" approach. As it turns out, this does work! Follow the example in [these slidesarrow-up-right](https://docs.google.com/presentation/d/1OHRI7Q_f8hlwjRJc8NPBUc1cMu5KhINH1xGXWDfs_dA/edit#slide=id.g76e0dad85_2_380) to see how. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.4-graph-problems#woah-what-did-we-just-develop) Woah, what did we just develop? Depth First Paths You may not have realized it, but we just developed a **depth-first traversal** (like pre-order, post-order, in-order) but for graphs. What did we do? Well, we marked ourself. Then we visited our first child. Then our first child marked itself, and visited its children. Then our first child's first child marked itself, and visited its children. Intuitively, we're going deep (i.e., down our family tree to our first child, our first child's first child aka our first grandchild, our first grandchild's first child, and so on... visiting this entire lineage), before we even touch our second child. Up next, we'll see the opposite notion, where first we visit all our children, then our grandchildren, and so on. [Previous23.3 Graphschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.3-graphs) [Next24\. Graph Traversals and Implementationschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations) Last updated 4 months ago * [One step at a time!](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.4-graph-problems#one-step-at-a-time) * [Woah, what did we just develop?](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.4-graph-problems#woah-what-did-we-just-develop) Copy if (s == t): return true; for child in neighbors(s): if isconnected(child, t): return true; return false; Copy mark s // i.e., remember that you visited s already if (s == t): return true; for child in unmarked_neighbors(s): // if a neighbor is marked, ignore! if isconnected(child, t): return true; return false; --- # 24.4 Exercises | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.4-exercises#factual) Factual ------------------------------------------------------------------------------------------------------------------------------------------- 1. Suppose we want to find the vertex with minimal degree (fewest neighbors). Is an adjacency matrix or adjacency list better? chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.4-exercises#problem-1) An adjacency list would be better: we can just assess the size of each list in constant time versus iterating over all VVV vertices in each row of the adjacency matrix to count the number of neighbors. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.4-exercises#procedural) Procedural ------------------------------------------------------------------------------------------------------------------------------------------------- ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-7715d730519098581a73dbdf11a1a5d27f47d681%252Fimage%2520%2871%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=c20e0163&sv=2) 1. For the graph above, write the representation for the adjacency matrix representation. 2. Repeat (1) for the list of edges representation. 3. Repeat (1) for the adjacency list representation. 4. Run BFS starting from A for the graph above, and list the order in which vertices are visited. Break ties alphabetically. 5. Run DFS starting from A for the graph above, and list the postorder/postorder traversals. Break ties alphabetically. chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.4-exercises#problem-1-1) chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.4-exercises#problem-2) chevron-rightProblem 3[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.4-exercises#problem-3) chevron-rightProblem 4[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.4-exercises#problem-4) `[A, B, C, D, E, F]` chevron-rightProblem 5[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.4-exercises#problem-5) preorder: `[A, B, C, F, D, E]` postorder: `[D, F, C, E, B, A]` [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.4-exercises#metacognitive) Metacognitive ------------------------------------------------------------------------------------------------------------------------------------------------------- 1. Suppose we find some shortest path from `a` to `b` using BFS. Consider a vertex `c` that is on the path between `a` and `b`. What can we say about the shortest path from `c` to `b`? 2. Problem 4c from the [Spring 2015 Midterm 2arrow-up-right](https://drive.google.com/file/d/1uE1QlF4YguWVp8m8UJ97R2xPC4b1NnQ5/view?usp=sharing) . chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.4-exercises#problem-1-2) Call the original shortest path from `a` to `b` pabp\_{ab}pab​, and the path from `c` to `b` along this path pcb∗p^{\*}\_{cb}pcb∗​. If there is some shorter path between `c` and `b` pcb′p'\_{cb}pcb′​, then we could simply take the path from `a` to `c` in the original pabp\_{ab}pab​, then take pcb′p'\_{cb}pcb′​ to find an even shorter path between `a` and `b`. However, we stated earlier that pabp\_{ab}pab​ is the shortest path between `a` and `b`. This is a contradiction. Thus, the shortest path between `c` and `b` must be on the shortest path between `a` and `b`. chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.4-exercises#problem-2-1) [Solutionsarrow-up-right](https://drive.google.com/file/d/1IYt4VbzdX4dTekh6cYAC8tigpJ_LgljV/view?usp=sharing) are linked here and on the course website. [Previous24.3 Summarychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.3-summary) [Next25\. Shortest Pathschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths) Last updated 4 months ago * [Factual](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.4-exercises#factual) * [Procedural](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.4-exercises#procedural) * [Metacognitive](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.4-exercises#metacognitive) Copy A B C D E F A F T F F F F B T F T T T F C F T F F F T D F T F F F T E F T F F F F F F F T T F F Copy [{A, B}, \ {B, A}, {B, C}, {B, D}, {B, E}, \ {C, B}, {C, F},\ {D, B}, {D, F},\ {E, B}, \ {F, C}, {F, D}] Copy A: [B] B: [A, C, D, E] C: [B, F] D: [B, F] E: [B] F: [C, D] --- # 18.7 Exercises | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.7-exercises#factual) Factual -------------------------------------------------------------------------------------------------------------- 1. Which of the following are valid 2-3 trees? ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-2dfd05832e1aaec33d0e64da629e94c564bc163e%252Fimage%2520%2854%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=edaa079b&sv=2) chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.7-exercises#problem-1) Only (a). A 2-3 tree only allows up to 3 children, which means a node can only have at most 2 values. (b) has an extra value in the rightmost node. A 2-3 tree must also be perfectly balanced. (c) is imbalanced. A B-Tree node always has one more child than the number of values. So in a 2-3 tree, a node with 1 value has 2 children, and a node with 2 values has 3 children. (d) violates this with the node `21`, which has one value but 3 children. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.7-exercises#procedural) Procedural -------------------------------------------------------------------------------------------------------------------- 1. Draw the 2-3 tree that results from inserting `1, 2, 3, 4, 5, 6, 7` in order. 2. [Problem 1arrow-up-right](https://drive.google.com/file/d/1Vo8p4vbOGt7eY5TtalvAEnk4ignpTVvm/view?usp=sharing) of the Spring 2018 Midterm 2 chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.7-exercises#problem-1-1) Check your answers using this [interactive visualizer.arrow-up-right](https://www.cs.usfca.edu/~galles/visualization/BTree.html) chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.7-exercises#problem-2) [Solutionsarrow-up-right](https://drive.google.com/file/d/1LIyFXwHYCWXNqIgKTsTyKiOYnB79_ykk/view?usp=sharing) and [walkthrougharrow-up-right](https://www.youtube.com/watch?v=nMZn4EV0gGw) are linked here and on the course website. [Previous18.6 Summarychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.6-summary) [Next19\. Red Black Treeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees) Last updated 4 months ago * [Factual](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.7-exercises#factual) * [Procedural](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.7-exercises#procedural) --- # 18.2 Big O vs. Worst Case | CS61B Textbook Fall 2025 Consider the following statements about BSTs. Which of the following are true? 1. The worst-case height of a BST is Θ(N)\\Theta(N)Θ(N). 2. BST height is O(N)O(N)O(N). 3. BST height is O(N2)O(N^2)O(N2). The answer is that all three statements are true. BSTs always have a height that is linear or better, and a linear height is obviously "less than" the quadratic upper bound in the last point. However, a more tricky question is which of the three statements is _the most informative_. The answer here is the first statement: it gives an _exact_ upper and lower bound unlike the other statements. O(N)O(N)O(N) could mean linear, logarithmic, square-root, or constant, but Θ(N)\\Theta(N)Θ(N) can only mean linear. For an analogy, consider the following statements about the worst-case cost of a hotel room: 1. The most expensive room is $639/night. 2. The most expensive room is less than or equal to $2000/night. Here, we see that the first statement gives us exact information, whereas the second statement does not. In the second statement, the most expensive room could be $2000, $10, or anywhere in between. However, _both are statements about the worst case_. Applying this to asymptotic notation, this means that we can refer to the worst case with Θ\\ThetaΘ, OOO, or even Ω\\OmegaΩ. **Big O is not the same as the worst case!** [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.2-big-o-vs.-worst-case#using-big-o) Using Big O --------------------------------------------------------------------------------------------------------------------------------- If Θ\\ThetaΘ is always more informative than OOO, then why do we bother using Big O notation at all? There are several reasons: * We can make broader statements. For example, saying "binary search is O(log⁡N)O(\\log N)O(logN) is correct, but saying "binary search tree is Θ(logN)\\Theta(log N)Θ(logN)" would not be correct, since it can be constant in certain scenarios. * Sometimes, it is not possible or extremely difficult to determine the exact runtime. In such cases, we would still like to provide a generalized upper bound. [Previous18.1 BST Performancechevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.1-bst-performance) [Next18.3 B-Tree Operationschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.3-b-tree-operations) Last updated 4 months ago --- # 32. Software Engineering II | CS61B Textbook Fall 2025 [32.1 Complexity IIchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.1-complexity-ii) [32.2 Sources of Complexitychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.2-sources-of-complexity) [32.3 Modular Designchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.3-modular-design) [32.4 Teamworkchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.4-teamwork) [32.5 Exeriseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.5-exerises) [Previous31.5 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/31.-quicksort/31.5-exercises) [Next32.1 Complexity IIchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.1-complexity-ii) Last updated 4 months ago --- # 16.3 BST Operations | CS61B Textbook Fall 2025 ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.3-bst-operations#search) Search To search for something, we employ binary search, which is made easy due to the BST property. We know that the BST is structured such that all elements to the right of a node are greater and all elements to the left are smaller. Knowing this, we can start at the root node and compare it with the element, X, that we are looking for. If X is greater to the root, we move on to the root's right child. If its smaller, we move on to the root's left child. We repeat this process recursively until we either find the item or we get to a leaf, in which case the tree does not contain the item. If our tree is relatively "bushy", the find operation will run in log⁡n\\log nlogn time because the height of the tree is log⁡n\\log nlogn. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.3-bst-operations#insert) Insert We **always** insert at a leaf node! First, we search in the tree for the node. If we find it, then we don't do anything. If we don't find it, we will be at a leaf node already. At this point, we can just add the new element to either the left or right of the leaf, preserving the BST property. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.3-bst-operations#delete) Delete Deleting from a binary tree is a little bit more complicated because whenever we delete, we need to make sure we reconstruct the tree and still maintain its BST property. Let's break this problem down into three categories: * the node we are trying to delete has no children * has 1 child * has 2 children #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.3-bst-operations#deletion-no-children) Deletion: No Children If the node has no children, it is a leaf, and we can just delete its parent pointer and the node will eventually be swept away by the [garbage collectorarrow-up-right](https://stackoverflow.com/questions/3798424/what-is-the-garbage-collector-in-java) . #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.3-bst-operations#deletion-one-child) Deletion: One Child If the node only has one child, we know that the child maintains the BST property with the parent of the node because the property is recursive to the right and left subtrees. Therefore, we can just reassign the parent's child pointer to the node's child and the node will eventually be garbage collected. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.3-bst-operations#deletion-two-children) Deletion: Two Children If the node has two children, the process becomes a little more complicated because we can't just assign one of the children to be the new root. This might break the BST property. Instead, we choose a new node to replace the deleted one. We know that the new node must: * be > than everything in left subtree. * be < than everything right subtree. In the below tree, we show which nodes would satisfy these requirements given that we are trying to delete the `dog` node. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-af9dff25f8bdd20a44da9a8b3ea20f741e38b69e%252Fimage%2520%287%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=492ee92&sv=2) Possible candidates to replace `dog` after deletion To find these nodes, you can just take the right-most node in the left subtree or the left-most node in the right subtree. Then, we replace the `dog` node with either `cat` or `elf` and then remove the old `cat` or `elf` node. This is called **Hibbard deletion**, and it gloriously maintains the BST property amidst a deletion. [Previous16.2 BST Definitionschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.2-bst-definitions) [Next16.4 BSTs as Sets and Mapschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.4-bsts-as-sets-and-maps) Last updated 4 months ago * [Search](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.3-bst-operations#search) * [Insert](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.3-bst-operations#insert) * [Delete](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/16.-adts-and-bsts/16.3-bst-operations#delete) Copy static BST find(BST T, Key sk) { if (T == null) return null; if (sk.equals(T.key)) return T; else if (sk ≺ T.key) return find(T.left, sk); else return find(T.right, sk); } Copy static BST insert(BST T, Key ik) { if (T == null) return new BST(ik); if (ik ≺ T.key) T.left = insert(T.left, ik); else if (ik ≻ T.key) T.right = insert(T.right, ik); return T; } --- # 19.5 Summary | CS61B Textbook Fall 2025 * Binary search trees are simple, but they are subject to imbalance which leads to crappy runtime. 2-3 Trees (B Trees) are balanced, but painful to implement. * LLRB insertion is simple to implement (deletion is a bit harder to implement, we won't go over the specifics in this course). * Use three basic operations to maintain the balanced structure, namely rotateLeft, rotateRight, and color flip. * LLRBs maintain correspondence with 2-3 trees, Standard Red-Black trees maintain correspondence with 2-3-4 trees. * Java’s [TreeMaparrow-up-right](https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/999dbd4192d0f819cb5224f26e9e7fa75ca6f289/src/java.base/share/classes/java/util/TreeMap.java) is a red-black tree that corresponds to 2-3-4 trees. * 2-3-4 trees allow glue links on either side (see [Red-Black Treearrow-up-right](http://en.wikipedia.org/wiki/Red%E2%80%93black_tree) ). * More complex implementation, but faster. [Previous19.4 Runtime Analysischevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.4-runtime-analysis) [Next19.6 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.6-exercises) Last updated 4 months ago --- # 26. Minimum Spanning Trees | CS61B Textbook Fall 2025 [26.1 MSTs and Cut Propertychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.1-msts-and-cut-property) [26.2 Prim's Algorithmchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.2-prims-algorithm) [26.3 Kruskal's Algorithmchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.3-kruskals-algorithm) [26.4 Chapter Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.4-chapter-summary) [26.5 MST Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.5-mst-exercises) [Previous25.5 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.5-exercises) [Next26.1 MSTs and Cut Propertychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.1-msts-and-cut-property) Last updated 4 months ago --- # 25.4 Summary | CS61B Textbook Fall 2025 **Dijktra’s Algorithm and Single-Source Shortest Paths.** Suppose we want to record the shortest paths from some source to every single other vertex (so that we can rapidly found a route from s to X, from s to Y, and so forth). We already know how to do this if we’re only counting the number of edges, we just use BFS. But if edges have weights (representing, for example road lengths), we have to do something else. It turns out that even considering edge weights, we can preprocess the shortest route from the source to every vertex very efficiently. We store the answer as a “shortest paths tree”. Typically, a shortest paths tree is stored as an array of edgeTo\[\] values (and optionally distTo\[\] values if we want a constant time distTo() operation). To find the SPT, we can use Dijkstra’s algorithm, which is quite simple once you understand it. Essentially, we visit each vertex in order of its distance from the source, where each visit consists of relaxing every edge. Informally, relaxing an edge means using it if its better than the best known distance to the target vertex, otherwise ignoring it. Or in pseudocode: Copy Dijkstra(G, s): while not every vertex has been visited: visit(unmarked vertex v for which distTo(v) is minimized) Where visit is given by the following pseudocode: Copy visit(v): mark(v) for each edge e of s: relax(e) And finally, relax is given by: Copy relax(e): v = e.source w = e.target currentBestKnownWeight = distTo(w) possiblyBetterWeight = distTo(v) + e.weight if possiblyBetterWeight < currentBestKnownWeight Use e instead of whatever we were using before Runtime is O(V∗logV+V∗logV+E∗logV)O(V \* logV + V \* log V + E \* logV)O(V∗logV+V∗logV+E∗logV), and since E\>VE > VE\>V for any graph we’d run Dijkstra’s algorithm on, this can be written as more simply O(E log V). See slides for runtime description. **A\* Single-Target Shortest Paths.** If we need only the path to a single target, then Dijkstra’s is inefficient as it explores many many edges that we don’t care about (e.g. when routing from Denver to NYC, we’d explore everything within more than a thousand miles in all directions before reaching NYC). To fix this, we make a very minor change to Dijkstra’s, where instead of visiting vertices in order of distance from the source, we visit them in order of distance from the source + h(v), where h(v) is some heuristic. Or in pseudocode: It turns out (but we did not prove), that as long as h(v) is less than the true distance from s to v, then the result of A\* will always be correct. Note: In the version in class, we did not use an explicit ‘mark’. Instead, we tossed everything in the PQ, and we effectively considered a vertex marked if it had been removed from the PQ. [Previous25.3 A\* Algorithmchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.3-a-algorithm) [Next25.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.5-exercises) Last updated 4 months ago Copy A*(G, s): while not every vertex has been visited: visit(unmarked vertex v for which distTo(v) + h(v) is minimized) --- # 23.3 Graphs | CS61B Textbook Fall 2025 Trees are great, aren't they? But as we saw, we could draw some things using nodes and edges that weren't trees. Specifically, our restriction that there can only be one path between any two nodes didn't fit every situation. Let's see what happens when we get rid of that restriction. Graph Definition ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.3-graphs#what-is-a-graph) What is a graph? A graph consists of: * A set of nodes (or vertices) * A set of zero of more edges, each of which connects two nodes. That's it! No other restrictions. All of the structures below in green? Everything is a valid graph! The second one is also a tree, but none of the others are. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-45410fbf5676ff2a7a470ccd1ec03ce2e33a138b%252FScreen%2520Shot%25202023-02-26%2520at%25202.02.16%2520PM.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=8dde86ab&sv=2) Graphs In general, note that **all trees are also graphs, but not all graphs are trees.** ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.3-graphs#simple-graphs-only) Simple Graphs only Graphs can be divided into two categories: _simple_ graphs and _multigraphs_ (or complicated graphs, a term I invented, because that's how I like to think of them.) Fortunately, in this course (and almost all applications and research) focuses only on simple graphs. So when we say "graph" in this course, you should always think of a "simple graph" (unless we say otherwise.) Well, it's time to address the elephant in the room. What's a simple graph? ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-5c9bd6abfeba650d201319515aa3f56f5e4fb12b%252FScreen%2520Shot%25202023-02-26%2520at%25202.03.21%2520PM.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=7951b42b&sv=2) Simple Graphs and Multigraphs Look at the graphs in red. The graph in the middle has 2 distinct edges going from/to bottom-next to/from bottom-left node. In other words, there are multiple edges between two nodes. This is **not** a simple graph, and we ignore their existence unless specified otherwise. Graphs like these are called multigraphs. Look at the third graph. It has a loop! An edge from a node to itself! We don't allow this either. Graphs like these are sometimes categorized as multigraphs, and sometimes, even multigraphs explicitly ban self-loops. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.3-graphs#more-categorizations) More categorizations. Graphs are simple in the following text, and in this course, unless specified otherwise. But there are more categorizations. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-01efd3f97dc8f6c7976119f7e3dcdb5473aa5218%252FScreen%2520Shot%25202023-02-26%2520at%25202.04.26%2520PM.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=b4dec9a2&sv=2) There are undirected graphs, where an edge `(u, v)` can mean that the edge goes from the nodes u to v and from the nodes v to u too. There are directed graphs, where the edge `(u, v)` means that the edge starts at u, and goes to v (and the vice versa is not true, unless the edge `(v, u)` also exists.) Edges in directed graphs have an arrow. There are also acyclic graphs. These are graphs that don't have any cycles. And then there are cyclic graphs, i.e., there exists a way to start at a node, follow some **unique** edges, and return back to the same node you started from. In the above picture, we can clearly see the difference between how we draw directed and undirected edges. Take a look at the cyclic graphs. If you start at `a`, you can run back around using only distinct edges and get back to `a`. Thus, the graph is cyclic. Take a look at the top-left graph. Is there any node `n`, such that if you start at `n`, you can follow some distinct edges, and get back to `n`? Nope! (Remember than for directed edges, you must follow the directions. You can go from `a` to `b` but not `b` to `a`.) [Previous23.2 Tree Traversalschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.2-tree-traversals) [Next23.4 Graph Problemschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.4-graph-problems) Last updated 4 months ago * [What is a graph?](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.3-graphs#what-is-a-graph) * [Simple Graphs only](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.3-graphs#simple-graphs-only) * [More categorizations.](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/23.-tree-traversals-and-graphs/23.3-graphs#more-categorizations) --- # 38.1 The end is near | CS61B Textbook Fall 2025 Another Software Engineering lecture!? Yes!! We're actually now almost done with 61B. Best of luck on finals! Today, we'll actually just be spending time reflecting on 61B. 61B has been taught since ~Spring 1994. Before that, it was CS60C, which goes back to at least 1988. In modern times there have been 4 varieties of 61B: * Hilfinger: 4 extremely long real world projects that are somewhat based on data structures material. * Hug: 1 (or 2) long real world project that is somewhat based on data structures material. Remaining material ties in tightly to lectures. * 61BL: Lab based class that focuses heavily on data structures, but with one large real world project (Gitlet). * Shewchuk / Yelick (extinct): Focus on implementing data structures. No large real world project ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/38.-software-engineering-iv/38.1-the-end-is-near#id-61b-1.0) 61B 1.0 Gitlet was first offered in the Spring 2015 offering of 61B. * My first solo offering of the class. * Projects had significant authorship from students. * Project 0 - Bomb Checkers (me, but implemented by Jimmy Lee). * Project 1 - ngordnet (me). * Project 2 - Gitlet (Joey Moghadam). * Project 3 - Fun with Tries (me, adapted from my old Princeton HWs). * Joey also used the project as one of his assignments in Summer 2015. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/38.-software-engineering-iv/38.1-the-end-is-near#id-61b-2.0) 61B 2.0 CS61B Version 2 (Spring 2016, 2017) * Fall 2014/Spring 2015 observation: Hated that students had to split time between the core data structures content and a huge project that wasn’t related to that content. * Decided to have the messy real world project due right before data structures: * 2016: Build a text editor. * 2017: Create software for manipulating text databases. (This is a very cool project! I really hope you can try this for yourselves and then take CS 186!!) ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/38.-software-engineering-iv/38.1-the-end-is-near#feedback-helps) Feedback helps... ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Flh3.googleusercontent.com%2FEYU0gslQ-pFXdqjr9voSyTbvcHUY7Ztp1DP5FmIlRDXW1T7_f2OyJObaQTxmHPs8CZLOnMm-UX5SpRpp_uzbCQvd161nJZh7jYifdMJM9Qo35KrbaHChB0wYBKbiLlt3qRjRJZCEsmb9O5EFrSJiMZlvDw%3Ds2048&width=768&dpr=3&quality=100&sign=973083&sv=2) Gitlet Feedback CS61B Current Version (3.6) (Fall 2022 and Spring 2023) * Replaced Gitlet with Ngordnet. * Ngordnet is much more data structures focused. * 2A: Build a TimeSeries and build an NgramMap. * 2B: Build whatever you need to support additional functionality, including implementing a graph somehow. GSIs voted ~3 to 1 to keep Ngordnet over Gitlet. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/38.-software-engineering-iv/38.1-the-end-is-near#summary) Summary Overall this course was meant to help you with Software Engineering. And all of you have done an amazing job making data structures such as HashMaps and ArrayLists and using Java to solve real-world problems that I hope was interesting! Many people even come out of this class signing up for Linguistics 100 after learning about hyponyms and doing Ngordnet. Wherever you now go equipped with this amazing knowledge of 61B, I wish you the best of luck! Here's to solving more problems with CS! Cheers and good luck on Finals! Spring/summer 2015 Gitlet was way too hard. * No testing provided. * No tips on persistence. [Previous38\. Software Engineering IVchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/38.-software-engineering-iv) [Next39\. Compression and Complexitychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/39.-compression-and-complexity) Last updated 4 months ago * [61B 1.0](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/38.-software-engineering-iv/38.1-the-end-is-near#id-61b-1.0) * [61B 2.0](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/38.-software-engineering-iv/38.1-the-end-is-near#id-61b-2.0) * [Feedback helps...](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/38.-software-engineering-iv/38.1-the-end-is-near#feedback-helps) * [Summary](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/38.-software-engineering-iv/38.1-the-end-is-near#summary) --- # 12.6 Exercises | CS61B Textbook Fall 2025 ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.6-exercises#factual) Factual 1. What methods are required for a class that is Iterable? 2. Which of the following is true about the `java.util.Se`t and the `java.util.List` interfaces? * If we add `String[][]` objects to a `Set` and a `List`, the size of the set will always be less than or equal to the size of the list. * The `java.util.ArrayList` class is an implementation of the `java.util.List` interface. * The `Set` and `List` interfaces extend the `Iterator` interface. * The `Set` and `List` interfaces extend the `Iterable` interface. 3. Suppose we have a class that implements `Iterator`. What methods must it override in order to compile? chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.6-exercises#problem-1) An `Iterable` is required to have the `iterator()` method, which returns an `Iterator`. chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.6-exercises#problem-2) * **If we add** `**String[][]**` **objects to a** `**Set**` **and a** `**List**`**, the size of the set will always be less than or equal to the size of the list.** Sets only have unique items, while lists can have duplicates, so if we add the same elements to both the list will always have at least as many elements as the set. * **The** `**java.util.ArrayList**` **class is an implementation of the** `**java.util.List**` **interface.** One implementation of the `List` interface in Java is the ArrayList class. * **The** `**Set**` **and** `**List**` **interfaces extend the** `**Iterable**` **interface.** Sets and Lists in Java can be used in enhanced for loops, which means that they are `Iterable`. chevron-rightProblem 3[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.6-exercises#problem-3) An `Iterator` must override `hasNext()`, which returns a boolean indicating whether there are more elements in the `Iterator`, and `next()`, which returns the next item. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.6-exercises#conceptual) Conceptual 1. Why do we want to override the `.equals` method? chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.6-exercises#problem-1-1) The `.equals()` method inherited from `Object` only checks if two items have the same memory address. This is undesireable behavior for many user-written classes in Java. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.6-exercises#metacognitive) Metacognitive ---------------------------------------------------------------------------------------------------------------------------------------------------------- 1. In lecture, you built the `ArraySetIterator` class. Modify the lecture class to take in a `Comparator` and an item of generic type `T` called `ref` in the constructor. This new iterator should only return items greater than `T`. For reference, the code for `ArraySetIterator` is included below. 1. Problem 7 from the Spring 2018 Midterm 2 chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.6-exercises#problem-1-2) chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.6-exercises#problem-2-1) [Solutionsarrow-up-right](https://drive.google.com/file/d/1LIyFXwHYCWXNqIgKTsTyKiOYnB79_ykk/view) and [walkthrougharrow-up-right](https://www.youtube.com/watch?v=nMZn4EV0gGw) are linked here and on the course website. [Previous12.5 Chapter Summarychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.5-chapter-summary) [Next13\. Asymptotics Ichevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i) Last updated 6 months ago * [Factual](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.6-exercises#factual) * [Conceptual](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.6-exercises#conceptual) * [Metacognitive](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.6-exercises#metacognitive) Copy private class ArraySetIterator implements Iterator { private int pos; public ArraySetIterator() { pos = 0; } public boolean hasNext() { return pos < size; } public T next() { T returnItem = items[pos]; pos += 1; return returnItem; } } Copy public class ArraySetGreaterIterator implements Iterator { private int pos; private T ref; private Comparator comp; public ArraySetGreaterIterator(T ref, Comparator comp) { this.ref = ref; this.comp = comp; } @Override public boolean hasNext() { return pos < size; } @Override public T next() { T returnItem = items[pos]; while (comp.compare(returnItem, ref) <= 0) { pos += 1; returnItem = items[pos]; } return returnItem; } } --- # 38. Software Engineering IV | CS61B Textbook Fall 2025 [38.1 The end is nearchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/38.-software-engineering-iv/38.1-the-end-is-near) [Previous37.5 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/37.-sorting-and-data-structures-conclusion/37.5-exercises) [Next38.1 The end is nearchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/38.-software-engineering-iv/38.1-the-end-is-near) Last updated 4 months ago --- # 20.2 Hash Code | CS61B Textbook Fall 2025 We face the problem that not every object in Java can easily be converted to a number. However, the key idea behind hashing is the transformation of any object into a numeric representation. The key is to have a hashing function transform our keys into different values, and convert that number into an index to then access the array. We achieve this through our own implementation of a `hashCode()` function, with a return value of an `int` type. This `int` type is our _hash value_. The built-in String class in Java, for example, might have the following code block: Copy public class String { public int hashCode() { //implementation here } } Based on this example, we can call `key.hashCode()` to generate an integer hash code for a `String` instance called `key`. Professor Hug's Lecture on Hash Codes [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.2-hash-code#memory-inefficiency-in-hash-codes) Memory Inefficiency in Hash Codes -------------------------------------------------------------------------------------------------------------------------------------------------------------------- An issue mentioned earlier is memory inefficiency: for a small range of hash values, we can get away with an array that individuates each hash value. That is, every index in the array would represent a unique hash value. This works well if our indices are small and close to zero. But remember that Java’s 32-bit integer type can support numbers anywhere between -2,147,483,648 and 2,147,483,647. Now, most of the time, our data won’t use anywhere near that many values. But even if we only wanted to support special characters, our array would still need to be 1,112,064 elements long! Instead, we'll slightly modify our indexing strategy. Let's say we only want to support an array of length 10 so as to avoid allocating excessive amounts of memory. How can we turn a number that is potentially millions or billions large into a value between 0 and 9, inclusive? ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.2-hash-code#wrapping) Wrapping! The **modulus operator (%)** allows us to achieve this. _Review_: The result of the modulo operator is like a remainder in fractional division. For example, `65 % 10` returns `5` because after dividing 65 by 10, we are left with a remainder of 5. Thus as other examples, `3 % 10 = 3`, `20 % 10 = 0`, and `19 % 10 = 9`. For an intuitive understanding of this, think about how we used the modulo operator in Project 1: Deques to ensure you avoided `IndexOutOfBounds` Exceptions and could accurately index into your deque via the concept of "wrapping around" the array. Returning back to our original problem, we want to be able to convert any number to a value between 0 and 9, inclusive. Given our discussion on the modulo operator, we can see that any number mod 10 will return an integer value between 0 and 9. This is what we need to index into an array of size 10! More generally, we can locate the correct index for any key with the following: where `array` is the underlying array representing our hash table. _Quick Note_: In Java, the `Math.floorMod` function will perform the modulus operation while correctly accounting for negative integers, whereas `%` does not. [Previous20.1.3 A third attempt: DataIndexedStringSetchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.1-introduction-to-hashing-data-indexed-arrays/20.1.3-a-third-attempt-dataindexedstringset) [Next20.3 "Valid" & "Good" Hashcodeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.3-valid-and-good-hashcodes) Last updated 4 months ago * [Memory Inefficiency in Hash Codes](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.2-hash-code#memory-inefficiency-in-hash-codes) * [Wrapping!](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.2-hash-code#wrapping) Copy Math.floorMod(key.hashCode(), array.length) --- # 20.5 Resizing & Hash Table Performance | CS61B Textbook Fall 2025 No matter how good our `hashCode()` method is, if the underlying array of our hash table is small and we add a lot of keys to it, then we will start getting more and more collisions. Because of this, a hash table should expand its underlying array once it starts to fill up (much like how an `ArrayList` expands once it fills up). Professor Hug's Lecture on why we have resizing and how it works! To keep track of how full our hash table is, we define the term **load factor**, which is the ratio of the number of elements inserted over the total physical length of the array. ![A picture of an equation which states that the load factor is equal to the number of elements divided by the total capacity of the array.](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-4b4e204e8cd1b4db3148ffa6aa5c68fec9a3993d%252Fimage%2520%2855%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=37da692e&sv=2) A very important equation For our hash table, we will define the maximum load factor that we will allow. **If adding another key-value pair would cause the load factor to exceed the specified maximum load factor, then the hash table should resize.** This is usually done by doubling the underlying array length. Java’s default maximum load factor is 0.75 which provides a good balance between a reasonably-sized array and reducing collisions. Note that if we are trying to add a key-value pair and the key already exists in the hash map, the corresponding value should be updated but no resizing should occur. As an example, let’s consider what happens if our hash table has an array length of 10 and currently contains 7 elements. Each of these 7 elements are hashed modulo 10 because we want to get an index within the range of 0 through 9. The current load factor is 7/10, or 0.7, just under the threshold. If we try to insert one more element, we would have a total of 8 elements in our hash table and a load factor of 0.8. Because this would cause the load factor to exceed the maximum load factor, we must resize the underlying array to length 20 before we insert the element. Remember that since our procedure for locating an entry in the hash table is to take the `hashCode() % array.length` and our array’s length has changed from 10 to 20, all the elements in the hash table need to be relocated. Once all the elements have been relocated and our new element has been added, we will have a load factor of 8/20, or 0.4, which is below the maximum load factor. [Previous20.4 Handling Collisions: Linear Probing and External Chainingchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.4-handling-collisions-linear-probing-and-external-chaining) [Next20.6 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.6-summary) Last updated 4 months ago --- # 24.2 Representing Graphs | CS61B Textbook Fall 2025 Professor Hug's Lecture on Graphs We will discuss our choice of **API**, and also the **underlying data structures** used to represent the graph. Our decisions can have profound implications on our _runtime_, _memory usage_, and _difficulty of implementing various graph algorithms_. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.2-representing-graphs#graph-api) Graph API An API (Application Programming Interface) is a list of methods available to a user of our class, including the method signatures (what arguments/parameters each function accepts) and information regarding their behaviors. You have already seen APIs from the Java developers for the classes they provide, such as the [Dequearrow-up-right](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Deque.html) . For our Graph API, let's use the common convention of assigning each unique node to an integer number. This can be done by maintaining a map which can tell us the integer assigned to each original node label. Doing so allows us to define our API to work with integers specifically, rather than introducing the need for generic types. We can then define our API to look something like this perhaps: Clients (people who wish to use our Graph data structure), can then use any of the functions we provide to implement their own algorithms. The methods we provide can have a significant impact on how easy/difficult it may be for our clients to implement particular algorithms. Now that we know how to draw a graph on paper and understand the basic concepts and definitions, we can now consider how a graph should be represented inside of a computer. We want to be able to get quick answers for the following questions about a graph: * Are given vertices `u` and `v` adjacent? * Is vertex `v` incident to a particular edge `e`? * What vertices are adjacent to `v`? * What edges are incident to `v`? Imagine that we want to represent a graph that looks like this: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-5b3ab2b49fd937dc7c9656da47840521415ce6c7%252Fimage%2520%2881%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=c825e56c&sv=2) One data structure we could use to implement this graph is called an _array of adjacency lists_. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.2-representing-graphs#the-adjacency-list) The Adjacency List --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- In an adjacency list, an array is created that has the same size as the number of vertices in the graph. Each position in the array represents one of the vertices in the graph. Each of these positions point to a list. These lists are called adjacency lists, as each element in the list represents a neighbor of the vertex. The array of adjacency lists that represents the above graph looks like this: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-1677c600a20920017a7b11f942f572b4d17d3bcd%252Fimage%2520%2868%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=876bf8f6&sv=2) Another data structure we could use to represent the edges in a graph is called an _adjacency matrix_. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.2-representing-graphs#the-adjacency-matrix) The Adjacency Matrix ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- In this data structure, we have a two dimensional array of size N×N (where N is the number of vertices) which contains boolean values. The (_i_, _j_)th entry of this matrix is true when there is an edge from _i_ to _j_ and false when no edge exists. Thus, each vertex has a row and a column in the matrix, and the value in that table says true or false whether or not that edge exists. The adjacency matrix that represents the above graph looks like this: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-d128971e8f123a52235f9b9fa821f2b973f69741%252Fimage%2520%2834%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=45fb0945&sv=2) [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.2-representing-graphs#efficiency) Efficiency ----------------------------------------------------------------------------------------------------------------------------------------------------------- Your choice of underlying data structure can impact the runtime and memory usage of your graph. This table from the [slidesarrow-up-right](https://docs.google.com/presentation/d/11iacyiFt3QUrzo1yAU_xoXAjGTH4UzV7o6CR04HYRrI/edit#slide=id.g54593997ea_0_422) summarizes the efficiencies of each representation for various operations. It is strongly not recommended to directly just copy this on to your cheatsheet for the exams without taking the time to first understand where and how these bounds fundamentally came to be. The lecture contains walkthroughs explaining the rationale in detail behind several of these cells. Further, DFS/BFS on a graph backed by adjacency lists runs in O(V+E), while on a graph backed by an adjacency matrix runs in O(V^2). See the [slidesarrow-up-right](https://docs.google.com/presentation/d/11iacyiFt3QUrzo1yAU_xoXAjGTH4UzV7o6CR04HYRrI/) for help in understanding why. [Previous24.1 BFS & DFSchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.1-bfs-and-dfs) [Next24.3 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.3-summary) Last updated 4 months ago * [Graph API](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.2-representing-graphs#graph-api) * [The Adjacency List](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.2-representing-graphs#the-adjacency-list) * [The Adjacency Matrix](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.2-representing-graphs#the-adjacency-matrix) * [Efficiency](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/24.-graph-traversals-and-implementations/24.2-representing-graphs#efficiency) Copy public class Graph { public Graph(int V): // Create empty graph with v vertices public void addEdge(int v, int w): // add an edge v-w Iterable adj(int v): // vertices adjacent to v int V(): // number of vertices int E(): // number of edges ... --- # 39. Compression, Complexity, P = NP | CS61B Textbook [39.1 Models of Compressionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.1-models-of-compression) [39.2 Optimal Compression, Kolmogorov Complexitychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.2-optimal-compression-kolmogorov-complexity) [39.3 Space/Time-Bounded Compressionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.3-space-time-bounded-compression) [39.4 P = NPchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.4-p-np) [39.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.5-exercises) [Previous38.8 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/38.-compression-and-complexity/38.8-exercises) [Next39.1 Models of Compressionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.1-models-of-compression) Last updated 2 years ago sun-brightdesktopmoon sun-brightdesktopmoon --- # 14.1 Introduction | CS61B Textbook Fall 2025 People like you and I reside in our countries and live here. We can think of each country as a set and all of the people within it as elements within that set. The same person cannot live in two different countries simultaneously. What we have just modeled is a **disjoint set**. > Two sets are named _disjoint sets_ if they have no elements in common. A Disjoint-Sets (or Union-Find) data structure keeps track of a fixed number of elements partitioned into a number of _disjoint sets_. The data structure has two operations: 1. `connect(x, y)`: connect `x` and `y`. Also known as `union` 2. `isConnected(x, y)`: returns true if `x` and `y` are connected (i.e. part of the same set). Professor Hug's Explanation of an Introduction to Disjoin Sets A Disjoint Sets data structure has a fixed number of elements that each start out in their own subset. By calling `connect(x, y)` for some elements `x` and `y`, we merge subsets together. For example, say we have four elements which we'll call A, B, C, D. To start off, each element is in its own set: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-fad0d4dc0ea99730c8efefbc94b1da139f9f829b%252Fimage%2520%2823%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=b1fc8d22&sv=2) {A} {B} {C} {D} After calling `connect(A, B)`: \\ ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-657f7b7c0383c8ba0df204e4d87c49b3897fa193%252Fimage%2520%28126%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=42e284d6&sv=2) {A, B} {C} {D} Note that the subsets A and B were merged. Let's check the output some `isConnected` calls: `isConnected(A, B) -> true` `isConnected(A, C) -> false` After calling `connect(A, D)`: \\ ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-4e367b05ea3b7aebc326bf9413f93259388d5234%252Fimage%2520%2835%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=3d09aec1&sv=2) {A, B, D} {C} We find the set A is part of and merge it with the set D is part of, creating one big A, B, D set. C is left alone. `isConnected(A, D) -> true` `isConnected(A, C) -> false` \\ With this intuition in mind, let's formally define what our DisjointSets interface looks like. As a reminder, an **interface** determines _what_ behaviors a data structure should have (but not _how_ to accomplish it). In this way, any class that implements the `DisjointSets` interface knows to always include functions: `connect(int p, int q)` and `isConnected(int p, int q)` as seen below. For now, we'll only deal with sets of non-negative integers. This is not a limitation because in production we can assign integer values to anything we would like to represent. But how are we going to save data for these Disjoint sets to see which member belongs to it's corresponding set? What data structures are we going to use to represent this awesome data structure? In addition to learning about how to implement a fascinating data structure, this chapter will be a chance to see how an implementation of a data structure evolves. We will discuss four iterations of a Disjoint Sets design before being satisfied: [_Quick Find_](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.2-quick-find) _→_ [_Quick Union_](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.3-quick-union) _→_ [_Weighted Quick Union (WQU)_](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.4-weighted-quick-union-wqu) _→_ [_WQU with Path Compression_](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.5-weighted-quick-union-with-path-compression) . **We will see how design decisions greatly affect asymptotic runtime and code complexity.** [Previous14\. Disjoint Setschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets) [Next14.2 Quick Findchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.2-quick-find) Last updated 6 months ago Copy public interface DisjointSets { /** connects two items P and Q */ void connect(int p, int q); /** checks to see if two items are connected */ boolean isConnected(int p, int q); } --- # 15.1 Big Theta | CS61B Textbook Fall 2025 ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.1-big-theta#formalizing-order-of-growth) Formalizing Order of Growth Given some function Q(N)Q(N)Q(N), we can apply our last two simplifications to get the order of growth of Q(N)Q(N)Q(N). For example, if Q(N)\=3N3+N2Q(N)=3N^3+N^2Q(N)\=3N3+N2, the order of growth is N3N^3N3. From now onward, we will refer to order of growth as Θ\\ThetaΘ (pronounced "big theta"). ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.1-big-theta#order-of-growth-examples) Order of Growth Examples The following functions have these corresponding order of growths: Function Order of Growth N3+3N4N^3+3N^4N3+3N4 N4N^4N4 1/N+N31/N + N^31/N+N3 N3N^3N3 1/N+51/N + 51/N+5 111 NeN+NNe^N+NNeN+N NeNNe^NNeN 40sin(N)+4N240sin(N)+4N^240sin(N)+4N2 N2N^2N2 Instead of saying a function has _order of growth_ \_\_\_, we say that the function _belongs to_ . In other words, it belongs to the family of functions that have that same order of growth. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.1-big-theta#formal-definition) Formal Definition For some function R(N)R(N)R(N) with order of growth f(N)f(N)f(N), we write that: R(N)∈Θ(f(N))R(N) \\in \\Theta(f(N))R(N)∈Θ(f(N)) and there exists some positive constants k1k\_1k1​, k2k\_2k2​ such that... k1⋅f(N)≤R(N)≤k2⋅f(N)k\_1 \\cdot f(N) \\leq R(N) \\leq k\_2\\cdot f(N)k1​⋅f(N)≤R(N)≤k2​⋅f(N) for all values NNN greater than some N0N\_0N0​ (a very large NNN). [Previous15\. Asymptotics IIchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii) [Next15.2 Big Ochevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.2-big-o) Last updated 4 months ago * [Formalizing Order of Growth](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.1-big-theta#formalizing-order-of-growth) * [Order of Growth Examples](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.1-big-theta#order-of-growth-examples) * [Formal Definition](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.1-big-theta#formal-definition) --- # 14.2 Quick Find | CS61B Textbook Fall 2025 Professor Hug's explanation on Quick Find ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.2-quick-find#list-of-sets) List of Sets Intuitively, we might first consider representing Disjoint Sets as a list of sets, e.g, `List>`. For instance, if we have N=6 elements and nothing has been connected yet, our list of sets looks like: `[{0}, {1}, {2}, {3}, {4}, {5}, {6}]`. Looks good. However, consider how to complete an operation like `connect(5, 6)`. We'd have to iterate through up to `N` sets to find 5 and `N` sets to find 6. Our runtime becomes `O(N)`. And, if you were to try and implement this, the code would be quite complex. > The lesson to take away is that **initial design decisions determine our code complexity and runtime.** ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.2-quick-find#quick-find) Quick Find Let's consider another approach using a _single array of integers_. * The **indices of the array** represent the elements of our set. * The **value at an index** is the set number it belongs to. For example, we represent `{0, 1, 2, 4}, {3, 5}, {6}` as:\\ ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-a63bdea29ed48e44fb8be95bcb1a3bab9b53447b%252Fimage%2520%2872%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=cb73f569&sv=2) Set 4: {0, 1, 2, 4} | Set 5: {3, 5} | Set 6: {6} The array indices (0...6) are the elements. The value at `id[i]` is the set it belongs to. _The specific set number doesn't matter as long as all elements in the same set share the same id._ ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.2-quick-find#connect-x-y) `**connect(x, y)**` Let's see how the connect operation would work. Right now, `id[2] = 4` and `id[3] = 5`. After calling `connect(2, 3)`, all the elements with id 4 and 5 should have the same id. Let's assign them all the value 5 for now:\\ ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-d5b0f2eb233c9f5e9316a6bc918ff866d6c317e2%252Fimage%2520%28102%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=74135a46&sv=2) Set 5: {0, 1, 2, 3, 4, 5} | Set 6: {6} `**isConnected(x, y)**` To check `isConnected(x, y)`, we simply check if `id[x] == id[y]`. Note this is a constant time operation! We call this implementation "Quick Find" because finding if elements are connected takes constant time. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.2-quick-find#code-and-runtimes) Code & Runtimes N = number of elements in our DisjointSets data structure\\ Implementation Constructor `connect` `isConnected` ListOfSets Θ(N)[¹](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.2-quick-find#note) O(N) O(N) QuickFind Θ(N) Θ(N) Θ(1) ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.2-quick-find#note) Note 1\. We didn't discuss this but you can reason that having to create N distinct sets initially is Θ(N) [↩arrow-up-right](https://joshhug.gitbooks.io/hug61b/content/chap9/chap92.html#reffn_1) [Previous14.1 Introductionchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.1-introduction) [Next14.3 Quick Unionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.3-quick-union) Last updated 6 months ago * [List of Sets](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.2-quick-find#list-of-sets) * [Quick Find](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.2-quick-find#quick-find) * [connect(x, y)](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.2-quick-find#connect-x-y) * [Code & Runtimes](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.2-quick-find#code-and-runtimes) * [Note](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.2-quick-find#note) Copy public class QuickFindDS implements DisjointSets { private int[] id; /* Θ(N) */ public QuickFindDS(int N){ id = new int[N]; for (int i = 0; i < N; i++){ id[i] = i; } } /* need to iterate through the array => Θ(N) */ public void connect(int p, int q){ int pid = id[p]; int qid = id[q]; for (int i = 0; i < id.length; i++){ if (id[i] == pid){ id[i] = qid; } } } /* Θ(1) */ public boolean isConnected(int p, int q){ return (id[p] == id[q]); } } --- # 1.3 Basic Java Features | CS61B Textbook Fall 2025 #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/1.-introduction/1.3-basic-java-features#variables-and-loops) Variables and Loops The program below will print out the integers from 0 through 9. When we run this program, we see: Some interesting features of this program that might jump out at you: * Our variable x must be declared before it is used, _and it must be given a type!_ * Our loop definition is contained inside of curly braces, and the boolean expression that is tested is contained inside of parentheses. * Our print statement is just `System.out.print` instead of `System.out.println`. This means we should not include a newline (a return). * Our print statement adds a number to a space. This makes sure the numbers don't run into each other. Try removing the space to see what happens. * When we run it, our prompt ends up on the same line as the numbers (which you can fix in the following exercise if you'd like). Of these features the most important one is the fact that variables have a declared type. We'll come back to this in a bit, but first, an exercise. **Exercise 1.1.2.** Modify `HelloNumbers` so that it prints out the cumulative sum of the integers from 0 to 9. For example, your output should start with 0 1 3 6 10... and should end with 45. Also, if you've got an aesthetic itch, modify the program so that it prints out a new line at the end. The program below will print out the integers from 0 through 9. When we run this program, we see: Some interesting features of this program that might jump out at you: * Our variable x must be declared before it is used, _and it must be given a type!_ * Our loop definition is contained inside of curly braces, and the boolean expression that is tested is contained inside of parentheses. * Our print statement is just `System.out.print` instead of `System.out.println`. This means we should not include a newline (a return). * Our print statement adds a number to a space. This makes sure the numbers don't run into each other. Try removing the space to see what happens. * When we run it, our prompt ends up on the same line as the numbers (which you can fix in the following exercise if you'd like). Of these features the most important one is the fact that variables have a declared type. We'll come back to this in a bit, but first, an exercise. **Exercise 1.1.2.** Modify `HelloNumbers` so that it prints out the cumulative sum of the integers from 0 to 9. For example, your output should start with 0 1 3 6 10... and should end with 45. Also, if you've got an aesthetic itch, modify the program so that it prints out a new line at the end. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/1.-introduction/1.3-basic-java-features#code-style-comments-javadoc) Static Typing Java is a **statically typed language**, which means that all variables, parameters, and methods must have a declared type. After declaration, _the type can never change_. Expressions also have an implicit type; for example, the expression `3 + 5` has type `int`. Because all types are declared statically, the compiler checks that types are compatible before the program even runs. This means that expressions with an incompatible type will fail to compile instead of crashing the program at runtime. The advantages of static typing include: * catching type errors earlier in the coding process, reducing the debugging burden on the programmer. * avoiding type errors for end users. * making it easier to read and reason about code. * avoiding expensive runtime type checks, making code more efficient. However, static typing also has several disadvantages; namely: * more verbose code. * less generalizable code. One of the most important features of Java is that all variables and expressions have a so-called `static type`. Java variables can contain values of that type, and only that type. Furthermore, the type of a variable can never change. One of the key features of the Java compiler is that it performs a static type check. For example, suppose we have the program below: Compiling this program, we see: The compiler rejects this program out of hand before it even runs. This is a big deal, because it means that there's no chance that somebody running this program out in the world will ever run into a type error! This is in contrast to dynamically typed languages like Python, where users can run into type errors during execution! In addition to providing additional error checking, static types also let the programmer know exactly what sort of object he or she is working with. We'll see just how important this is in the coming weeks. This is one of my personal favorite Java features. To summarize, static typing has the following advantages: * The compiler ensures that all types are compatible, making it easier for the programmer to debug their code. * Since the code is guaranteed to be free of type errors, users of your compiled programs will never run into type errors. For example, Android apps are written in Java, and are typically distributed only as .class files, i.e. in a compiled format. As a result, such applications should never crash due to a type error since they have already been checked by the compiler. * Every variable, parameter, and function has a declared type, making it easier for a programmer to understand and reason about code. However, static typing also has several disadvantages, which will be discussed further in later chapters. To name a few: * More verbose code. * Less generalizable code. **Extra Thought Exercise** In Java, we can say `System.out.println(5 + " ");`. But in Python, we can't say `print(5 + "horse")`, like we saw above. Why is that so? Consider these two Java statements: and The first one of these will succeed; the second will give a compiler error. Since Java is strongly typed, if you tell it `h` is a string, it can concatenate the elements and give you a string. But when `h` is an `int`, it can't concatenate a number and a string and give you a number. Python doesn't constrain the type, and it can't make an assumption for what type you want. Is `x = 5 + "horse"` supposed to be a number? A string? Python doesn't know. So it errors. In this case, `System.out.println(5 + "horse");`, Java interprets the arguments as a string concatentation, and prints out "5horse" as your result. Or, more usefully, `System.out.println(5 + " ");` will print a space after your "5". What does `System.out.println(5 + "10");` print? 510, or 15? How about `System.out.println(5 + 10);`? #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/1.-introduction/1.3-basic-java-features#defining-functions-in-java) Defining Functions in Java In languages like Python, functions can be declared anywhere, even outside of functions. For example, the code below declares a function that returns the larger of two arguments, and then uses this function to compute and print the larger of the numbers 8 and 10: Since all Java code is part of a class, we must define functions so that they belong to some class. Functions that are part of a class are commonly called "methods". We will use the terms interchangably throughout the course. The equivalent Java program to the code above is as follows: The new piece of syntax here is that we declared our method using the keywords `public static`, which is a very rough analog of Python's `def` keyword. We will see alternate ways to declare methods in the next chapter. The Java code given here certainly seems much more verbose! You might think that this sort of programming language will slow you down, and indeed it will, in the short term. Think of all of this stuff as safety equipment that we don't yet understand. When we're building small programs, it all seems superfluous. However, when we get to building large programs, we'll grow to appreciate all of the added complexity. As an analogy, programming in Python can be a bit like [Dan Osman free-soloing Lover's Leaparrow-up-right](https://www.youtube.com/watch?v=NCByLWtM7y4) . It can be very fast, but dangerous. Java, by contrast is more like using ropes, helmets, etc. as in [this videoarrow-up-right](https://www.youtube.com/watch?v=tr6UIfPEuI0) . #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/1.-introduction/1.3-basic-java-features#code-style-comments-javadoc-1) Code Style, Comments, Javadoc Code can be beautiful in many ways. It can be concise. It can be clever. It can be efficient. One of the least appreciated aspects of code by novices is code style. When you program as a novice, you are often single mindedly intent on getting it to work, without regard to ever looking at it again or having to maintain it over a long period of time. In this course, we'll work hard to try to keep our code readable. Some of the most important features of good coding style are: * Consistent style (spacing, variable naming, brace style, etc) * Size (lines that are not too wide, source files that are not too long) * Descriptive naming (variables, functions, classes), e.g. variables or functions with names like `year` or `getUserName` instead of `x` or `f`. * Avoidance of repetitive code: You should almost never have two significant blocks of code that are nearly identical except for a few changes. * Comments where appropriate. Line comments in Java use the `//` delimiter. Block (a.k.a. multi-line comments) comments use `/*` and `*/`. The golden rule is this: Write your code so that it is easy for a stranger to understand. Here is the course's official [style guidearrow-up-right](https://sp19.datastructur.es/materials/guides/style-guide.html) . It's worth taking a look! Often, we are willing to incur slight performance penalties, just so that our code is simpler to [grokarrow-up-right](https://en.wikipedia.org/wiki/Grok) . We will highlight examples in later chapters. **Comments** We encourage you to write code that is self-documenting, i.e. by picking variable names and function names that make it easy to know exactly what's going on. However, this is not always enough. For example, if you are implementing a complex algorithm, you may need to add comments to describe your code. Your use of comments should be judicious. Through experience and exposure to others' code, you will get a feeling for when comments are most appropriate. One special note is that all of your methods and almost all of your classes should be described in a comment using the so-called [Javadocarrow-up-right](https://en.wikipedia.org/wiki/Javadoc) format. In a Javadoc comment, the block comment starts with an extra asterisk, e.g. `/**`, and the comment often (but not always) contains descriptive tags. We won't discuss these tags in this textbook, but see the link above for a description of how they work. As an example without tags: The widely used [javadoc toolarrow-up-right](https://www.oracle.com/java/technologies/javase/javadoc-tool.html) can be used to generate HTML descriptions of your code. [Previous1.2 Java Workflowchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/1.-introduction/1.2-java-workflow) [Next2\. Defining and Using Classeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/2.-defining-and-using-classes) Last updated 6 months ago Copy public class HelloNumbers { public static void main(String[] args) { int x = 0; while (x < 10) { System.out.print(x + " "); x = x + 1; } } } Copy $ javac HelloNumbers.java $ java HelloNumbers $ 0 1 2 3 4 5 6 7 8 9 Copy public class HelloNumbers { public static void main(String[] args) { int x = 0; while (x < 10) { System.out.print(x + " "); x = x + 1; } } } Copy $ javac HelloNumbers.java $ java HelloNumbers $ 0 1 2 3 4 5 6 7 8 9 Copy public class HelloNumbers { public static void main(String[] args) { int x = 0; while (x < 10) { System.out.print(x + " "); x = x + 1; } x = "horse"; } } Copy $ javac HelloNumbers.java HelloNumbers.java:9: error: incompatible types: String cannot be converted to int x = "horse"; ^ 1 error Copy String h = 5 + "horse"; Copy int h = 5 + "horse"; Copy def larger(x, y): if x > y: return x return y print(larger(8, 10)) Copy public class LargerDemo { public static int larger(int x, int y) { if (x > y) { return x; } return y; } public static void main(String[] args) { System.out.println(larger(8, 10)); } } Copy public class LargerDemo { /** Returns the larger of x and y. */ public static int larger(int x, int y) { if (x > y) { return x; } return y; } public static void main(String[] args) { System.out.println(larger(8, 10)); } } --- # 39.3 Space/Time-Bounded Compression | CS61B Textbook As described in the previous chapter, it is impossible to write the "perfect" compression algorithm that requires the fewest bits to output some bitstream BBB. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.3-space-time-bounded-compression#space-bounded-compression) Space-Bounded Compression --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- However, what about the problem of space-bounded compression? In this problem, we take in two inputs: a bitstream BBB and a target size SSS. The goal, then, is to find a program of length ≤S\\leq S≤S that outputs BBB. It turns out that such a problem is also uncomputable. If it were, then we could simply binary search on different values of SSS to find the optimal compression program size, which is impossible as shown in te previous section. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.3-space-time-bounded-compression#space-time-bounded-compression) Space-Time-Bounded Compression ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- What if we take our problem from above, and add a constraint that we can run at most TTT lines of bytecode? It might seem unintuitive, but this kind of problem is actually solvable. We will use the following algorithm: Copy for length L = 1....S: for each possible program P of length L: while (P is running && !(B is outputted) && lines_executed < T): run the next line of P The runtime of this algorithm is O(T∗2S)O(T \* 2^S)O(T∗2S), and in the end, it will either output some program `P` that has the correct output and is bounded by TTT and SSS, or return that no such program is possible. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.3-space-time-bounded-compression#efficient-bounded-compression) Efficient Bounded Compression ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- The runtime above is exponential in SSS. Thus, we might ask if it's possible to solve the space-time-bounded compression problem _efficiently_. As we'll see in the next chapter, this depends on our definition of efficiency. [Previous39.2 Optimal Compression, Kolmogorov Complexitychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.2-optimal-compression-kolmogorov-complexity) [Next39.4 P = NPchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.4-p-np) Last updated 2 years ago * [Space-Bounded Compression](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.3-space-time-bounded-compression#space-bounded-compression) * [Space-Time-Bounded Compression](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.3-space-time-bounded-compression#space-time-bounded-compression) * [Efficient Bounded Compression](https://cs61b-2.gitbook.io/cs61b-textbook/39.-compression-complexity-p-np/39.3-space-time-bounded-compression#efficient-bounded-compression) sun-brightdesktopmoon sun-brightdesktopmoon --- # 33.3 Stability, Adaptiveness, and Optimization | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.3-stability-adaptiveness-and-optimization#stability) Stability ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ A sort is stable if the order of equivalent elements is preserved. The following is an example of a stable sort. After sorting by section, notice how Bas, Jana, Jouni, and Rosella are in the same order as before sorting. If we want records sorted by section and then by name within each section, we can sort by name and then by section as below. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-bae771517402f3cddd4c514c784807c871163102%252FScreenshot%25202023-04-10%2520at%252012.16.47%2520AM.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=69fb1a5a&sv=2) The following example is an unstable sort. It can make things really annoying! If we want records sorted by section and then by name within each section, we can't just sort by name and then by section as before. After an unstable sort, the previous ordering is not maintained. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-5c28e480345993fb3a29c3658d5561b0e017e408%252FScreenshot%25202023-04-10%2520at%252012.28.10%2520AM.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=3a0d7c64&sv=2) Are some of the sorts we learned stable? Insertion sort is stable! Equivalent elements move past their equivalent brethren. MergeSort is stable. HeapSort is not stable. QuickSort can be stable depending on its partitioning scheme, but its stability cannot be assumed since many of its popular partitioning schemes, like Hoare, are unstable. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.3-stability-adaptiveness-and-optimization#optimizations) Optimizations -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Adaptiveness - A sort that is adaptive exploits the existing order of the array. Examples are InsertionSort, SmoothSort, and TimSort. Switch to Insertion Sort - When a subproblem reaches size 15 or lower, use insertion sort. It is very very fast for inputs of small sizes. Exploit restrictions on set of keys - For example, if the number of keys is some constant, we can use this constraint to sort faster by applying 3-way QuickSort. Switch from QuickSort - If the recursion goes too deep, switch to a different type of sort. [Previous33.2 Quick Selectchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.2-quick-select) [Next33.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.4-summary) Last updated 4 months ago * [Stability](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.3-stability-adaptiveness-and-optimization#stability) * [Optimizations](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.3-stability-adaptiveness-and-optimization#optimizations) --- # 20.1.1 A first attempt: DataIndexedIntegerSet | CS61B Textbook Fall 2025 Let us begin by considering the following approach. This approach was introduced in [Hashing Video 1arrow-up-right](https://youtu.be/rSqSlu8sEkI) . For now, we're only going to try to improve complexity from Θ(logN)\\Theta(logN)Θ(logN) to Θ(1)\\Theta(1)Θ(1). We're going to not worry about comparability. In fact, we're going to only consider storing and searching for `int`s. Here's an idea: let's create an ArrayList of type `boolean` and size 2 billion. Let everything be false by default. * The `add(int x)` method simply sets the `x` position in our ArrayList to true. This takes Θ(1)\\Theta(1)Θ(1) time. * The `contains(int x)` method simply returns whether the `x` position in our ArrayList is `true` or `false`. This also takes Θ(1)\\Theta(1)Θ(1) time! Copy public class DataIndexedIntegerSet { private boolean[] present; public DataIndexedIntegerSet() { present = new boolean[2000000000]; } public void add(int x) { present[i] = true; } public boolean contains(int x) { return present[i]; } } There we have it. That's all folks. Well, not really. What are some potential **issues** with this approach? * Extremely wasteful. If we assume that a `boolean` takes 1 byte to store, the above needs `2GB` of space per `new DataIndexedIntegerSet()`. Moreover, the user may only insert a handful of items... * What do we do if someone wants to insert a `String`? Or other data types? * Let's look at this next. Of course, we may want to insert other things, like `Dog`s. That'll come soon! [Previous20.1 Introduction to Hashing: Data Indexed Arrayschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.1-introduction-to-hashing-data-indexed-arrays) [Next20.1.2 A second attempt: DataIndexedWordSetchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.1-introduction-to-hashing-data-indexed-arrays/20.1.2-a-second-attempt-dataindexedwordset) Last updated 4 months ago --- # 20.1.3 A third attempt: DataIndexedStringSet | CS61B Textbook Fall 2025 There is a character format called **ASCII**, which has an integer per character. Here, we see that the largest value (i.e., the base/multiplier we need to use) is 126. Let's just do that. The same thing as `DataIndexedEnglishWordSet`, but just with base `126`. Copy public static int asciiToInt(String s) { int intRep = 0; for (int i = 0; i < s.length(); i += 1) { intRep = intRep * 126; intRep = intRep + s.charAt(i); } return intRep; } What about adding support for Chinese? The largest possible representation is 40959, so we need to use that as the base. So... to store a 3-character Chinese word, we need an array of size larger than **39 trillion** (with a T)!. This is getting out of hand... so let's explore what we can do to improve this, namely, using hashCode. [Previous20.1.2 A second attempt: DataIndexedWordSetchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.1-introduction-to-hashing-data-indexed-arrays/20.1.2-a-second-attempt-dataindexedwordset) [Next20.2 Hash Codechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.2-hash-code) Last updated 4 months ago --- # 20.6 Summary | CS61B Textbook Fall 2025 In this chapter, we learned about hashing, a powerful technique for turning a more complex object like a `String` into a numerically representable value like an `int`. The _hash table_ is a data structure that combines the _hash function_ with the fact that arrays can be indexed in constant time. Using the hash table and the map abstract data type, we can build a `HashMap` which allows for amortized constant time access to any key-value pair so long as we know which bucket the key falls into. However, we quickly demonstrated that this naive implementation has several drawbacks: the ability to represent all different kinds of objects, memory efficiency, and collisions. We investigated the importance of the `hashCode()` function to gain an understanding of how it affects the runtime of the hash table. To allow for smaller `size()` in the array, we used the modulo operator to shrink hash values down to a specified range of numbers. We then added external chaining to solve collisions by allowing multiple entries to live in a single bucket in the form of a LinkedList, and explored resizing functionality based on the load factor! [Previous20.5 Resizing & Hash Table Performancechevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.5-resizing-and-hash-table-performance) [Next20.7 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.7-exercises) Last updated 4 months ago --- # 13.5 Simplified Analysis Process | CS61B Textbook Fall 2025 **Summary of our (Painful) Analysis Process** * Construct a table of exact counts of all possible operations (takes lots of effort!) * Convert table into worst case order of growth using 4 simplifications. We will now propose an alternative method that avoids building a table altogether! Our simplified analysis process will consist of: * Choosing our cost model, which is the representative operation we want to count. * Figuring out the order of growth for the count of our representative operation by either: * Making an exact count and discarding unnecessary pieces or... * Using intuition/inspection to determine orders of growth. This is something that comes with practice. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.5-simplified-analysis-process#example-analysis-of-nested-for-loops-exact-counts) Example: Analysis of Nested For Loops - Exact Counts Find the order of growth of the worst case runtime of `dup1`. We will choose our cost model to be the _number of == operations_. Looking at the structure of the loops, the inner loop first gets run j=N-1 times. At the second iteration, i=1, so the inner loop runs an additional j=N-2 times. At the third iteration, i=2, so the inner loop runs an additional j=N-3 times. The total number of times the loop is run is thus: cost\=1+2+3+…+(N−2)+(N−1)\\text{cost} = 1 + 2 + 3 + \\ldots + (N-2) + (N-1)cost\=1+2+3+…+(N−2)+(N−1) This cost can be simplified to N(N−1)2\\frac{N(N-1)}{2}2N(N−1)​ ([how?arrow-up-right](https://en.wikipedia.org/wiki/1_%2B_2_%2B_3_%2B_4_%2B_%E2%8B%AF) ). We can use simplification to throw away all lower order terms and constants to get the worst case order of growth N2N^2N2. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.5-simplified-analysis-process#example-analysis-of-nested-for-loops-geometric-argument) Example: Analysis of Nested For Loops - Geometric Argument * We can see that the number of equals can be given by the area of a right triangle, which has a side length of N−1N- 1N−1. * Therefore, the order of growth of area is N2N^2N2.​​ * This is definitely not something that is immediately obvious. It takes time and practice to see these patterns! [Previous13.4 Asymptotic Behaviorchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.4-asymptotic-behavior) [Next13.6 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.6-summary) Last updated 4 months ago Copy int N = A.length; for (int i = 0; i < N; i += 1) for (int j = i + 1; j < N; j += 1) if (A[i] == A[j]) return true; return false; --- # 13.2 Runtime Characterization | CS61B Textbook Fall 2025 We want to be able to _characterize_ the runtime of the two algorithms we previously saw. In other words, we want to come up with some proxy that communicates the overall performance of each algorithm. When characterizing runtimes, we have two goals in mind: * They should be simple but mathematically rigorous. * They should clearly demonstrate the superiority of one algorithm over another if one algorithm is better than the other. We have converted both the naïve algorithm and the better algorithm into Java code. The function dup1 corresponds to the naïve algorithm and dup2 corresponds to the better algorithm. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.2-runtime-characterization#technique-1) Technique 1 The first technique we will consider using for runtime characterization is directly measuring execution time in seconds using a client program. There are a few ways we could do this: * Use a physical stopwatch (not recommended). * Use Unix's built in `time` command. * Use the Princeton Standard library which has a `stopwatch` class. Using any of these methods will show (with varying levels of accuracy, depending on whether the physical stopwatch path was chosen) that as input size increases, `dup1` takes a longer time to complete, whereas `dup2` completes at relatively around the same rate. It seems like technique 1 works perfectly fine for characterizing our runtimes! It is very easy to understand the results of the experiment, and it is easy to implement. However, there are some serious cons associated it that dissuades us from using it for everything: * It could take a _long_ time to finish running. * Running times can vary by machine, compiler, input data, etc. For these reasons, technique 1 does not meet our goals in characterizing runtimes. It's simple, but it's not mathematically rigorous. Moreover, the differences based on machine, compiler, input, etc. mean that the results may not clearly demonstrate the relationship between `dup1` and `dup2`. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.2-runtime-characterization#technique-2a) Technique 2A Rather than physically timing the amount of time it takes for the algorithm to run, we can instead count the total number of operations completed by each algorithm! Intuitively, the algorithm that runs the fewer number of operations would be superior to the other. We can fix the input size `N` to be the same for both algorithms and compare the number of operations run. Let us apply this to the `dup1` algorithm with an input size of `N=10000`: Operation Count (for N=10000) i = 0 1 j = i+1 1 (in the best case) to 10000 (in the worst case) < 2 to 50,015,001 += 1 0 to 50,005,000 \== 1 to 49,995,000 array accesses 2 to 99,990,000 The operation `i = 0` is only run a single time at the very start of the function call. `j = i+1` is more complicated--in the best case when `A[0] == A[1]`, it only runs a single time (convince yourself of this fact!). In the worst case, `j` is initialized once for each value that `i` takes on in the outer loop, so it is initialized a total of 10000 times. As we dive further into the loop, it gets progressively less feasible to calculate the exact counts. We will show later on that the exact numbers do not matter that much. To summarize technique 2A, we have solved the issue of _machine independence_. Differences in machines do not affect the total of operations run (usually). However, it is tedious to compute all the counts for each operation. In addition, our chosen input size is arbitrary, and we do not know how much time it actually takes to run. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.2-runtime-characterization#technique-2b) Technique 2B We can resolve the issue of choosing input size by calculating the _symbolic count_ instead, which means that we calculate our counts in terms of the input `N`. Using this technique, we can update our table to include the symbolic counts: Operation Symbolic Count Count (for N=10000) i = 0 1 1 j = i+1 1 to N 1 (in the best case) to 10000 (in the worst case) < 2 to (N² + 3N + 2)/2 2 to 50,015,001 += 1 0 to (N² + N)/2 0 to 50,005,000 \== 1 to (N² - N)/2 1 to 49,995,000 array accesses 2 to N²-N 2 to 99,990,000 Using symbolic counts allows us to see how the algorithms _scales_ with input size. However, it is now even more tedious to calculate! It also still does not tell the actual time. [Previous13.1 An Introduction to Asymptotic Analysischevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.1-an-introduction-to-asymptotic-analysis) [Next13.3 Checkpoint: An Exercisechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.3-checkpoint-an-exercise) Last updated 6 months ago * [Technique 1](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.2-runtime-characterization#technique-1) * [Technique 2A](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.2-runtime-characterization#technique-2a) * [Technique 2B](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.2-runtime-characterization#technique-2b) Copy // Naïve algorithm: compare everything public static boolean dup1(int[] A) { for (int i = 0; i < A.length; i += 1) { for (int j = i + 1; j < A.length; j += 1) { if (A[i] == A[j]) { return true; } } } return false; } Copy // Better algorithm: compare only neighbors public static boolean dup2(int[] A) { for (int i = 0; i < A.length - 1; i += 1) { if (A[i] == A[i + 1]) { return true; } } return false; } Copy for (int i = 0; i < A.length; i += 1) { for (int j = i+1; j < A.length; j += 1) { if (A[i] == A[j]) { return true; } } } return false; --- # 19.4 Runtime Analysis | CS61B Textbook Fall 2025 Because a left-leaning red-black tree has a 1-1 correspondence with a 2-3 tree and will always remain within 2x the height of its 2-3 tree, the runtimes of the operations will take log(N)log(N)log(N)time. Here's the abstracted code for insertion into a LLRB: Copy private Node put(Node h, Key key, Value val) { if (h == null) { return new Node(key, val, RED); } int cmp = key.compareTo(h.key); if (cmp < 0) { h.left = put(h.left, key, val); } else if (cmp > 0) { h.right = put(h.right, key, val); } else { h.val = val; } if (isRed(h.right) && !isRed(h.left)) { h = rotateLeft(h); } if (isRed(h.left) && isRed(h.left.left)) { h = rotateRight(h); } if (isRed(h.left) && isRed(h.right)) { flipColors(h); } return h; } [Previous19.3 Inserting LLRB Treeschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.3-inserting-llrb-trees) [Next19.5 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.5-summary) Last updated 4 months ago --- # 19.6 Exercises | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.6-exercises#factual) Factual ---------------------------------------------------------------------------------------------------------------------- 1. Consider the tree below. If we call `rotateLeft(C)`, which operation reverts the tree back to its original form? ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-e2c6abd0e649db92f31fbec7ad507ca8c22f0276%252Fimage%2520%28123%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=56aef2df&sv=2) 1. When you rotate a nodes in a tree, which of the following can happen? * If the tree was previously a valid search tree, it can become invalid. * The height can stay the same. * The height can increase. * The height can decrease. * The number of nodes can change. * The root of the tree can change. chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.6-exercises#problem-1) `rotateRight(D)`. The inverse of any `rotateLeft` operation is a `rotateRight` on the node's left child. chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.6-exercises#problem-2) * **If the tree was previously a valid search tree, it can become invalid.** Rotation always preserves search tree properties. * check **The height can stay the same.** If the rotation does not affect any leaf nodes on the bottommost level, the height will stay the same. * check **The height can increase.** If the rotation affects a leaf on the bottommost level, the height can increase. * check **The height can decrease.** If the rotation affects a leaf on the bottommost level, the height can increase. * **The number of nodes can change.** Rotation only changes the structure of the tree, not the nodes. * check **The root of the tree can change.** For example, rotating the root. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.6-exercises#procedural) Procedural ---------------------------------------------------------------------------------------------------------------------------- 1. Consider the following LLRB. What is the height of the corresponding 2-3 tree and how many 3-nodes does it have? ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-c77380b25c8636a7125ae487254c0fec93e1f9b2%252Fimage%2520%2899%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=9a0b1d98&sv=2) 1. Suppose we insert `15` in the LLRB above. What is the first operation that must be applied to maintain the LLRB invariants? 2. Suppose in the process of insertion, we end up with the following temporary 4-node. What is the corresponding LLRB representation of this node? ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-5614f135f4e07ec478dd75a3b93e711cb8c0ed43%252Fimage%2520%2873%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=d6c21e0d&sv=2) chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.6-exercises#problem-1-1) ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-ea18eaa474344296b2e0d46e8b8b9da99a2f2739%252FCheck-in%252018%2520Q3%2520Answer%2520Img.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=aa4988f&sv=2) The corresponding 2-3 tree has height 1 and has two 3-nodes (`17 25` and `39 43`). chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.6-exercises#problem-2-1) `15` is inserted to the right of `13`. Since we cannot have a right-leaning red link, we must `rotateLeft(13)`. chevron-rightProblem 3[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.6-exercises#problem-3) ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-162cf833c42c2f1ac026855f4a44f4ff28e03a5d%252Fimage%2520%28118%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=cd4ae9&sv=2) [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.6-exercises#metacognitive) Metacognitive ---------------------------------------------------------------------------------------------------------------------------------- 1. Give a range of values, when inserted into the LLRB below, results in a `rotateRight` operation as the first balancing operation. Assume that values are distinct, but not necessarily integers. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-c77380b25c8636a7125ae487254c0fec93e1f9b2%252Fimage%2520%2899%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=9a0b1d98&sv=2) chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.6-exercises#problem-1-2) `rotateRight` occurs when we have two red links in a row. This occurs when we insert to the left of `39`. This value must be larger than `25` (since it is in its right branch) but less than `39` (since it is the left child of 39). So our final range is (25,39)(25, 39)(25,39). [Previous19.5 Summarychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.5-summary) [Next20\. Hashing Ichevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i) Last updated 4 months ago * [Factual](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.6-exercises#factual) * [Procedural](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.6-exercises#procedural) * [Metacognitive](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.6-exercises#metacognitive) --- # 14.3 Quick Union | CS61B Textbook Fall 2025 Professor Hug's explanation on Quick Union Suppose we prioritize making the `connect` operation fast. We will still represent our sets with an array. Instead of an id, we assign each item the index of its parent. If an item has no parent, then it is a 'root' and we assign it a negative value. This approach allows us to imagine each of our sets as a tree. For example, we represent `{0, 1, 2, 4}, {3, 5}, {6}` as: Note that we represent the sets using **only an array**. We visualize it ourselves as trees. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap9%2F9.3.1.png&width=768&dpr=3&quality=100&sign=8aa7d9ba&sv=2) For QuickUnion we define a helper function `find(int item)` which returns the root of the tree `item` is in. For example, for the sets above, `find(4) == 0`, `find(1) == 0`, `find(5) == 3`, etc. Each element has a unique root. `**connect(x, y)**` To connect two items, we find the set that each item belongs to (the roots of their respective trees), and make one the child of the other. Example: `connect(5, 2)`: 1. `find(5)` -> 3 2. `find(2)` -> 0 3. Set `find(5)`'s value to `find(2)` aka `parent[3] = 0` Note how element 3 now points to element 0, combining the two trees/sets into one. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap9%2F9.3.2.png&width=768&dpr=3&quality=100&sign=412d5b87&sv=2) In the best case, if `x` and `y` are both roots of their trees, then `connect(x, y)` just makes `x` point to `y`, a Θ(1) operation! (Hence the name QuickUnion) `**isConnected(x, y)**` If two elements are part of the same set, then they will be in the same tree. Thus, they will have the same root. So for `isConnected(x, y)` we simply check if `find(x) == find(y)`. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.3-quick-union#performance) Performance There is a potential performance issue with QuickUnion: the tree can become very long. In this case, finding the root of an item (`find(item)`) becomes very expensive. Consider the tree below: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fchap9%2F9.3.3.png&width=768&dpr=3&quality=100&sign=867b6f90&sv=2) In the worst case, we have to traverse all the items to get to the root, which is a Θ(N) runtime. Since we have to call `find` for both `connect` and `isConnected`, the runtime for both is upper bounded by O(N). ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.3-quick-union#summary-and-code) Summary and Code N = number of elements in our DisjointSets data structure Implementation Constructor `connect` `isConnected` QuickUnion Θ(N) O(N) O(N) QuickFind Θ(N) Θ(N) Θ(1) QuickUnion Θ(N) O(N) O(N) From the runtime chart, QuickUnion seems worse than QuickFind! Note however that O(N) as an **upper bound**. When our trees are balanced, both `connect` and `isConnected` perform reasonably well. In the next section we'll see how to _guarantee_ they perform well. [Previous14.2 Quick Findchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.2-quick-find) [Next14.4 Weighted Quick Union (WQU)chevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.4-weighted-quick-union-wqu) Last updated 6 months ago * [Performance](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.3-quick-union#performance) * [Summary and Code](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets/14.3-quick-union#summary-and-code) Copy public class QuickUnionDS implements DisjointSets { private int[] parent; public QuickUnionDS(int num) { parent = new int[num]; for (int i = 0; i < num; i++) { parent[i] = -1; } } private int find(int p) { while (parent[p] >= 0) { p = parent[p]; } return p; } @Override public void connect(int p, int q) { int i = find(p); int j= find(q); parent[i] = j; } @Override public boolean isConnected(int p, int q) { return find(p) == find(q); } } --- # 19.1 Rotating Trees | CS61B Textbook Fall 2025 Wonderfully balanced as they are, B-Trees are really difficult to implement. We need to keep track of the different nodes and the splitting process is pretty complicated. As computer scientists who appreciate clean code and a good challenge, let's find another way to create a balanced tree. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.1-rotating-trees#multiple-structures-of-bst) Multiple Structures of BST ----------------------------------------------------------------------------------------------------------------------------------------------------------------- For any BST, there are multiple ways to structure it so that you maintain the BST invariants. Earlier, we talked about how **inserting** elements in different orders will result in a different BST. The sequence in which one inserts into a BST will affect its structure. The BSTs below all consist of the elements 1, 2, and 3, yet all have different structures. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-97e2d7a6ed7696ee3300e54df328fa958eab4e85%252FScreen%2520Shot%25202023-02-27%2520at%25207.32.00%2520PM.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=2067dbb1&sv=2) chevron-rightExercise: For each tree shown above, provide an order of insertion that yields the structure.[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.1-rotating-trees#exercise-for-each-tree-shown-above-provide-an-order-of-insertion-that-yields-the-structure) 1. insert(1), insert(2), insert(3) 2. insert(1), insert(3), insert(2) 3. insert(2), insert(1), insert(3) or insert(2), insert(3), insert(1) 4. insert(3), insert(1), insert(2) 5. insert(3), insert(2), insert(1) [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.1-rotating-trees#tree-rotation) Tree Rotation --------------------------------------------------------------------------------------------------------------------------------------- The formal definition of rotation is: We will slowly demystify this process in the next few paragraphs. Below is a graphical description of what happens in a left rotation on the node G: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-24077d8c5e7f33bfce00dfed896c2bcb751ea550%252FScreen%2520Shot%25202023-02-27%2520at%25207.38.06%2520PM.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=941ce17a&sv=2) rotateLeft(G) The written description of what happened above is this: * G's right child, P, merges with G, bringing its children along. * P then passes its left child to G and G goes down to the left to become P's left child. You can see that the structure of the tree changes as well as its height. We can also rotate on a non-root node. We just disconnect the node from the parent temporarily, rotate the subtree at the node, then reconnect the new root. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.1-rotating-trees#implementation) Implementation Here are the implementations of `rotateRight` and `rotateLeft:` You may be wondering how does the parent node know about which node to point to after we rotate? That's why we are returning x, and other parts of our code will make use of this information to correctly update the parent node's pointer. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.1-rotating-trees#balancing-bsts-with-tree-rotation) Balancing BSTs with Tree Rotation ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- With rotations, we can actually balance a tree. Consider the example below: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-a23fab5bb4095f542ee4a34a309d28b3a2260cb0%252Fimage%2520%2890%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=fa399483&sv=2) We can do balance the given BST on the left by calling: 1. `rotateRight(3)` 2. `rotateLeft(1)` The main observation to make for tree rotation is that it is possible to **shorten** or **lengthen** a tree, while maintaining the search tree's property. For more examples, see the demo in [these slidesarrow-up-right](https://docs.google.com/presentation/d/1pfkQENfIBwiThGGFVO5xvlVp7XAUONI2BwBqYxib0A4/edit#slide=id.g465b5392c_00) . [Previous19\. Red Black Treeschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees) [Next19.2 Creating LLRB Treeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.2-creating-llrb-trees) Last updated 4 months ago * [Multiple Structures of BST](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.1-rotating-trees#multiple-structures-of-bst) * [Tree Rotation](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.1-rotating-trees#tree-rotation) * [Implementation](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.1-rotating-trees#implementation) * [Balancing BSTs with Tree Rotation](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/19.-red-black-trees/19.1-rotating-trees#balancing-bsts-with-tree-rotation) Copy rotateLeft(G): Let x be the right child of G. Make G the new left child of x. Copy rotateRight(G): Let x be the left child of G. Make G the new right child of x. Copy private Node rotateRight(Node h) { // assert (h != null) && isRed(h.left); Node x = h.left; h.left = x.right; x.right = h; return x; } // make a right-leaning link lean to the left private Node rotateLeft(Node h) { // assert (h != null) && isRed(h.right); Node x = h.right; h.right = x.left; x.left = h; return x; } --- # 26.4 Chapter Summary | CS61B Textbook Fall 2025 In this chapter, we learned about Minimum Spanning Trees and the Cut Property: * **MST:** the lightest set of edges in a graph possible such that all the vertices are connected and acyclic. * **The Cut Property**: given any cut, the minimum weight crossing edge is in the MST. * _Cut_: an assignment of a graph’s nodes to two non-empty sets * _Crossing Edge:_ an edge which connects a node from one set to a node from the other set. We also learned about how to find MSTs of a graph with two algorithms: * **Prim's Algorithm**: Construct MST through a mechanism similar to Dijkstra's Algorithm, with the only difference of inserting vertices into the fringe not based on distance to goal vertex but distance to the MST under construction. * _Runtime_: O((∣V∣+∣E∣)log(∣V∣))O((|V| + |E| )log(|V|))O((∣V∣+∣E∣)log(∣V∣)) * **Kruskal's Algorithm**: Construct MST by first sorting edges from lightest to heaviest, then add edges sequentially if no cycles are formed until there are V - 1 edges. * Runtime: * O(∣E∣log∣E∣)O(|E| log |E|)O(∣E∣log∣E∣) (unsorted edges) * O(∣E∣log∗∣V∣)O(|E| log\* |V|)O(∣E∣log∗∣V∣) (sorted edges) [Previous26.3 Kruskal's Algorithmchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.3-kruskals-algorithm) [Next26.5 MST Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.5-mst-exercises) Last updated 4 months ago --- # 36.1 Counting Sort | CS61B Textbook Fall 2025 Imagine if instead of driving a slow Honda Civic, we started driving a fast Ferrari. Unfortunately, we won't actually be driving in a Ferrari today, but we will witness a blazing fast algorithm that's just as fast called Radix Sorts. When sorting an array, sorting requires Ω(Nlog⁡N)\\Omega(N \\log N)Ω(NlogN)compare operations in the worst case (array is sorted in descending order). Thus, the ultimate comparison based sorting algorithm has a worst case runtime of Θ(Nlog⁡N)\\Theta(N \\log N)Θ(NlogN). From an asymptotic perspective, that means no matter how clever we are, we can never beat Merge Sort’s worst case runtime of Θ(Nlog⁡N)\\Theta(N \\log N)Θ(NlogN). But what if we don't compare at all? ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-1563217215c989a59a79554e822ea8648a2fdf62%252FScreen%2520Shot%25202023-04-15%2520at%25202.26.42%2520AM.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=dbdd4c9f&sv=2) Left is original, right is ordered output Essentially what just happened is that we first made a new array of the same size and then just copied all of the # indexes to the correct location. So first we look at 5 Sandra Vanilla Grimes and then copy this over to the 5th index in our new array. This does guarantee Θ(N)\\Theta(N)Θ(N) worst case time. However what if we were working with * Non-unique keys. * Non-consecutive keys. * Non-numerical keys. All of these cases are complex cases that aren't so simple to deal with. Essentially what we can do is create a simpler method which is to: * Count number of occurrences of each item. * Iterate through list, using count array to decide where to put everything. Bottom line, we can use counting sort to sort NNN objects in Θ(N)\\Theta(N)Θ(N) time. [Previous36\. Radix Sortschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/36.-radix-sorts) [Next36.2 LSD Radix Sortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/36.-radix-sorts/36.2-lsd-radix-sort) Last updated 4 months ago --- # 34.4 Summary | CS61B Textbook Fall 2025 In this fun lecture, we steered a bit away from core CS61B exam material and discussed a high-level overview of the impacts that students can have on society and on their own lives using the skills obtained in CS 61B and beyond. We covered specific societal impacts in the realm of economics within the context of industry, and social impacts with how humans in our global civilization interact with one another using the digital products that we can create using our coding skills. Most importantly, we delved into the potential _harms_ to society that our coding skills could have – from the psychological foundations that could be altered on our youth to the reconfiguration of politics and governmental leaders are elected. With great power comes great responsibility, and it is evermore vital that we consider the deep, all-encompassing question of ethics in creating software for humanity. With the demand for software engineers and the financial impact that creating software solutions to the worldwide marketplace has, the value added to you as a worker in our society is immense & very fruitful. However, it is important to consider factors beyond just work to create a multifaceted thing called your life. We only work so many hours in the weeks in our lives– so it is incredibly important to ensure that work (and learning CS to become a software engineer, etc.) is just a subpart of what we consider our entire life. Have fun on the journey! [Previous34.3 Your Lifechevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/34.-software-engineering-iii/34.3-your-life) [Next34.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/34.-software-engineering-iii/34.5-exercises) Last updated 4 months ago --- # 28. Software Engineering I | CS61B Textbook Fall 2025 Prior to the Fall 2023 semester, Professor Hug had a lecture about software code complexity. For the Fall 2023 semester, this lecture was adjusted to have a similar form of content with different examples and real-world experiences that I (Aniruth) have been through on my gap year from Berkeley. The content in this GitBook has been adjusted to the newest information. Both video recordings are linked below. Full Lecture on Software Engineering I by Aniruth Narayanan, Fall 2023 Lecture Slides for Software Engineering I, Fall 2023 Full Lecture on Software Engineering I by Professor Hug, Fall 2022 [Previous27.5 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.5-exercises) [Next28.1 Introduction to Software Engineeringchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.1-introduction-to-software-engineering) Last updated 4 months ago --- # 26.2 Prim's Algorithm | CS61B Textbook Fall 2025 One way to find the MST of a graph is the Prim's Algorithm. In this section, we will discuss both the conceptual and concrete implementation of this algorithm, as well as its runtime analysis. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.2-prims-algorithm#conceptual-visualization) Conceptual Visualization --------------------------------------------------------------------------------------------------------------------------------------------------------------------- Prim's Algorithm is one way to find a MST from a graph. It is as follows: 1. Start from some arbitrary node. 2. Repeatedly add the shortest edge that has **one node inside the MST in construction.** 3. Repeat until there are V - 1 edges. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.2-prims-algorithm#why-does-it-work) Why does it work? Prim's algorithm works because at all stages of the algorithm, we can reason as follows: * Consider dividing all the nodes in the graph into two sets: * Set 1: nodes that are part of the existing MST that's under construction * Set 2: all other nodes * According to the algorithm, we always add the **lightest, or minimally weighted, edge** that crosses this cut. * By **the Cut Property**, the added edge is necessarily part of the final MST. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.2-prims-algorithm#implementation) Implementation ------------------------------------------------------------------------------------------------------------------------------------------------- Essentially, this algorithm runs via the same mechanism as [Dijkstra's algorithmarrow-up-right](https://github.com/Berkeley-CS61B/fa25-gitbook/blob/main/24.-shortest-paths/24.2-dijkstras-algorithm.md) . The only difference is that while Dijkstra's considers candidate nodes by their distance from the source node, Prim's looks at each candidate node's **distance from the MST under construction.** [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.2-prims-algorithm#runtime-analysis) Runtime Analysis ----------------------------------------------------------------------------------------------------------------------------------------------------- Because this algorithm runs through the same mechanism as Dijkstra's algorithm, its runtime is also identical to Dijkstra's: O((∣V∣+∣E∣)log(∣V∣))O((|V| + |E| )log(|V|))O((∣V∣+∣E∣)log(∣V∣)) Remember, this is because we need to add to a priority queue fringe once for every edge we have, and we need to dequeue from it once for every vertex we have. [Previous26.1 MSTs and Cut Propertychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.1-msts-and-cut-property) [Next26.3 Kruskal's Algorithmchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.3-kruskals-algorithm) Last updated 4 months ago * [Conceptual Visualization](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.2-prims-algorithm#conceptual-visualization) * [Why does it work?](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.2-prims-algorithm#why-does-it-work) * [Implementation](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.2-prims-algorithm#implementation) * [Runtime Analysis](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.2-prims-algorithm#runtime-analysis) --- # 28.3 Strategic vs Tactical Programming | CS61B Textbook Fall 2025 ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.3-strategic-vs-tactical-programming#tactical-programming) Tactical Programming The goal is to get something working quickly, often using workarounds. Consider code that has many if statements to handle many separate cases to pass autograder tests that is challenging to update and explain. Prototypes, proof-of-concepts often leverage tactical programming, to show that something could theoretically work. However: * There’s no time spent on overall design * Code is complicated * Refactoring takes time and potentially means restarting * If you didn't plan for Project 2 runtime requirements, you would have to redo the constructor and the entire project * Proof of concepts are sometimes deployed in the real world due to lack of time ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.3-strategic-vs-tactical-programming#strategic-programming) Strategic Programming The goal is to write code that works elegantly - at the cost of planning time, to reduce coding time. This emphasizes long term strategy. Code should be: * Maintainable to fix bugs * Simple to understand * Future-proof to add new functionality * 61B projects have deadlines; afterwards, you can throw it away If the strategy is insufficient, go back to the drawing board before continuing work. Helper method strategy is key to leverage throughout projects, especially when we have written comprehensive tests to ensure that these methods are correct. [Previous28.2 Complexitychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.2-complexity) [Next28.4 Real World Exampleschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.4-real-world-examples) Last updated 4 months ago * [Tactical Programming](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.3-strategic-vs-tactical-programming#tactical-programming) * [Strategic Programming](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.3-strategic-vs-tactical-programming#strategic-programming) --- # 20.1 Introduction to Hashing: Data Indexed Arrays | CS61B Textbook Fall 2025 So far in the course, we've taken a look at many ways to store things in data structures, but they're not always the most efficient in terms of runtime. Enter an incredible data structure that can provide insertion, removal, and contains checks, regardless of how many elements are inside of it (even if theres millions of items)– all in O(1) runtime in the best case! Sound too good to be true? We will explore the magic of hashing in this chapter to see how this is possible. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.1-introduction-to-hashing-data-indexed-arrays#quick-recap-of-data-structures-weve-seen-so-far) Quick Recap of Data Structures we've seen so far We've looked at a few data structures that efficiently search for the existence of items within the data structure. We looked at Binary Search Trees, then made them balanced using 2-3 Trees. However, there are some limitations that these structures impose (yes, even 2-3 trees!) 1. They require that items be comparable. How do you decide where a new item goes in a BST? You have to answer the question "are you smaller than or bigger than the root"? For some objects, this question may make no sense. 2. They give a complexity of Θ(logN)\\Theta(logN)Θ(logN). Is this good? Absolutely. But maybe we can do better. Professor Hug's Lecture on Hash Tables. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.1-introduction-to-hashing-data-indexed-arrays#using-data-as-indices) Using Data as Indices Arrays have amazing runtime for its basic operations. Is there a good way to convert data into indices and store them in an array? In the next couple of sub-sections, we will walk through the steps that will eventually lead us to our final creation -- the hash table. Watching the videos linked in the subsections is helpful for understanding the process through which we arrived at the invention of hash tables. Hash tables will be the main focus of the rest of the chapter. [Previous20\. Hashing Ichevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i) [Next20.1.1 A first attempt: DataIndexedIntegerSetchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.1-introduction-to-hashing-data-indexed-arrays/20.1.1-a-first-attempt-dataindexedintegerset) Last updated 4 months ago * [Quick Recap of Data Structures we've seen so far](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.1-introduction-to-hashing-data-indexed-arrays#quick-recap-of-data-structures-weve-seen-so-far) * [Using Data as Indices](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.1-introduction-to-hashing-data-indexed-arrays#using-data-as-indices) --- # 13.7 Exercises | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.7-exercises#factual) Factual -------------------------------------------------------------------------------------------------------------------- 1. Analyze the runtime of the following code in terms of `N`: Copy for (int i = 0; i < N; i++) { int j = 0; while (j < N) { j = N; } } 1. Let f(N)\=2Nf(N) = 2Nf(N)\=2N. Which of the following statements is true? * f(N)∈Θ(1)f(N) \\in \\Theta(1)f(N)∈Θ(1) * f(N)∈Θ(N)f(N) \\in \\Theta(N)f(N)∈Θ(N) * f(N)∈Θ(N2)f(N) \\in \\Theta(N^2)f(N)∈Θ(N2) * f(N)∈O(1)f(N) \\in O(1)f(N)∈O(1) * f(N)∈O(N)f(N) \\in O(N)f(N)∈O(N) * f(N)∈O(N2)f(N) \\in O(N^2)f(N)∈O(N2) chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.7-exercises#problem-1) Note that the inner loop only runs once, since it immediately sets `j = N` in the first iteration. As such, the runtime is just the runtime of the outer loop, which iterates `N` times. The overall runtime, then, is linear. chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.7-exercises#problem-2) Remember that Θ\\ThetaΘ means the same order of growth (linear), while OOO can be roughly thought of as "less than or equal to" some order of growth. * f(N)∈Θ(1)f(N) \\in \\Theta(1)f(N)∈Θ(1) * check f(N)∈Θ(N)f(N) \\in \\Theta(N)f(N)∈Θ(N) * f(N)∈Θ(N2)f(N) \\in \\Theta(N^2)f(N)∈Θ(N2) * f(N)∈O(1)f(N) \\in O(1)f(N)∈O(1) * check f(N)∈O(N)f(N) \\in O(N)f(N)∈O(N) * check f(N)∈O(N2)f(N) \\in O(N^2)f(N)∈O(N2) [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.7-exercises#procedural) Procedural -------------------------------------------------------------------------------------------------------------------------- 1. **True or false.** Suppose we have a function fff, and we are told f(N)∈Θ(N2)f(N) \\in \\Theta(N^2)f(N)∈Θ(N2). If we run fffon an input of size NNN, then an input of size 2N2N2N, it will take roughly 4 times as long. 2. **True or false.** Suppose we have a function fff, and we are told f(N)∈Θ(N2)f(N) \\in \\Theta(N^2)f(N)∈Θ(N2). If we run fffon an input of size 100, then an input of size 200, it will take roughly 4 times as long. chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.7-exercises#problem-1-1) **True**. This is the definition of asymptotics. chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.7-exercises#problem-2-1) **False**. 100 may be too small of an input for asymptotic behavior to start displaying. Remember that asymptotics only apply to very large inputs! [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.7-exercises#metacognitive) Metacognitive -------------------------------------------------------------------------------------------------------------------------------- 1. Why do use asymptotics instead of empirical timing (for example, like the `Stopwatch` class from Lab 3)? chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.7-exercises#problem-1-2) There are several advantages to using asymptotics over empirical timing. See if you can come up with more beyond the list below! * Different computers run at different speeds. Depending on architecture, hardware components, even room temperature, the same code can execute with vastly different empirical times. * It may not be feasible to test code on extremely large inputs. * Asymptotics are language-agnostic. The same algorithm may have different empirical runtime depending on which language it's written in (for example, C is usually around 10-400x faster than Python), but will have the same asymptotic runtime. * The worst-case runtime may only occur in certain cases that are hard to measure empirically. [Previous13.6 Summarychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.6-summary) [Next14\. Disjoint Setschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/14.-disjoint-sets) Last updated 4 months ago * [Factual](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.7-exercises#factual) * [Procedural](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.7-exercises#procedural) * [Metacognitive](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.7-exercises#metacognitive) --- # 27.5 Exercises | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.5-exercises#factual) Factual ---------------------------------------------------------------------------------------------------------------------------------- 1. Suppose we use the following trie to represent a map. What would `get("sea")` return? What about `get("sell")`? ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-ab77129352e2083f2fa365a20a68052713457618%252Fimage%2520%28147%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=1cbea460&sv=2) 1. Consider the Trie-based set below. What does `keysWithPrefix("sp")` return? What nodes does it visit during this call? ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-5366f896e9fbc3858d7dcca391190a7389c334a9%252Fimage%2520%2818%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=d916dc92&sv=2) 1. What is the worst-case runtime when searching for a single word in a trie? Let RRR be the size of the alphabet, and NNN be the number of items in the trie, and LLL be the length of the word being operated on. chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.5-exercises#problem-1) `sea` terminates at the node with value 6. `sell` does not exist in the trie (since it does not terminate at the node with `l`, so the `get` operation returns null. chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.5-exercises#problem-2) `keysWithPrefix` follows the prefix to the final letter of the prefix, then performs DFS from that node to get all children. During this procedure, it traverses the nodes `s, p, i, t, e, y`. The final return value is `spit, spite, spy`. chevron-rightProblem 3[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.5-exercises#problem-3) Θ(L)\\Theta(L)Θ(L). In the worst case, the word is a prefix of some other word in the trie, but is not present in the trie itself. In this case, we go through all the letters of the word. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.5-exercises#metacognitive) Metacognitive ---------------------------------------------------------------------------------------------------------------------------------------------- 1. Compare the worst-case number of character comparisons required to insert a word into an LLRB, hash table, and R-way trie. Let RRR be the size of the alphabet, and NNN be the number of items in the trie, and LLL be the maximum length of any word. chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.5-exercises#problem-1-1) **LLRB**: We always insert at the bottom of the LLRB, so there are Θ(log⁡N)\\Theta(\\log N)Θ(logN)comparisons to figure out where the new node goes. Each word comparison takes up to LLL character comparisons. Thus, there are Θ(Llog⁡N)\\Theta(L \\log N)Θ(LlogN) comparisons. **Hash table**: In the worst case, all items hash to the same bucket. On an insertion, we must compare a word to all other words in the bucket for equality. Assuming this bucket has NNN items, this takes Θ(LN)\\Theta(LN)Θ(LN) comparisons. **R-way trie**: In the worst case, we follow or create LLL nodes to the end of the word. Thus, there are at most Θ(L)\\Theta(L)Θ(L) comparisons. [Previous27.4 Summarychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.4-summary) [Next28\. Software Engineering Ichevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i) Last updated 4 months ago * [Factual](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.5-exercises#factual) * [Metacognitive](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.5-exercises#metacognitive) --- # 39. Compression and Complexity | CS61B Textbook Fall 2025 [39.1 Introduction to Compressionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/39.-compression-and-complexity/39.1-introduction-to-compression) [39.2 Prefix-free Codeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/39.-compression-and-complexity/39.2-prefix-free-codes) [39.3 Shannon-Fano Codeschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/39.-compression-and-complexity/39.3-shannon-fano-codes) [39.4 Huffman Coding Conceptualschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/39.-compression-and-complexity/39.4-huffman-coding-conceptuals) [39.5 Compression Theorychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/39.-compression-and-complexity/39.5-compression-theory) [39.6 LZW Compressionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/39.-compression-and-complexity/39.6-lzw-compression) [39.7 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/39.-compression-and-complexity/39.7-summary) [39.8 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/39.-compression-and-complexity/39.8-exercises) [Previous38.1 The end is nearchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/38.-software-engineering-iv/38.1-the-end-is-near) [Next39.1 Introduction to Compressionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/39.-compression-and-complexity/39.1-introduction-to-compression) Last updated 4 months ago --- # 18.5 B-Tree Performance | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.5-b-tree-performance#b-tree-runtime-analysis) B-Tree Runtime Analysis ------------------------------------------------------------------------------------------------------------------------------------------------------- To consider the runtime of B-Trees, let LLL be the maximum items per node. Based on our invariants, the maximum height must be somewhere between log⁡L+1N\\log\_{L + 1} NlogL+1​N (best case, when all nodes have LLL items) and log⁡2N\\log\_2 Nlog2​N (worst case, when each node has 1 item). The overall height, then, is always on the order of Θ(log⁡N)\\Theta(\\log N)Θ(logN) ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-198297e9bde038def4a09d7720b946a70bf7cdf6%252Fimage%2520%2845%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=519d5c80&sv=2) Worst-case B-Tree height ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-184b80167d5870976f988d29ab6609ca021c2596%252Fimage%2520%2826%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=776f91f0&sv=2) Best-case B-Tree height ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.5-b-tree-performance#runtime-for-contains) Runtime for `contains` In the worst case, we have to examine up to LLL items per node. We know that height is logarithmic, so the runtime of `contains` is bounded by O(Llog⁡N)O(L \\log N)O(LlogN). Since LLL is a constant, we can drop the multiplicative factor, resulting in a runtime of O(log⁡N)O(\\log N)O(logN). ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.5-b-tree-performance#runtime-for-add) Runtime for `add` A similar analysis can be done for `add`, except we have to consider the case in which we must split a leaf node. Since the height of the tree is O(log⁡N)O(\\log N)O(logN), at worst, we do log⁡N\\log NlogN split operations (cascading from the leaf to the root). This simply adds an additive factor of log⁡N\\log NlogN to our runtime, which still results in an overall runtime of O(log⁡N)O(\\log N)O(logN). [Previous18.4 B-Tree Invariantschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.4-b-tree-invariants) [Next18.6 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.6-summary) Last updated 4 months ago * [B-Tree Runtime Analysis](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.5-b-tree-performance#b-tree-runtime-analysis) * [Runtime for contains](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.5-b-tree-performance#runtime-for-contains) * [Runtime for add](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.5-b-tree-performance#runtime-for-add) --- # 12.3 Iteration | CS61B Textbook Fall 2025 We can use a clean enhanced for loop with Java's `HashSet` Copy Set s = new HashSet<>(); s.add("Tokyo"); s.add("Lagos"); for (String city : s) { System.out.println(city); } However, if we try to do the same with our `ArraySet`, we get an error. How can we enable this functionality? [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.3-iteration#enhanced-for-loop) Enhanced For Loop ------------------------------------------------------------------------------------------------------------------------------------------------------------------ Let's first understand what is happening when we use an enhanced for loop. We can "translate" an enhanced for loop into an ugly, manual approach. The above code translates to: Let’s strip away the magic so we can build our own classes that support this. The key here is an object called an _iterator_. For our example, in List.java we might define an `iterator()` method that returns an iterator object. Now, we can use that object to loop through all the entries in our list: This code behaves identically to the foreach loop version above. There are three key methods in our iterator approach: First, we get a new iterator object with `Iterator seer = friends.iterator();` Next, we loop through the list with our while loop. We check that there are still items left with `seer.hasNext()`, which will return true if there are unseen items remaining, and false if all items have been processed. Last, `seer.next()` does two things at once. It returns the next element of the list, and here we print it out. It also advances the iterator by one item. In this way, the iterator will only inspect each item once. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.3-iteration#implementing-iterators) Implementing Iterators ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- In this section, we are going to talk about how to build a class to support iteration. Let's start by thinking about what the compiler needs to know in order to successfully compile the following iterator example: We can look at the static types of each object that calls a relevant method. `friends` is a List, on which `iterator()` is called, so we must ask: * Does the List interface have an iterator() method? `seer` is an Iterator, on which `hasNext()` and `next()` are called, so we must ask: * Does the Iterator interface have next/hasNext() methods? So how do we implement these requirements? The List interface extends the Iterable interface, inheriting the abstract iterator() method. (Actually, List extends Collection which extends Iterable, but it's easier to codethink of this way to start.) Next, the compiler checks that Iterators have `hasNext()` and `next()`. The Iterator interface specifies these abstract methods explicitly: **What if someone calls** `**next**` **when** `**hasNext**` **returns false?** **Will** `**hasNext**` **always be called before** `**next**`**?** Specific classes will implement their own iteration behaviors for the interface methods. Let's look at an example. (Note: if you want to build this up from the start, follow along with the live coding in the video.) We are going to add iteration through keys to our `ArraySet` class. First, we write a new class called ArraySetIterator, nested inside of ArraySet: This ArraySetIterator implements `Iterator`, which means it implements a `hasNext()` method, and a `next()` method, using a `wizPos` position as an index to keep track of its position in the array. For a different data structure, we might implement these two methods differently. **Thought Exercise:** How would you design `hasNext()` and `next()` for a linked list? Now that we have the appropriate methods, we could use a ArraySetIterator to iterate through an `ArraySet`: We still want to be able to support the enhanced for loop, though, to make our calls cleaner. So, we need to make `ArraySet` implement the Iterable interface. The essential method of the Iterable interface is `iterator()`, which returns an Iterator object for that class. All we have to do is return an instance of our `ArraySetIterator` that we just wrote! Now we can use enhanced for loops with our `ArrraySet`! Here we've seen **Iterable**, the interface that makes a class able to be iterated on, and requires the method `iterator()`, which returns an Iterator object. And we've seen **Iterator**, the interface that defines the object with methods to actually do that iteration. You can think of an Iterator as a machine that you put onto an iterable that facilitates the iteration. Any iterable is the object on which the iterator is performing. With these two components, you can make fancy for loops for your classes! `ArraySet` code with iteration support is below: [Previous12.2 Exceptionschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.2-exceptions) [Next12.4 Object Methodschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.4-object-methods) Last updated 6 months ago * [Enhanced For Loop](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.3-iteration#enhanced-for-loop) * [Implementing Iterators](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/12.-inheritance-iv-iterators-object-methods/12.3-iteration#implementing-iterators) Copy Set s = new HashSet<>(); ... for (String city : s) { ... } Copy Set s = new HashSet<>(); ... Iterator seer = s.iterator(); while (seer.hasNext()) { String city = seer.next(); ... } Copy public Iterator iterator(); Copy List friends = new ArrayList(); ... Iterator seer = friends.iterator(); while (seer.hasNext()) { System.out.println(seer.next()); } Copy List friends = new ArrayList(); Iterator seer = friends.iterator(); while(seer.hasNext()) { System.out.println(seer.next()); } Copy public interface Iterable { Iterator iterator(); } Copy public interface List extends Iterable{ ... } Copy public interface Iterator { boolean hasNext(); T next(); } Copy This behavior is undefined. However, a common convention is to throw a `NoSuchElementException`. See [Discussion 5](https://sp19.datastructur.es/materials/discussion/disc05sol.pdf) for examples. Copy Not necessarily. This is sometimes the case when someone using the iterator knows exactly how many elements are in the sequence. Thus, we can't rely on the user calling `hasNext` before `next`. However, you can always call `hasNext` from within your `next` function. Copy private class ArraySetIterator implements Iterator { private int wizPos; public ArraySetIterator() { wizPos = 0; } public boolean hasNext() { return wizPos < size; } public T next() { T returnItem = items[wizPos]; wizPos += 1; return returnItem; } } Copy ArraySet aset = new ArraySet<>(); aset.add(5); aset.add(23); aset.add(42); Iterator iter = aset.iterator(); while(iter.hasNext()) { System.out.println(iter.next()); } Copy public Iterator iterator() { return new ArraySetIterator(); } Copy ArraySet aset = new ArraySet<>(); ... for (int i : aset) { System.out.println(i); } Copy import java.util.Iterator; public class ArraySet implements Iterable { private T[] items; private int size; // the next item to be added will be at position size public ArraySet() { items = (T[]) new Object[100]; size = 0; } /* Returns true if this map contains a mapping for the specified key. */ public boolean contains(T x) { for (int i = 0; i < size; i += 1) { if (items[i].equals(x)) { return true; } } return false; } /* Associates the specified value with the specified key in this map. Throws an IllegalArgumentException if the key is null. */ public void add(T x) { if (x == null) { throw new IllegalArgumentException("can't add null"); } if (contains(x)) { return; } items[size] = x; size += 1; } /* Returns the number of key-value mappings in this map. */ public int size() { return size; } /** returns an iterator (a.k.a. seer) into ME */ public Iterator iterator() { return new ArraySetIterator(); } private class ArraySetIterator implements Iterator { private int wizPos; public ArraySetIterator() { wizPos = 0; } public boolean hasNext() { return wizPos < size; } public T next() { T returnItem = items[wizPos]; wizPos += 1; return returnItem; } } public static void main(String[] args) { ArraySet aset = new ArraySet<>(); aset.add(5); aset.add(23); aset.add(42); //iteration for (int i : aset) { System.out.println(i); } } } --- # 36. Radix Sorts | CS61B Textbook Fall 2025 [36.1 Counting Sortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/36.-radix-sorts/36.1-counting-sort) [36.2 LSD Radix Sortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/36.-radix-sorts/36.2-lsd-radix-sort) [36.3 MSD Radix Sortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/36.-radix-sorts/36.3-msd-radix-sort) [36.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/36.-radix-sorts/36.4-summary) [36.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/36.-radix-sorts/36.5-exercises) [Previous35.5 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/35.-sorting-and-algorithmic-bounds/35.5-exercises) [Next36.1 Counting Sortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/36.-radix-sorts/36.1-counting-sort) Last updated 4 months ago --- # 27.3 Trie String Operations | CS61B Textbook Fall 2025 Tries give us the ability to have constant time lookup and insertion, but they do not always perform better than BSTs and Hash Tables. For any string, we have to traverse through every character, whereas in BSTs and Hash Tables we have access to the entire string immediately. However, Tries are _much_ more useful in the specific application of String Operations. The main appeal of tries is the ability to efficiently support specific string operations like _prefix matching_. You can imagine why tries make this extremely efficient! Say we were trying to find the longestPrefixOf. Just take the word you're looking for, compare each character with characters in your trie until you can go no longer. Similarly, if we wanted keyWithPrefix, we can traverse to the end of the prefix and return all remaining keys in the Trie. Let's attempt to define a method `collect` which returns all of the keys in a Trie. The pseudocode will be as follows: We first initialize our values inside of the parent function, and then create a recursive helper function to hold more parameters throughout the recursive calls. We only add the current string if it is a key, otherwise we concatenate the character to the string/path we are currently traversing and call the helper on the next child. Now we can try writing the method `keysWithPrefix` which returns all keys that contain the prefix passed in as an argument. We will borrow heavily from the collect method above. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.3-trie-string-operations#autocomplete) Autocomplete When you type into any search browser, for example Google, there are always suggestions of what you are about to type. This is extremely helpful and convenient. Say we were searching "How are you doing", if we just type in "how are" into google, we will see that it suggests this exact query. One way to achieve this is using a Trie! We will build a map from strings to values. * Values will represent how important Google thinks that string is (Probably frequency) * Store billions of strings efficiently since they share nodes, less wasteful duplicates * When a user types a query, we can call the method `keysWithPrefix(x)` and return the 10 strings with the highest value One major flaw with this system is if the user types in short length strings. You can imagine that the number of keys with the prefix of the input is in the millions when in reality we only want 10. A possible solution to this issue is to store the best value of a substring in each node. We can then consider children in the order of the best value. Another optimization is to merge nodes that are redundant. This would give us a "radix trie", which holds characters as well as strings in each node. We won't discuss this in depth. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.3-trie-string-operations#summary) Summary Knowing the types of data that you are storing can give you great power in creating efficient data structures. Specifically for implementing Maps and Sets, if we know that all keys will be Strings, we can use a Trie: * Tries theoretically have better performances for searching and insertion than hash tables or balanced search trees * There are more implementations for how to store the children of every node of the trie, specifically three. These three are all fine, but hash table is the most natural * _DataIndexedCharMap_ (Con: excessive use of space, Pro: speed efficient) * _Bushy BST_ (Con: slower child search, Pro: space efficient) * _Hash Table_ (Con: higher cost per link, Pro: space efficient) * Tries may not actually be faster in practice, but they support special string operations that other implementations don't * `longestPrefixOf` and `keysWithPrefix` are easily implemented since the trie is stored character by character * `keysWithPrefix` allows for algorithms like autocomplete to exist, which can be optimized through use of a priority queue. Name key type get(x) Balanced BST comparable Θ(logN)\\Theta (log N)Θ(logN) Θ(logN)\\Theta (log N)Θ(logN) RSC Hash Table hashable Θ(1)†\\Theta (1)^{\\dag}Θ(1)† Θ(1)∗†\\Theta (1)^{\*\\dag}Θ(1)∗† Data Indexed Array chars Θ(1)\\Theta (1)Θ(1) Θ(1)\\Theta (1)Θ(1) Tries (BST, HT, DICM) strings Θ(1)\\Theta (1)Θ(1) Θ(1)\\Theta (1)Θ(1) \*: on average, †\\dag†: items are evenly spread [Previous27.2 Trie Implementationchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.2-trie-implementation) [Next27.4 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.4-summary) Last updated 4 months ago * [Autocomplete](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.3-trie-string-operations#autocomplete) * [Summary](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.3-trie-string-operations#summary) Copy collect(): Create an empty list of results x For character c in root.next.keys(): Call colHelp(c, x, root.next.get(c)) Return x colHelp(String s, List x, Node n): if n.isKey: x.add(s) For character c in n.next.keys(): Call colHelp(s + c, x, n.next.get(c)) Copy keysWithPrefix(String s): Find the end of the prefix, alpha Create an empty list x For character in alpha.next.keys(): Call colHelp("sa" + c, x, alpha.next.get(c)) Return x --- # 28.4 Real World Examples | CS61B Textbook Fall 2025 For this section, it's _highly_ recommended to watch the lecture video sections. What follows are summaries of the content; following the visuals are more insightful. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.4-real-world-examples#retool) Retool Building a system to implement many different kinds of things in a repeatable manner lends itself to designing general specifications for information, and then generating pages as needed. This concept extends towards testing - particularly important as the real world has no autograder and no definition of correctness beyond tests that you write. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.4-real-world-examples#commit-history-traversal) Commit History Traversal Using what you already know in 61B - representing graphs, doing a BFS - you can solve real-world industry problems! This was an example of finding what deployments contain a certain change, by adjusting how we might represent a graph and then doing a BFS while choosing specific data structures for runtime and ease of use. [Previous28.3 Strategic vs Tactical Programmingchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.3-strategic-vs-tactical-programming) [Next28.5 Summary, Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.5-summary-exercises) Last updated 4 months ago * [Retool](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.4-real-world-examples#retool) * [Commit History Traversal](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.4-real-world-examples#commit-history-traversal) --- # 30.2 Selection Sort & Heapsort | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.2-selection-sort-and-heapsort#selection-sort) Selection Sort -------------------------------------------------------------------------------------------------------------------------------------------------- Selection sort uses the following algorithm: 1. Find the smallest item. 2. Swap that item to the front. 3. Repeat until all items are fixed (there are no inversions). You can see a demo of the sorting algorithm [herearrow-up-right](https://docs.google.com/presentation/u/1/d/1p6g3r9BpwTARjUylA0V0yspP2temzHNJEJjCG41I4r0/edit?usp=sharing) . Selection sort runs in Θ(N2)\\Theta(N^2)Θ(N2) time using an array or similar data structure. You may have noticed that selection sort seems inefficient, and you'd be right--we look through the entire remaining array each time to find the minimum, examining the same items over and over. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.2-selection-sort-and-heapsort#heapsort) Heapsort -------------------------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.2-selection-sort-and-heapsort#naive-heapsort) Naive Heapsort To avoid the inefficiency mentioned above regarding selection sort, we can leverage a max-oriented heap instead of scanning over the array linearly. _Note: Because of the array-based representation of a heap, using a max heap results in a simpler implementation where we can maintain a "sorted" and "unsorted" portion of the array. This will be explained further in the next section._ Then, to heapsort N items, we can insert all the items into a max heap and create and output array. Then, we repeatedly delete the largest item from the max heap and put the largest item at the end part of the output array. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.2-selection-sort-and-heapsort#naive-heapsort-analysis) Naive Heapsort Analysis The overall runtime of this algorithm is Θ(Nlog⁡N)\\Theta(N \\log N)Θ(NlogN). There are three main components to this runtime: * Inserting N items into the heap: O(Nlog⁡N)O(N \\log N)O(NlogN). * Selecting the largest item: Θ(1)\\Theta(1)Θ(1). * Removing the largest item: O(log⁡N)O(\\log N)O(logN). This is a large improvement over selection sort's Θ(N2)\\Theta(N^2)Θ(N2) runtime. In terms of memory usage, the output array takes an extra Θ(N)\\Theta(N)Θ(N)space. This is worse than selection sort, which uses no extra space, but the improvement in runtime far outweighs this downside. Even more, we can use a trick with heapsort to get rid of the extra output array. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.2-selection-sort-and-heapsort#in-place-heapsort) In-place Heapsort As an alternate approach, we can use the input array itself to form the heap and output array. Rather than inserting into a new array that represents our heap, we can use a process known as _bottom-up heapification_ to convert the input array into a heap. Bottom-up heapification involves moving in reverse level order up the heap, sinking nodes to their appropriate location as you move up. By using this approach, we avoid the need for an extra copy of the data. Once heapified, we use the naive heapsort approach of popping off the maximum and placing it at the end of our array. In doing so, we maintain an "unsorted" front portion of the array (representing the heap) and a "sorted" back portion of the the array (representing the sorted items so far). ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-3a4b66ad50934c916a28089a6c84f3eae77532a1%252Fimage%2520%28120%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=2f5023ef&sv=2) Bottom-up heapfication You can see a demo of this algorithm [herearrow-up-right](https://docs.google.com/presentation/d/1SzcQC48OB9agStD0dFRgccU-tyjD6m3esrSC-GLxmNc/edit?usp=sharing) . #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.2-selection-sort-and-heapsort#in-place-heapsort-runtime) In-place Heapsort Runtime This process overall is still O(Nlog⁡N)O(N \\log N)O(NlogN), since bottom-up heapification requires at most NNN sink-down operations that take at most log⁡N\\log NlogN time each. _Note: it is possible to prove that bottom-up heapficiation is bounded by_ Θ(N)\\Theta(N)Θ(N). _However, this proof is out of scope for this class._ #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.2-selection-sort-and-heapsort#in-place-heapsort-memory) In-place Heapsort Memory Using in-place heapsort, we reduce the memory usage of heapsort to Θ(1)\\Theta(1)Θ(1). Since we are reusing the input array, no additional space is used (and remember that we do not count the input when assessing memory complexity). [Previous30.1 The Sorting Problemchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.1-the-sorting-problem) [Next30.3 Mergesortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.3-mergesort) Last updated 4 months ago * [Selection Sort](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.2-selection-sort-and-heapsort#selection-sort) * [Heapsort](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.2-selection-sort-and-heapsort#heapsort) * [Naive Heapsort](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.2-selection-sort-and-heapsort#naive-heapsort) * [In-place Heapsort](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.2-selection-sort-and-heapsort#in-place-heapsort) --- # 7. Testing | CS61B Textbook Fall 2025 ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/7.-testing#testing-and-selection-sort) Testing and Selection Sort One of the most important skills an intermediate to advanced programmer can have is the ability to tell when your code is correct. In this chapter, we'll discuss how you can write tests to evaluate code correctness. Along the way, we'll also discuss an algorithm for sorting called Selection Sort. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/7.-testing#a-new-way) A New Way When you write a program, it may have errors. In a classroom setting, you gain confidence in your code's correctness through some combination of user interaction, code analysis, and autograder testing, with this last item being of the greatest importance in many cases, particularly as it is how you earn points. Autograders, of course, are not magic. They are code that the instructors write that is fundamentally not all that different from the code that you are writing. In the real world, these tests are written by the programmers themselves, rather than some benevolent Josh-Hug-like third party. In this chapter, we'll explore how we can write our own tests. Our goal will be to create a class called `Sort` that provides a method `sort(String[] x)` that destructively sorts the strings in the array `x`. As a totally new way of thinking, we'll start by writing `testSort()` first, and only after we've finished the test, we'll move on to writing the actual sorting code. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/7.-testing#a-d-hoc-testing) Ad Hoc Testing Writing a test for `Sort.sort` is relatively straightforward, albeit tedious. We simply need to create an input, call `sort`, and check that the output of the method is correct. If the output is not correct, we print out the first mismatch and terminate the test. For example, we might create a test class as follows: Copy public class TestSort { /** Tests the sort method of the Sort class. */ public static void testSort() { String[] input = {"i", "have", "an", "egg"}; String[] expected = {"an", "egg", "have", "i"}; Sort.sort(input); for (int i = 0; i < input.length; i += 1) { if (!input[i].equals(expected[i])) { System.out.println("Mismatch in position " + i + ", expected: " + expected + ", but got: " + input[i] + "."); break; } } } public static void main(String[] args) { testSort(); } } We can test out our test by creating a blank `Sort.sort` method as shown below: If we run the `testSort()` method with this blank `Sort.sort` method, we'd get: The fact that we're getting an error message is a good thing! This means our test is working. What's very interesting about this is that we've now created a little game for ourselves to play, where the goal is to modify the code for `Sort.sort` so that this error message no longer occurs. It's a bit of a psychological trick, but many programmers find the creation of these little mini-puzzles for themselves to be almost addictive. In fact, this is a lot like the situation where you have an autograder for a class, and you find yourself hooked on the idea of getting the autograder to give you its love and approval. You now have the ability to create a judge for your code, whose esteem you can only win by completing the code correctly. **Important note:** You may be asking "Why are you looping through the entire array? Why don't you just check if the arrays are equal using `==`? ". The reason is, when we test for equality of two objects, we cannot simply use the `==` operator. The `==` operator compares the literal bits in the memory boxes, e.g. `input == expected` would test whether or not the addresses of `input` and `expected` are the same, not whether the values in the arrays are the same. Instead, we used a loop in `testSort`, and print out the first mismatch. You could also use the built-in method `java.util.Arrays.equals` instead of a loop. While the single test above wasn't a ton of work, writing a suite of such _ad hoc_ tests would be very tedious, as it would entail writing a bunch of different loops and print statements. In the next section, we'll see how the `org.junit` library saves us a lot of work. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/7.-testing#junit-testing) JUnit Testing The Google Truth library provides a number of helpful methods and useful capabilities for simplifying the writing of tests. For example, we can replace our simple _ad hoc_ test from above with: **This code is much simpler**, and does more or less the exact same thing, i.e. if the arrays are not equal, it will tell us the first mismatch. For example, if we run `testSort()` on a `Sort.sort` method that does nothing, we'd get: While this output is a little uglier than our _ad hoc_ test, we'll see at the very end of this chapter how to make it nicer. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/7.-testing#selection-sort) Selection Sort Before we can write a `Sort.sort` method, we need some algorithm for sorting. Perhaps the simplest sorting algorithm around is "selection sort." Selection sort consists of three steps: * Find the smallest item. * Move it to the front. * Selection sort the remaining N-1 items (without touching the front item). For example, suppose we have the array `{6, 3, 7, 2, 8, 1}`. The smallest item in this array is `1`, so we'd move the `1` to the front. There are two natural ways to do this: One is to stick the `1` at the front and slide all the numbers over, i.e. `{1, 6, 3, 7, 2, 8}`. However, the much more efficient way is to simply swap the `1` with the old front (in this case `6`), yielding `{1, 3, 7, 2, 8, 6}`. We'd simply repeat the same process for the remaining digits, i.e. the smallest item in `... 3, 7, 2, 8, 6}` is `2`. Swapping to the front, we get `{1, 2, 7, 3, 8, 6}`. Repeating until we've got a sorted array, we'd get `{1, 2, 3, 7, 8, 6}`, then `{1, 2, 3, 6, 8, 7}`, then finally `{1, 2, 3, 6, 7, 8}`. We could mathematically prove the correctness of this sorting algorithm on any arrays by using the concept of invariants that was originally introduced in chapter 2.4, though we will not do so in this textbook. Before proceeding, try writing out your own short array of numbers and perform selection sort on it, so that you can make sure you get the idea. Now that we know how selection sort works, we can write in a few short comments in our blank `Sort.sort` method to guide our thinking: In the following sections, I will attempt to complete an implementation of selection sort. I'll do so in a way that resembles how a student might approach the problem, so **I'll be making a few intentional errors along the way**. These intentional errors are a good thing, as they'll help demonstrate the usefulness of testing. If you spot any of the errors while reading, don't worry, we'll eventually come around and correct them. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/7.-testing#findsmallest) findSmallest The most natural place to start is to write a method for finding the smallest item in a list. As with `Sort.sort`, we'll start by writing a test before we even complete the method. First, we'll create a dummy `findSmallest` method that simply returns some arbitrary value: Obviously this is not a correct implementation, but we've chosen to defer actually thinking about how `findSmallest` works until after we've written a test. Using the `org.junit` library, adding such a test to our `TestSort` class is very easy, as shown below: As with `TestSort.testsort`, we then run our `TestSort.testFindSmallest` method to make sure that it fails. When we run this test, we'll see that it actually passes, i.e. no message appears. This is because we just happened to hard code the correct return value `x[2]`. Let's modify our `findSmallest` method so that it returns something that is definitely incorrect: After making this change, when we run `TestSort.testFindSmallest`, we'll get an error, which is a good thing: As before, we've set up for ourselves a little game to play, where our goal is now to modify the code for `Sort.findSmallest` so that this error no longer appears. This is a smaller goal than getting `Sort.sort` to work, which might be even more addictive. Side note: It might have seem rather contrived that I just happened to return the right value `x[2]`. However, when I was recording this lecture video, I actually did make this exact mistake without intending to do so! Next we turn to actually writing `findSmallest`. This seems like it should be relatively straightforward. If you're a Java novice, you might end up writing code that looks something like this: However, this will yield the compilation error "< cannot be applied to 'java.lang.String'". The issue is that Java does not allow comparisons between Strings using the < operator. When you're programming and get stuck on an issue like this that is easily describable, it's probably best to turn to a search engine. For example, we might search "less than strings Java" with Google. Such a search might yield a Stack Overflow post like [this onearrow-up-right](https://stackoverflow.com/questions/5153496/how-can-i-compare-two-strings-in-java-and-define-which-of-them-is-smaller-than-t) . One of the popular answers for this post explains that the `str1.compareTo(str2)` method will return a negative number if `str1 < str2`, 0 if they are equal, and a positive number if `str1 > str2`. Incorporating this into our code, we might end up with: Note that we've used a `@source` tag in order to cite our sources. I'm showing this by example for those of you who are taking 61B as a formal course. This is not a typical real world practice. Since we are using syntax features that are totally new to us, we might lack confidence in the correctness of our `findSmallest` method. Luckily, we just wrote that test a little while ago. If we try running it, we'll see that nothing gets printed, which means our code is probably correct. We can augment our test to increase our confidence by adding more test cases. For example, we might change `testFindSmallest` so that it reads as shown below: Rerunning the test, we see that it still passes. We are not absolutely certain that it works, but we are much more certain that we would have been without any tests. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/7.-testing#swap) Swap Looking at our `sort` method below, the next helper method we need to write is something to move an item to the front, which we'll call `swap`. Writing a `swap` method is very straightforward, and you've probably done so before. A correct implementation might look like: However, for the moment, let's introduce an intentional error so that we can demonstrate the utility of testing. A more naive programmer might have done something like: Writing a test for this method is quite easy with the help of JUnit. An example test is shown below. Note that we have also edited the main method so that it calls `testSwap` instead of `testFindSmallest` or `testSort`. Running this test on our buggy `swap` yields an error, as we'd expect. It's worth briefly noting that it is important that we call only `testSwap` and not `testSort` as well. For example, if our `main` method was as below, the entire `main` method will terminate execution as soon as `testSort` fails, and `testSwap` will never run: We will learn a more elegant way to deal with multiple tests at the end of this chapter that will avoid the need to manually specify which tests to run. Now that we have a failing test, we can use it to help us debug. One way to do this is to set a breakpoint inside the `swap` method and use the visual debugging feature in IntelliJ. If you would like more information about and practice on debugging, check out [Lab3arrow-up-right](https://sp19.datastructur.es/materials/lab/lab3/lab3) . Stepping through the code line-by-line makes it immediately clear what is wrong (see video or try it yourself), and we can fix it by updating our code to include a temporary variable as that the beginning of this section: Rerunning the test, we see that it now passes. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/7.-testing#revising-findsmallest) Revising findSmallest Now that we have multiple pieces of our method done, we can start trying to connect them up together to create a `Sort` method. It's clear how to use our `findSmallest` and `swap` methods, but when we do so, we immediately realize there is a bit of a mismatch: `findSmallest` returns a `String`, and `swap` expects two indices. In other words, what `findSmallest` should have been returning is the index of the smallest String, not the String itself. Making silly errors like this is normal and really easy to do, so don't sweat it if you find yourself doing something similar. Iterating on a design is part of the process of writing code. Luckily, this new design can be easily changed. We simply need to adjust `findSmallest` to return an `int`, as shown below: Since this is a non-trivial change, we should also update `testFindSmallest` and make sure that `findSmallest` still works. After modifying `TestSort` so that this test is run, and running `TestSort.main`, we see that our code passes the tests. Now, revising sort, we can fill in the first two steps of our sorting algorithm. All that's left is to somehow selection sort the remaining items, perhaps using recursion. We'll tackle this in the next section. Reflecting on what we've accomplished, it's worth noting how we created tests first, and used these to build confidence that the actual methods work before we ever tried to use them for anything. This is an incredibly important idea, and one that will serve you well if you decide to adopt it. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/7.-testing#recursive-helper-methods) Recursive Helper Methods To begin this section, consider how you might make the recursive call needed to complete `sort`: For those of you who are used to a language like Python, it might be tempting to try and use something like slice notation, e.g. However, there is no such thing in Java as a reference to a sub-array, i.e. we can't just pass the address of the next item in the array. This problem of needing to consider only a subset of a larger array is very common. A typical solution is to create a private helper method that has an additional parameter (or parameters) that delineate which part of the array to consider. For example, we might write a private helper method also called `sort` that consider only the items starting with item `start`. Unlike our public sort method, it's relatively straightforward to use recursion now that we have the additional parameter `start`, as shown below. We'll test this method in the next section. Now that we have a helper method, we need to set up the correct original call. If we set the start to 0, we effectively sort the entire array. This approach is quite common when trying to use recursion on a data structure that is not inherently recursive, e.g. arrays. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/7.-testing#debugging-and-completing-sort) Debugging and Completing Sort Running our `testSort` method, we immediately run into a problem: Using the Java debugger, we see that the problem is that somehow `start` is reaching the value 4. Stepping through the code carefully (see video above), we find that the issue is that we forgot to include a base case in our recursive `sort` method. Fixing this is straightforward: Rerunning this test again, we get another error: Again, with judicious use of the IntelliJ debugger (see video), we can identify a line of code whose result does not match our expectations. Of note is the fact that I debugged the code at a higher level of abstraction than you might have otherwise, which I achieve by using `Step Over` more than `Step Into`. As discussed in lab 3, debugging at a higher level of abstraction saves you a lot of time and energy, by allowing you to compare the results of entire function calls with your expectation. Specifically, we find that when sorting the last 3 (out of 4) items, the `findSmallest` method is giving as the 0th item (`"an"`) rather than the 3rd item (`"egg"`) when called on the input `{"an", "have", "i", "egg"}`. Looking carefully at the definition of `findSmallest`, this behavior is not a surprise, since `findSmallest` looks at the entire array, not just the items starting from position `start`. This sort of design flaw is very common, and writing tests and using the debugger is a great way to go about fixing them. To fix our code, we revise `findSmallest` so that it takes a second parameter `start`, i.e. `findSmallest(String[] x, int start)`. In this way, we ensure that we're finding the smallest item only out of the last however many are still unsorted. The revision is as shown below: Given that we've made a significant change to one of our building blocks, i.e. `findSmallest`, we should ensure that our changes are correct. We first modify `testFindSmallest` so that it uses our new parameter, as shown below: We then modify `TestSort.main` so that it runs `testFindSmallest`. This test passes, strongly suggesting that our revisions to `findSmallest` were correct. We next modify `Sort.sort` so that it uses the new `start` parameter in `findSmallest`: We then modify `TestSort` so that it runs `TestSort.sort` and voila, the method works. We are done! You have now seen the "new way" from the beginning of this lecture, which we'll reflect on for the remainder of this chapter. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/7.-testing#reflections-on-the-development-process) Reflections on the Development Process When you're writing and debugging a program, you'll often find yourself switching between different contexts. Trying to hold too much in your brain at once is a recipe for disaster at worst, and slow progress at best. Having a set of automated tests helps reduce this cognitive load. For example, we were in the middle of writing `sort` when we realized there was a bug in `findSmallest`. We were able to switch contexts to consider `findSmallest` and establish that it was correct using our `testFindSmallest` method, and then switch back to `sort`. This is in sharp contrast to a more naive approach where you would simply be calling `sort` over and over and trying to figure out if the behavior of the overall algorithm suggests that the `findSmallest` method is correct. As an analogy, you could test that a parachute's ripcord works by getting in an airplane, taking off, jumping out, and pulling the ripcord and seeing if the parachute comes out. However, you could also just pull it on the ground and see what happens. So, too, is it unnecessary to use `sort` to try out `findSmallest`. As mentioned earlier in this chapter, tests also allow you to gain confidence in the basic pieces of your program, so that if something goes wrong, you have a better idea of where to start looking. Lastly, tests make it easier to refactor your code. Suppose you decide to rewrite `findSmallest` so that it is faster or more readable. We can safely do so by making our desired changes and seeing if the tests still work. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/7.-testing#more-testing-features) More Testing Features If we add `@Test` before a method AND make the function non-static, green arrows appear. * The single green arrow by testSort means “run this function”. * The double green arrow means run all tests in this class. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Flh7-us.googleusercontent.com%2FAxG53TwAIPggHLPD_XgOBQkOXuaAfUs5lyTY7_WBhpeS8EmNiNFppveTHIXU0HUgji_NxgpI6v6wAfGJfQuMDoRkSFm78_fL5PT_1QkjfiEyrTv7GSIPhWOSX9P_RhKgw6XKc_-M0K-992_ktzGMtEokSQ%3Ds2048&width=768&dpr=3&quality=100&sign=4a532ae0&sv=2) The reason why the function has to be non-static is unclear, though this probably has to do with things happening behind the scene. One added benefit of doing this is that IntelliJ will now gamify bug fixing and design. You have concrete mini-goals and your progress is summarized in bottom left. You win when you get green checks for every test. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Flh7-us.googleusercontent.com%2FBRu_eKYg7yRI7jAqgGXnHzN4CvNiKWtVMq-SbDC9vzjS2Vd8rNbGB-GBUWcGxyinejZWzO-dmWbJ2WGa3jRDpJ56yPXHExjkUJYfCL46V7stzGSFT2dOO0FsrIQqgOLdE6iFfrVvCfr0n6IN1EjQcRXYnw%3Ds2048&width=768&dpr=3&quality=100&sign=7eb65227&sv=2) #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/7.-testing#testing-philosophy) Testing Philosophy **Correctness Tool #1: Autograder** Let's go back to ground zero. The autograder was likely the first correctness tool you were exposed to. Our autograder is in fact based on JUnit plus some extra custom libraries. There are some great benefits to autograders. Perhaps most importantly, it verifies correctness for you, saving you from the tedious and non-instructive task of writing all of your own tests. It also gamifies the assessment process by providing juicy points as an incentive to acheiving correctness. This can also backfire if students spend undue amounts of time chasing final points that won't actually affect their grade or learning. However, autograders don't exist in the real world and relying on autograders can build bad habits. One's workflow is hindered by sporadically uploading your code and waiting for the autograder to run. _Autograder Driven Development_ is an extreme version of this in which students write all their code, fix their compiler errors, and then submit to the autograder. After getting back errors, students may try to make some changes, sprinkle in print statements, and submit again. And repeat. Ultimately, you are not in control of either your workflow or your code if you rely on an autograder. **Correctness Tool #2: JUnit Tests** JUnit testing, as we have seen, unlocks a new world for you. Rather than relying on an autograder written by someone else, you write tests for each piece of your program. We refer to each of these pieces as a unit. This allows you to have confidence in each unit of your code - you can depend on them. This also helps decrease debugging time as you can isolate attention to one unit of code at a time (often a single method). Unit testing also forces you to clarify what each unit of code should be accomplishing. There are some downsides to unit tests, however. First, writing thorough tests takes time. It's easy to write incomplete unit tests which give a false confidence to your code. It's also difficult to write tests for units that depend on other units (consider the `addFirst` method in your `LinkedListDeque`). _**Test-Driven Development (TDD)**_ TDD is a development process in which we write tests for code before writing the code itself. The steps are as follows: 1. Identify a new feature. 2. Write a unit test for that feature. 3. Run the test. It should fail. 4. Write code that passes the test. Yay! 5. Optional: refactor code to make it faster, cleaner, etc. Except now we have a reference to tests that should pass. Test-Driven Development is not required in this class and may not be your style but unit testing in general is most definitely a good idea. **Correctness Tool #3: Integration Testing** Unit tests are great but we should also make sure these units work properly together ([unlike this memearrow-up-right](https://media.giphy.com/media/3o7rbPDRHIHwbmcOBy/giphy.gif) ). Integration testing verifies that components interact properly together. JUnit can in fact be used for this. You can imagine unit testing as the most nitty gritty, with integration testing a level of abstraction above this. The challenge with integration testing is that it is tedious to do manually yet challenging to automate. And at a high level of abstraction, it's easy to miss subtle or rare errors. As a summary, you should **definitely write tests but only when they might be useful!** Taking inspiration from TDD, writing your tests before writing code can also be very helpful in some cases. [Previous6\. Arrayschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/6.-arrays) [Next8\. ArrayListchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/8.-arraylist) Last updated 6 months ago Copy public class Sort { /** Sorts strings destructively. */ public static void sort(String[] x) { } } Copy Mismatch in position 0, expected: an, but got: i. Copy import static com.google.common.truth.Truth.assertThat; public class TestSort { /** Tests the sort method of the Sort class. */ public static void testSort() { String[] input = {"cows", "dwell", "above", "clouds"}; String[] expected = {"above", "clouds", "cows", "dwell"}; Sort.sort(input); assertThat(input).isEqualTo(expected); } public static void main(String[] args) { testSort(); } } Copy Exception in thread "main" arrays first differed at element [0]; expected:<[an]> but was:<[i]> at org.junit.internal.ComparisonCriteria.arrayEquals(ComparisonCriteria.java:55) at org.junit.Assert.internalArrayEquals(Assert.java:532) ... Copy public class Sort { /** Sorts strings destructively. */ public static void sort(String[] x) { // find the smallest item // move it to the front // selection sort the rest (using recursion?) } } Copy public class Sort { /** Sorts strings destructively. */ public static void sort(String[] x) { // find the smallest item // move it to the front // selection sort the rest (using recursion?) } /** Returns the smallest string in x. */ public static String findSmallest(String[] x) { return x[2]; } } Copy public class TestSort { @Test public void testFindSmallest() { String[] input = {"rawr", "a", "zaza", "newway"}; String expected = "zaza"; String actual = Sort.findSmallest(input); assertThat(actual).isEqualTo(expected); } } Copy /** Returns the smallest string in x. */ public static String findSmallest(String[] x) { return x[3]; } Copy Exception in thread "main" java.lang.AssertionError: expected:<[an]> but was:<[null]> at org.junit.Assert.failNotEquals(Assert.juava:834) at TestSort.testFindSmallest(TestSort.java:9) at TestSort.main(TestSort.java:24) Copy /** Returns the smallest string in x. */ public static String findSmallest(String[] x) { String smallest = x[0]; for (int i = 0; i < x.length; i += 1) { if (x[i] < smallest) { smallest = x[i]; } } return smallest; } Copy /** Returns the smallest string in x. * @source Got help with string compares from https://goo.gl/a7yBU5. */ public static String findSmallest(String[] x) { String smallest = x[0]; for (int i = 0; i < x.length; i += 1) { int cmp = x[i].compareTo(smallest); if (cmp < 0) { smallest = x[i]; } } return smallest; } Copy public static void testFindSmallest() { String[] input = {"i", "have", "an", "egg"}; String expected = "an"; String actual = Sort.findSmallest(input); assertThat(actual).isEqualTo(expected); String[] input2 = {"there", "are", "many", "pigs"}; String expected2 = "are"; String actual2 = Sort.findSmallest(input2); assertThat(actual2).isEqualTo(expected2); } Copy /** Sorts strings destructively. */ public static void sort(String[] x) { // find the smallest item // move it to the front // selection sort the rest (using recursion?) } Copy public static void swap(String[] x, int a, int b) { String temp = x[a]; x[a] = x[b]; x[b] = temp; } Copy public static void swap(String[] x, int a, int b) { x[a] = x[b]; x[b] = x[a]; } Copy public class TestSort { ... /** Test the Sort.swap method. */ public static void testSwap() { String[] input = {"i", "have", "an", "egg"}; int a = 0; int b = 2; String[] expected = {"an", "have", "i", "egg"}; Sort.swap(input, a, b); assertThat(expected).isEqualTo(input); } public static void main(String[] args) { testSwap(); } } Copy Exception in thread "main" arrays first differed in element [2]; expected:<[i]> but was:<[an]> at TestSort.testSwap(TestSort.java:36) Copy public static void main(String[] args) { testSort(); testFindSmallest(); testSwap(); } Copy public static void swap(String[] x, int a, int b) { String temp = x[a]; x[a] = x[b]; x[b] = temp; } Copy /** Sorts strings destructively. */ public static void sort(String[] x) { // find the smallest item // move it to the front // selection sort the rest (using recursion?) } Copy /** Sorts strings destructively. */ public static void sort(String[] x) { // find the smallest item String smallest = findSmallest(x); // move it to the front swap(x, 0, smallest); // selection sort the rest (using recursion?) } Copy public static int findSmallest(String[] x) { int smallestIndex = 0; for (int i = 0; i < x.length; i += 1) { int cmp = x[i].compareTo(x[smallestIndex]); if (cmp < 0) { smallestIndex = i; } } return smallestIndex; } Copy public static void testFindSmallest() { String[] input = {"i", "have", "an", "egg"}; int expected = 2; int actual = Sort.findSmallest(input); assertThat(actual).isEqualTo(expected); String[] input2 = {"there", "are", "many", "pigs"}; int expected2 = 1; int actual2 = Sort.findSmallest(input); assertThat(actual2).isEqualTo(expected2); } Copy /** Sorts strings destructively. */ public static void sort(String[] x) { // find the smallest item // move it to the front // selection sort the rest (using recursion?) int smallestIndex = findSmallest(x); swap(x, 0, smallestIndex); } Copy /** Sorts strings destructively. */ public static void sort(String[] x) { int smallestIndex = findSmallest(x); swap(x, 0, smallestIndex); // recursive call?? } Copy /** Sorts strings destructively. */ public static void sort(String[] x) { int smallestIndex = findSmallest(x); swap(x, 0, smallestIndex); sort(x[1:]) } Copy /** Sorts strings destructively starting from item start. */ private static void sort(String[] x, int start) { // TODO } Copy /** Sorts strings destructively starting from item start. */ private static void sort(String[] x, int start) { int smallestIndex = findSmallest(x); swap(x, start, smallestIndex); sort(x, start + 1); } Copy /** Sorts strings destructively. */ public static void sort(String[] x) { sort(x, 0); } Copy Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 4 at Sort.swap(Sort.java:16) Copy /** Sorts strings destructively starting from item start. */ private static void sort(String[] x, int start) { if (start == x.length) { return; } int smallestIndex = findSmallest(x); swap(x, start, smallestIndex); sort(x, start + 1); } Copy Exception in thread "main" arrays first differed at element [0]; expected<[an]> bit was:<[have]> Copy public static int findSmallest(String[] x, int start) { int smallestIndex = start; for (int i = start; i < x.length; i += 1) { int cmp = x[i].compareTo(x[smallestIndex]); if (cmp < 0) { smallestIndex = i; } } return smallestIndex; } Copy public static void testFindSmallest() { String[] input = {"i", "have", "an", "egg"}; int expected = 2; int actual = Sort.findSmallest(input, 0); assertThat(actual).isEqualTo(expected); String[] input2 = {"there", "are", "many", "pigs"}; int expected2 = 2; int actual2 = Sort.findSmallest(input2, 2); assertThat(actual2).isEqualTo(expected2); } Copy /** Sorts strings destructively starting from item start. */ private static void sort(String[] x, int start) { if (start == x.length) { return; } int smallestIndex = findSmallest(x, start); swap(x, start, smallestIndex); sort(x, start + 1); } --- # 15.3 For Loops | CS61B Textbook Fall 2025 Now that we've seen some runtime analysis, let's work through some more difficult examples. Our goal is to get some practice with the patterns and methods involved in runtime analysis. This can be a tricky idea to get a handle on, so the more practice the better. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.3-for-loops#scan-example) Scan Example: -------------------------------------------------------------------------------------------------------------------------------- Last time, we saw the function dup1, that checks for the first time any entry is duplicated in a list: We have two ways of approaching our runtime analysis: 1. Counting number of operations 2. Geometric visualization ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.3-for-loops#method-1-count-number-of-operations) Method 1: Count Number of Operations Since the main repeating operation is the comparator, we will count the number of **"=="** operations that must occur. The first time through the outer loop, the inner loop will run N−1N-1N−1times. The second time, it will run N−2N-2N−2 times. Then N−3N-3N−3, N−4N-4N−4, .... all the way till running the inner loop exactly 111 time when i = N−1N - 1N−1. In the worst case, we have to go through every entry, and the outer loop runs NNN times. Then, let CCC = total number of "==" operations that have occurred. The number of comparisons is: C\=1+2+3+...+(N−3)+(N−2)+(N−1)\=N(N−1)/2C = 1 + 2 + 3 + ... + (N - 3) + (N - 2) + (N - 1) = N(N-1)/2C\=1+2+3+...+(N−3)+(N−2)+(N−1)\=N(N−1)/2 where N(N−1)/2N(N-1)/2N(N−1)/2 is part of the N2N^2N2 family. Since "==" is a constant time operation, the overall runtime in the worst case is θ(N2)\\theta(N^2)θ(N2). ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.3-for-loops#method-2-geometric-visualization) Method 2: Geometric Visualization We can also approach this from a geometric view. Let's draw out when we use == operations in the grid of i,ji,ji,jcombinations: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-b4d7d4369013f708b152a85652ed24b19e7d8ac3%252Fimage%2520%2896%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=326265ad&sv=2) We see that the number of == operations is the same as the _area_ of a right triangle with a side length of N−1N - 1N−1. Since area is in the N2N^2N2​​ family, we see again that the overall runtime is θ(N2)\\theta(N^2)θ(N2). [Previous15.2 Big Ochevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.2-big-o) [Next15.4 For Loops Print Partychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.4-for-loops-print-party) Last updated 4 months ago * [Scan Example:](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.3-for-loops#scan-example) * [Method 1: Count Number of Operations](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.3-for-loops#method-1-count-number-of-operations) * [Method 2: Geometric Visualization](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/15.-asymptotics-ii/15.3-for-loops#method-2-geometric-visualization) Copy int N = A.length; for (int i = 0; i < N; i += 1) for (int j = i + 1; j < N; j += 1) if (A[i] == A[j]) return true; return false; --- # 30.1 The Sorting Problem | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.1-the-sorting-problem#sorting) Sorting ---------------------------------------------------------------------------------------------------------------------------- For the remaining part of this textbook, we'll discuss the sorting problem, which can be informally defined as putting a given set of items in a particular order. This is a useful task in its own right, but can also be a subproblem in larger algorithmic problems. Sorting can be applied to problems like duplicate finding (after sorting, equivalent items are adjacent), binary search, and balancing data structures. The other reason we introduce sorting is that the different sorts provide general ideas about how to approach computational problems. The solution(s) to sorting problems will often involve data structures covered in the earlier parts of this course. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.1-the-sorting-problem#sorting-definitions) Sorting: Definitions An **ordering relation** <<< for keys a, b, and c has the following properties: * _Law of Trichotomy_: Exactly one of a <<< b, a = b, b <<< a is true. * _Law of Transitivity_: If a <<< b, and b <<< c, then a <<< c. An ordering relation with the properties above is also known as a **total order**. A **sort** is a permutation of a sequence of elements that puts the keys into non-decreasing order relative to a given ordering relation, such that x1 ≤ x2 ≤ x3≤ ...≤ xN. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.1-the-sorting-problem#example-string-length) Example: String Length One example of a ordering relation is the length of strings. To see how the two laws apply: * _trichotomy_: only one of the following can be true for two strings `a` and `b`\--`len(a)` < `len(b)`, `len(a)` = `len(b)`, or `len(a)` > `len(b)`. * _transitivity:_ if `len(a)` < `len(b)` and `len(b)` < `len(c)`, then clearly `len(a)` < `len(c)`. Suppose we use the ordering relation above to sort `["cows", "get", "going", "the"]`. Then two valid sorts would be `["the", "get", "cows", "going"]` and `["get", "the", "cows", "going"]`. Note that in this sort, `the` and `get` are equivalent since their lengths are equal. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.1-the-sorting-problem#ordering-relations-in-java) Ordering Relations in Java In Java, ordering relations are typically given by the `compareTo` or `compare` methods. For example: Note by the relation above, `the` and `get` are equal in ordering, but _not_ equal by the `.equals()` method. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.1-the-sorting-problem#inversions) Inversions An alternate way to view sorting is as fixing inversions within a sequence of elements. An **inversion** is a pair of elements that are out of order with respect to the defined ordering relation. For example, in the following sequence of 11 elements, there are 55 possible inversions at most (11 choose 2), and the sequence itself has 6 inversions. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-4e211b3a974fa45f9a69a46117fae18f1dfc5b5c%252Fimage%2520%2833%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=4b464b5b&sv=2) The sequence above has 6 inversions Sorting, then, can be viewed as follows: given a sequence of elements with Z inversions, perform some sequence of operations to reduce the total number of inversions to zero. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.1-the-sorting-problem#sorting-performance) Sorting: Performance Previously, we have seen characterizations of of the runtime efficiency of an algorithm, also called the **time complexity** of an algorithm. For example, we can say that Dijkstra’s has time complexity O(E log V). Characterizations of the “extra” memory usage of an algorithm is sometimes called the **space complexity** of an algorithm. For example, Dijkstra’s has space complexity Θ(V) to store the queue, `distTo`, and `edgeTo` arrays. Note that the graph takes up space Θ(V+E), but we don’t count this as part of the space complexity of Dijkstra since the graph is an input to Dijkstra’s. In other words, we are only concerned with the _extra_ space used by the algorithm. [Previous30\. Basic Sortschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts) [Next30.2 Selection Sort & Heapsortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.2-selection-sort-and-heapsort) Last updated 4 months ago * [Sorting](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.1-the-sorting-problem#sorting) * [Sorting: Definitions](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.1-the-sorting-problem#sorting-definitions) * [Ordering Relations in Java](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.1-the-sorting-problem#ordering-relations-in-java) * [Inversions](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.1-the-sorting-problem#inversions) * [Sorting: Performance](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.1-the-sorting-problem#sorting-performance) Copy import java.util.Comparator; public class LengthComparator implements Comparator { public int compare(String x, String b) { return x.length() - b.length(); } } --- # 40.2 Optimal Compression, Kolmogorov Complexity | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.2-optimal-compression-kolmogorov-complexity#kolmogorov-complexity) Kolmogorov Complexity ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- We define the **Kolmogorov complexity** of a bitstream `B` to be the shortest bitstream CBC\_BCB​ that outputs `B`. Let the _Java-Kolmogorov complexity_ KJ(B)K\_J(B)KJ​(B) be the shortest Java program that generates `B`. Note that for any bitstream BBB, K(B)K(B)K(B) definitely exists. However, finding and proving K(B)K(B)K(B) might be difficult or even impossible. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.2-optimal-compression-kolmogorov-complexity#languages-and-complexity) Languages and Complexity An important thing to note is that Kolmogorov complexity is language-independent. To run any program in one language in another, all I have to do is write an interpreter. For example, if I want to run a Python program that is not easily translatable to Java, I could instead just write a Java interpreter to read the text of the Python program and run it. In this case, KJ(B)≤KP(B)+IK\_J(B) \\leq K\_P(B) + IKJ​(B)≤KP​(B)+I, where III is the length of the interpreter (a constant value). This highlights a very deep fact about Kolmogorov complexity: most bitstreams are fundamentally incompressible no matter which language we choose for our compression algorithm. Consider a bitstream of 1,000,000 bits. Out of all compression algorithms possible, only 1 in 249999992^{4999999}24999999 bitstreams have a change of being compressed by more than 50% (499,999 bits or less). ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.2-optimal-compression-kolmogorov-complexity#uncomputability) Uncomputability Another important fact regarding Kolmogorov complexity is that it is impossible to compute. A proof of this fact is provided [herearrow-up-right](https://en.wikipedia.org/w/index.php?title=Kolmogorov_complexity#Uncomputability_of_Kolmogorov_complexity) . Practically, this means that it is impossible to write a "perfect" (optimal) compression algorithm, since we can't even compute the length of the shortest program! [Previous40.1 Models of Compressionchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.1-models-of-compression) [Next40.3 Space/Time-Bounded Compressionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.3-space-time-bounded-compression) Last updated 4 months ago * [Kolmogorov Complexity](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.2-optimal-compression-kolmogorov-complexity#kolmogorov-complexity) * [Languages and Complexity](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.2-optimal-compression-kolmogorov-complexity#languages-and-complexity) * [Uncomputability](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.2-optimal-compression-kolmogorov-complexity#uncomputability) --- # 26.1 MSTs and Cut Property | CS61B Textbook Fall 2025 Before we dive into the chapter, let's hear a couple words from Professor Hug on MSTs Let's do a quick warmup! Spanning Tree Definition Spanning Tree Usefulness MSTs vs SPTs [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.1-msts-and-cut-property#minimum-spanning-trees) Minimum Spanning Trees ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- A minimum spanning tree (MST) is the lightest set of edges in a graph possible such that all the vertices are connected. Because it is a tree, it must be connected and acyclic. And it is called "spanning" since all vertices are included. In this chapter, we will look at two algorithms that will help us find a MST from a graph. Before we do that, let's introduce ourselves to the Cut Property, which is a tool that is useful for finding MSTs. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.1-msts-and-cut-property#cut-property) Cut Property We can define a **cut** as an assignment of a graph’s nodes to two non-empty sets (i.e. we assign every node to either set number one or set number two). We can define a **crossing edge** as an edge which connects a node from one set to a node from the other set. With these two definitions, we can understand the **Cut Property**; given any cut, the minimum weight crossing edge is in the MST. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fassets%2FScreen%2520Shot%25202019-04-14%2520at%25208.57.22%2520PM.png&width=768&dpr=3&quality=100&sign=427a25c9&sv=2) The proof for the cut property is as follows: Suppose (for the sake of contradiction) that the minimum crossing edge _e_ were not in the MST. Since it is not a part of the MST, if we add that edge, a cycle will be created. Because there is a cycle, this implies that some other edge f must also be a crossing edge (for a cycle, if _e_ crosses from one set to another, there must be another edge that crosses back over to the first set). Thus, we can remove _f_ and keep _e_, and this will give us a lower weight spanning tree. But this is a contradiction because we supposedly started with a MST, but now we have a collection of edges which is a spanning tree but that weighs less, thus the original MST was not actually minimal. As a result, the cut property must hold. [Previous26\. Minimum Spanning Treeschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees) [Next26.2 Prim's Algorithmchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.2-prims-algorithm) Last updated 4 months ago * [Minimum Spanning Trees](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.1-msts-and-cut-property#minimum-spanning-trees) * [Cut Property](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.1-msts-and-cut-property#cut-property) --- # 10.2 Comparables and Comparators | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.2-encapsulation#comparables) Comparables ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ As a motivating example, consider the following Java code, where we want to try to find the largest dog in a list of dogs. Copy List dogs = new ArrayList<>(); dogs.add(new Dog("Grigometh", 200)); dogs.add(new Dog("Pelusa", 5)); dogs.add(new Dog("Clifford", 9000)); Dog maxDog = Collections.max(dogs); This code almost works, but there's one problem, Java doesn't know how to compare dogs. This will result in a fairly incomprehensible error message that "no instance(s) of type variable(s) T exist so that Dog conforms to Comparable". Unlike Python, where we defined a `__gt__` method to allow for comparison, Java doesn't have operator overloading. Instead, we must utilize implementation inheritance. Specifically, we'll implement the `Comparable` interface, given below: Copy public interface Comparable { int compareTo(T o); } As we saw earlier in class, the `compareTo` method returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second. If you're curious you can also look at the source for Comparator.java, which can be seen at [this linkarrow-up-right](https://github.com/openjdk/jdk/blob/ab66c82ce9fdb5ee3fd7690f42b8ad4d78bf5e40/src/java.base/share/classes/java/lang/Comparable.java#L105) . After implementing the `Comparable` interface, we end up with the code below: Copy public class Dog implements Comparable { ... @Override public int compareTo(Dog uddaDog) { if (size > uddaDog.size) { return 1; } if (size < uddaDog.size) { return -1; } return 0; } } After implementing this interface, `Collections.max(dogs)` will now work. While the code we wrote above works, it's a bit clumsy. An alternate approach is shown below: Here, we save some lines of code by simply subtracting the sizes. Returning a difference between two numbers is a common way to implement `compareTo` methods in Java. The flavor of **polymorphism** we’ve just employed is sometimes called **subtype polymorphism**. The idea here is that a supertype (`Comparable`) specifies the capability (in this case, comparison). Then a subtype (`Dog`) overrides the supertype’s abstract method. At runtime, Java decides what to do based on the type of the object that is invoking the method. To test your understanding of these ideas, try answering these two questions in the lecture slides: 1. [Question 1arrow-up-right](https://docs.google.com/presentation/d/1zLCqAei8OELHPV_v-kCyBWSkgotnkhzYsrQT8TCZHlY/edit#slide=id.g3336a39b10c_0_800) 2. [Question 2arrow-up-right](https://docs.google.com/presentation/d/1zLCqAei8OELHPV_v-kCyBWSkgotnkhzYsrQT8TCZHlY/edit#slide=id.g3336a39b10c_0_810) [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.2-encapsulation#comparators) Comparators ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ The term "Natural Order" is sometimes used to refer to the ordering implied by a `Comparable`'s `compareTo` method. Our compareTo method thinks of the size as the natural for the dogs. Sometimes we want to consider other ordering. For example, we might want to sort the dogs by name. In pPython, we achieve this goal using function passing. For example, in the code below, we pass `name_len` as a key function to `get_the_max`. By contrast, Java code doesn't typically use function passing for handling alernate orders. Instead, we rely again on **subtype polymorphism**. Specifically, we can implement the `Comparator` interface, given below: An example `Comparator` that compares dogs based on name is given below: To test your understanding of these ideas, consider this problem. What is the output of the code below? Is it a positive number? Negative number? Or zero? ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.2-encapsulation#using-a-comparator) Using a Comparator The code below shows an example of how we can pass a `Comparator` to `Collections.max`. This is similar to how we can pass a key function to `get_the_max` in Python. Notice the difference here. In Java, we package our comparison function inside of a `Comparator` object, i.e. we rely on subtype polymorphism. By contrast, in Python, we pass a comparison function to `get_the_max`, i.e. we rely on function passing. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.2-encapsulation#minor-improvements) Minor Improvements The NameComparator instantiation is awkward and aesthetically unpleasant (to me). It seems strange to have to instantiate a class just to pass a comparison function. One fix is to add a static variable reference to a pre-instantiated NameComparator. We do this in our Dog class. This allows us to pass `Dog.NAME_COMPARATOR` to `Collections.max` without having to instantiate a new `NameComparator` object. This results in code that looks like this: Is this actually better? You be the judge! ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.2-encapsulation#bonus-content) Bonus Content It is also possible to implement a `Comparator` using a lambda expression. This is shown below: This is a bit more concise, but it's also a bit more difficult to read. We won't expect you to learn lambda expression in this class. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.2-encapsulation#comparables-vs.-comparators) Comparables vs. Comparators -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- The Comparable interface specifies that a “natural order” exists. Instances of the class can compare themselves to other objects. Only one such order is possible. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fgithub.com%2FBerkeley-CS61B%2Ffa25-gitbook%2Fblob%2Fmain%2F.gitbook%2Fassets%2Fimage%2520%28162%29.png&width=768&dpr=3&quality=100&sign=c79fa2b2&sv=2) The Comparator interface is used to compare extrinsically (by other classes). May have many such classes, each specifying one such order. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fgithub.com%2FBerkeley-CS61B%2Ffa25-gitbook%2Fblob%2Fmain%2F.gitbook%2Fassets%2Fimage%2520%28163%29.png&width=768&dpr=3&quality=100&sign=b2214def&sv=2) [Previous10.1 Polymorphism vs. Function Passingchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.1-subtype-polymorphism-vs.-function-passing) [Next10.3 Writing a Max Functionchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.3-casting) Last updated 6 months ago * [Comparables](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.2-encapsulation#comparables) * [Comparators](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.2-encapsulation#comparators) * [Using a Comparator](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.2-encapsulation#using-a-comparator) * [Minor Improvements](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.2-encapsulation#minor-improvements) * [Bonus Content](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.2-encapsulation#bonus-content) * [Comparables vs. Comparators](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/10.-inheritance-ii-extends-casting-higher-order-functions/10.2-encapsulation#comparables-vs.-comparators) Copy public class Dog implements Comparable { ... @Override public int compareTo(Dog uddaDog) { return this.size - uddaDog.size; } } Copy def get_the_max(x, key): max_value = x[0] for item in x: if key(item) > key(max_value): max_value = item return max_value def name_len(dog): return len(dog.name) max_dog=get_the_max(doglist, name_len) Copy public interface Comparator { int compare(T o1, T o2); } Copy public static class NameComparator implements Comparator { @Override public int compare(Dog a, Dog b) { return a.name.compareTo(b.name); } } Copy Dog a = new Dog("Frank", 1); Dog b = new Dog("Zeke", 1); Comparator nc = new Dog.NameComparator(); System.out.println(nc.compare(a, b)); Copy List dogs = new ArrayList<>(); dogs.add(new Dog("Grigometh", 200)); dogs.add(new Dog("Pelusa", 5)); dogs.add(new Dog("Clifford", 9000)); Dog maxNameDog = Collections.max(dogs, new Dog.NameComparator()); Copy def length_of_name(dog): return len(dog.name) dogs = [Dog("Grigometh", 10),\ Dog("Pelusa", 5),\ Dog("Clifford", 9000)] max_dog = get_the_max(dogs, length_of_name) Copy public class Dog { ... public static final Comparator NAME_COMPARATOR = new NameComparator(); } Copy List dogs = new ArrayList<>(); dogs.add(new Dog("Grigometh", 200)); dogs.add(new Dog("Pelusa", 5)); dogs.add(new Dog("Clifford", 9000)); Dog maxNameDog = Collections.max(dogs, Dog.NAME_COMPARATOR); Copy List dogs = new ArrayList<>(); dogs.add(new Dog("Grigometh", 200)); dogs.add(new Dog("Pelusa", 5)); dogs.add(new Dog("Clifford", 9000)); Comparator dc = (a, b) -> a.name.compareTo(b.name); Dog maxNameDog = Collections.max(dogs, dc); --- # 21.2 Distribution By Other Hash Functions | CS61B Textbook Fall 2025 Distribution by other Hash Functions Suppose our `hashCode()` implementation simply returns 0. Copy @Override public int hashCode() { return 0; } chevron-rightWhat distribution do we expect?[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/21.-hashing-ii/21.2-distribution-by-other-hash-functions#what-distribution-do-we-expect) We would expect all of the items in our Hash Table to be in bucket 0. As we discussed in the [previous sectionarrow-up-right](https://github.com/Berkeley-CS61B/fa25-gitbook/blob/main/21.-hashing-ii/20.1-hash-table-recap-default-hash-function.md) , our Hash Table would place all elements in the 0th bucket because the hashCode tells it to. In the 0th bucket, there will be a LinkedList of all elements from the data yielding a very inefficient linear lookup time compared to the constant time we are expecting. No matter what key we provide, our hashCode always tells the HashMap to only add to the 0th bucket which is why we get this long LinkedList. So what do we do to make sure we get constant lookup time? We use a better hash function! In order to get a more even distribution, what we can do is something to what we tried in the [previous sectionarrow-up-right](https://github.com/Berkeley-CS61B/fa25-gitbook/blob/main/21.-hashing-ii/20.1-hash-table-recap-default-hash-function.md) where we utilize modulo. Let's say that we define the size of our Hash Table to have 4 buckets. This means it has 4 corresponding LinkedLists and 4 bucket indices labeled {0, 1, 2, 3}. The modding is not required in our `hashCode()` function as it is being done for us in the hash table to guarantee we can add to that bucket. As we said the hash code could really be any integer in the range of 4 billion unique values! By using modulo, we ensure that our hashcode yields a number that can be represented as an index and clearly identifies which LinkedList to add to. Additionally, when adding a series of numbers at once, we see that we get an even distribution of numbers in our LinkedList yielding a constant lookup time. This hash function should yield a much more even distribution! Objects with different `num` will now be more spread out across the buckets instead of all living in the 0th bucket. If our class does not explicitly override the `hashCode()` function, Java will use the default implementation, which returns the object's address in memory as its hash code! [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/21.-hashing-ii/21.2-distribution-by-other-hash-functions#why-bother-with-custom-hash-functions) Why Bother With Custom Hash Functions? --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Let's discuss if the default hashCode function is a good hashCode function! It actually is a good spread as it relies on the fact that different objects will live in different places in the memory, and the memory address is effectively random. We will get a good distribution, since objects are basically assigned random indices to insert into the hash table. This really raises an interesting question: why do we care about other custom hash functions when the default hashcode gets good spread? We'll read about this in the [next section.arrow-up-right](https://github.com/Berkeley-CS61B/fa25-gitbook/blob/main/21.-hashing-ii/20.3-contains-and-duplicate-items.md) [Previous21.1 Hash Table Recap, Default Hash Functionchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/21.-hashing-ii/21.1-hash-table-recap-default-hash-function) [Next21.3 Contains & Duplicate Itemschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/21.-hashing-ii/21.3-contains-and-duplicate-items) Last updated 4 months ago Copy @Override public int hashCode() { return num; } --- # 13.3 Checkpoint: An Exercise | CS61B Textbook Fall 2025 Exercise: Apply techniques 2A and 2B to `dup2`. * Calculate the counts of each operation for the following code with respect to N. * Predict the _**rough**_ magnitudes of each one. Copy for (int i = 0; i < A.length - 1; i += 1){ if (A[i] == A[i + 1]) { return true; } } return false; #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.3-checkpoint-an-exercise#solution) Solution: Note: It's okay if you were slightly off—as mentioned earlier, you want _**rough**_ estimates. Operation Symbolic Count Count (for N=10000) i = 0 1 1 j = i+1 0 to NNN 0 to 10,000 < 0 to N−1N-1N−1 0 to 9,999 \== 1 to N−1N-1N−1 1 to 9,999 array accesses 2 to 2N−22N-22N−2 2 to 19998 "I have another problem for you to solve ( ͡° ͜ʖ ͡°)..." - Josh Hug Let us compare the `dup1` table with the `dup2` table: `dup1` table: Operation Symbolic Count Count (for N=10000) i = 0 1 1 j = i+1 1 to NNN 1 (in the best case) to 10000 (in the worst case) < 2 to (N2+3N+2)/2(N² + 3N + 2)/2(N2+3N+2)/2 2 to 50,015,001 += 1 0 to (N2+N)/2(N² + N)/2(N2+N)/2 0 to 50,005,000 \== 1 to (N2−N)/2(N² - N)/2(N2−N)/2 1 to 49,995,000 array accesses 2 to N2−NN²-NN2−N 2 to 99,990,000 `dup2` table: Operation Symbolic Count Count (for N=10000) i = 0 1 1 j = i+1 0 to NNN 0 to 10,000 < 0 to N−1N-1N−1 0 to 9,999 \== 1 to N−1N-1N−1 1 to 9,999 array accesses 2 to 2N−22N-22N−2 2 to 19998 We can see that `dup2` performs significantly better than `dup1` in the worst case! One way to rationalize this is that it takes fewer operations for `dup2` to accomplish the same goal as `dup1`. A better realization is that the algorithm for `dup2` scales much better in the worst case (e.g. (N2+3N+2)/2N² + 3N + 2)/2N2+3N+2)/2 vs NNN) An even **better** realization is that parabolas (N2N²N2) always grow faster than lines (NNN). [Previous13.2 Runtime Characterizationchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.2-runtime-characterization) [Next13.4 Asymptotic Behaviorchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.4-asymptotic-behavior) Last updated 6 months ago --- # 27.2 Trie Implementation | CS61B Textbook Fall 2025 Now, we will walk through an implementation of a Trie. We'll take a first approach with the idea that each node stores a letter, its children, and a color. Since we know each node key is a character, we can use our `DataIndexedCharMap` class we defined earlier to map to all of a nodes' children. Remember that each node can have at most the number of possible characters as its number of children. Copy public class TrieSet { private static final int R = 128; // ASCII private Node root; // root of trie private static class Node { private char ch; private boolean isKey; private DataIndexedCharMap next; private Node(char c, boolean blue, int R) { ch = c; isKey = blue; next = new DataIndexedCharMap(R); } } } Note that for any given node, the DataIndexedCharMap object for that node will have mostly null values if nodes in our tree have relatively few children. For a node with only one child, we will have 128 links with 127 equal to null and 1 being used. This means that we are wasting a lot of excess space! We will explore alternative representations further on. From this, we can make another important observation: each link corresponds to a character if and only if that character **exists**. Therefore, we can remove the Node's character variable and instead base the value of the character from its position in the parent `DataIndexedCharMap`. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.2-trie-implementation#performance) Performance Given a Trie with N keys the runtime for our Map/Set operations are as follows: * `add`: Θ(1)\\Theta (1)Θ(1) * `contains`: Θ(1)\\Theta (1)Θ(1) Why is this the case? It doesn't matter how many items we have in our Trie, the runtime will always be _independent_ of the number of keys. We only traverse the length of one key in the worst case ever, which is unrelated to the number of keys in the Trie. Therefore, let's analyze the runtime with a more appropriate measurement: L, the length of the key we are searching for: * `add`: Θ(L)\\Theta (L)Θ(L) * `contains`: Θ(L)\\Theta (L)Θ(L) We have achieved constant runtime without having to worry about amortized resizing times or having an even spreading of keys! Our only issue is that as we mentioned above, our current design is extremely wasteful memory-wise since each node contains an array for every single character even if that character doesn't exist. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.2-trie-implementation#improvement-child-tracking) Improvement: Child Tracking To address the issue of wasted space, let us explore two possible solutions: * _Alternate Idea #1_: Hash-Table based Trie. This won't create an array of 128 spots but instead initialize the default value and resize the array only when necessary with the load factor. * _Alternate Idea #2_: BST based Trie. Again this will only create children pointers when necessary, and we will store the children in the BST. We will have to worry about the runtime for searching in this BST, but this is not a bad approach. When we implement a Trie, we have to pick a map to our children. A Map is an ADT, so we must also choose the underlying implementation for the map. What does this reiterate to us? There is an **abstraction** barrier between the implementations and the ADT that we are trying to create. This abstraction barrier allows us to take advantage of what each implementation has to offer when we try to meet the ADT behavior. Let's consider each advantage: * DataIndexedCharMap * Space: 128 links per node * Runtime: Θ(1)\\Theta (1)Θ(1) * BST * Space: CCC links per node, where CCC is the number of children * Runtime: O(logRRR), where RRR is the size of the alphabet * Hash Table * Space: CCC links per node, where CCC is the number of children * Runtime: O(RRR), where RRR is the size of the alphabet Note: Cost per link is higher in BST and Hash Tables; R is a fixed number (this means we can think of the runtimes as constant) We can takeaway a couple of things. There is a slight memory and efficiency trade off (with BST/Hash Tables vs. DataIndexedCharMap). The runtimes for Trie operations are still constant without any caveats. Tries will especially thrive with some special operations. [Previous27.1 Introduction to Trieschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.1-introduction-to-tries) [Next27.3 Trie String Operationschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.3-trie-string-operations) Last updated 4 months ago * [Performance](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.2-trie-implementation#performance) * [Improvement: Child Tracking](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries/27.2-trie-implementation#improvement-child-tracking) Copy public class TrieSet { private static final int R = 128; // ASCII private Node root; // root of trie private static class Node { // no more 'ch' instance variable private boolean isKey; private DataIndexedCharMap next; private Node(boolean blue, int R) { isKey = blue; next = new DataIndexedCharMap(R); } } } --- # 33.4 Summary | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.4-summary#quicksort) Quicksort ---------------------------------------------------------------------------------------------------------------------------------------- What can we do to avoid the worst case runtime of θ(N2)\\theta(N^2)θ(N2) for Quicksort? 1. Randomness: pick a random pivot point, shuffle items before sorting 2. Smarter Pivot Selection: constant time pivot pick (ex: pick a few and then choose the best out of them), linear time pivot pick like (ex: median) 3. Introspection: switch to a different sort if current sort hits recursion depth threshold Quicksort vs. Mergesort * Mergesort is faster than Quicksort L3S (leftmost pivot, 3-scan partition) and QuickSort PickTH (median pivot, Tony Hoare partition) * Quicksort LTHS (leftmost pivot, Tony Hoare partition) is faster than Mergesort! * Tony Hoare's partitioning: * two pointers, one at each end of the items array, walking towards each other * left pointer hates larger or equal items, right pointer hates smaller or equal items * swapping anything they don’t like; stop when the two pointers cross each other * new pivot = Item at right pointer. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.4-summary#quick-select) Quick Select ---------------------------------------------------------------------------------------------------------------------------------------------- Quick select helps us quickly find the median with partitioning. Median of an length n array will be around index n /2. 1. Initialize array with the leftmost item as the pivot. 2. Partition around pivot. 3. Partition the subproblem. Repeat the process. 4. Stop when the pivot is at the median index. Expected runtime: θ(N)\\theta(N)θ(N). Worst case runtime (when array is in sorted order): θ(N2)\\theta(N^2)θ(N2). [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.4-summary#stability-adaptiveness-and-optimization) Stability, Adaptiveness, and Optimization ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ Stability: A sort is stable if the order of equivalent elements is preserved. Optimizations: * Adaptiveness - sort that exploits the existing order of the array. * Switch to Insertion Sort - when a subproblem reaches size 15 or lower * Exploit restrictions on set of keys * Switch from QuickSort - if the recursion goes too deep [Previous33.3 Stability, Adaptiveness, and Optimizationchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.3-stability-adaptiveness-and-optimization) [Next33.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.5-exercises) Last updated 4 months ago * [Quicksort](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.4-summary#quicksort) * [Quick Select](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.4-summary#quick-select) * [Stability, Adaptiveness, and Optimization](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/33.-more-quick-sort-sorting-summary/33.4-summary#stability-adaptiveness-and-optimization) --- # 32.3 Modular Design | CS61B Textbook Fall 2025 Modular design is a powerful tool for managing complexity because it divides the project complexity into manageable pieces. One way to implement modular design is to create helper methods or interfaces. This way, the programmer can individually handle each component of complexity rather than having to always keep track of the details of every piece of code that they write. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.3-modular-design#modules-should-be-simple) Modules should be simple: In an ideal world, every module is totally independent from one another. Unfortunately, this is not possible because code from each module needs to call other modules. However, we can still try to minimize these dependencies between modules! In other words, we want to minimize how many _things_ you need to know about a given module in order to use it. This is exactly what we mean when we talk about the difference between _implementation_ versus _interface_. A good module will not require the user to know the specific implementation in order to use it. Rather, it should be sufficient to just know the interface of the module. Changing the implementation of a module should not affect the interface. John Ousterhout once said: "The best modules are those whose interfaces are much simpler than their implementation." This is a good rule to swear by, and putting it into practice will save a lot of headache. One other technique of minimizing complexity is to restrict what the user can do. If a user does not _need_ to interact with an instance variable, then don't give them access to it. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.3-modular-design#interface-rules) Interface rules: Interfaces have a further set of rules. These rules are divided into _formal_ and _informal_ rules. The difference between the two is that informal rules are not enforced by the compiler. Formal rules are the list of method signatures. If a method is not implemented in a class that implements the interface, then the compiler will give an error. Some examples of informal rules are: * If your iterator class does not call hasNext() on its own (for some reason) and instead requires the user to call it. * Any exceptions that are thrown. * Any runtime specifications. Be especially wary of informal rules! They are hard to keep track of. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.3-modular-design#modules-should-be-deep) Modules should be deep Another cool idea is that Modules should be deep. Their simple interfaces but powerful functionality. We do this a lot like thats the 61B story! A set for example is a deep module that has power functioanlity and simple interfaces. So Red Black BSTs is very deep. I can add, contain, and delete, and there is nothing informal I need to know, it's all under the hood. Powerful functionality means that all operations are efficient. Tree balancing is maintained using sophisticated yet subtle rules. They are tricky and we hide them under the surface. The most important way to keep modules deep is by practicing information hiding. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.3-modular-design#information-hiding) Information Hiding That is, make your variables private, don't let anyone see what's inside the module as much as you can. Embed all the cleverness inside the modules. So that will keep your interfaces simple. And also it would keep it easy to modify your system. If I made a mistake, I can go fix that without thinking about it in another context. The opposite of hiding information is leaking information. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.3-modular-design#leaking-information) Leaking Information This occurs when design decisions are reflected across multiple modules. * Any change to one module requires a change to all modules * Information leakage is one of the most important red flags in software design * One of the best skills you can learn as a software designed is a high level of sensitivity to information leakage ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.3-modular-design#temporal-decomposotion) Temporal Decomposotion One of the biggest causes of information leaking is "temporal decomposition," especially in BYOW. The structure of your system very much reflects the order in which events occur. For example, student often do the following in BYOW: * Game is started with an input string, so call interactWithInputString() * Parse the String and find the seed by extracting N#####S (example code that contains the seed.)¹ * Generate the world * Process each character using move(World, char) * etc. * Game is started with no input String, so call interactWithKeyboard * Display a menu and collect the seed (number we are using to generate the world).¹ * Generate the world * Until done, call moveWithKeyboard(World) * etc. ¹ Because the temporal discussion of when you worked on the project and the temporal decomposition of when these things happen, you don't really recognize that they should be sharing code that collects and extracts the seed for example. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.3-modular-design#summary) Summary * Buld classes that provide functionality needed in many places in your code. * Create deep modules, classes with simple interfaces that do complicated things * avoid over-reliance on temporal decomposition where your decomposition is driven primarily by the order in which things occur. * It’s OK to use some temporal decomposition, but try to fix any information leakage that occurs! * Be strategic, not tactical. * Most importantly: Hide information from yourself when unneeded! [Previous32.2 Sources of Complexitychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.2-sources-of-complexity) [Next32.4 Teamworkchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.4-teamwork) Last updated 4 months ago * [Modules should be simple:](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.3-modular-design#modules-should-be-simple) * [Interface rules:](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.3-modular-design#interface-rules) * [Modules should be deep](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.3-modular-design#modules-should-be-deep) * [Information Hiding](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.3-modular-design#information-hiding) * [Leaking Information](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.3-modular-design#leaking-information) * [Temporal Decomposotion](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.3-modular-design#temporal-decomposotion) * [Summary](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/32.-software-engineering-ii/32.3-modular-design#summary) --- # 28.2 Complexity | CS61B Textbook Fall 2025 ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.2-complexity#restrictions-of-engineering) Restrictions of Engineering In other engineering disciplines, we are subject to the laws of nature. Objects have limits on how fast they can move, on how dense they can be, on how much of it there is. * Chemical engineers worry about temperature * Material scientists worry about how brittle material is * Civil engineers worry about the strength of concrete However, in computer science, we've solved most of these constraints already - the sum power of Apollo missions to get us to the moon is less than the computing power of your phone. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.2-complexity#the-power-of-software) The Power of Software Computers have evolved over time from being large calculators to fine-tuned machines to being multi-purpose and powerful. Video games, for example, used to be customized for the limitations of operating systems but now can be built in frameworks and abstractions. From this, the limitation is no longer the limit of computing power; it is from the ways that we plan and design what we build. Further: * An individual programmer is no longer able to effectively manage the entire software system for a large project * Spotify, for example, [has over a billion lines of code and 60 million used in productionarrow-up-right](https://engineering.atspotify.com/2023/04/spotifys-shift-to-a-fleet-first-mindset-part-1/) * Any one programmer should only need to understand a fraction of the codebase ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.2-complexity#a-definition-of-complexity) A Definition of Complexity “_Anything related to the structure of a software system that makes it hard to understand and modify it_” - John Ousterhout, “A Philosophy of Software Design” As programs have more features and functionality, their complexity increases exponentially. Consider Spotify adding a queue feature; it has to work, but it also needs to work with everything already implemented such as play/pause, search, skip, etc. Complex systems are not a goal; our goal is to keep software simple. Complex systems: * Take longer to understand how code works * Are more difficult to fix bugs with confidence * Harder to find what needs to change * Unknown unknowns: unclear what needs to be known to make modifications * Very common in large codebases ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.2-complexity#managing-complexity) Managing Complexity There are two kinds of complexity: * Unavoidable (Essential) Complexity * To implement certain features, that feature carries some level of inherent complexity with it * Avoidable Complexity * Complexity that we can address with our choices In response to avoidable complexity, we can: * Make code simpler and more obvious * Using sentinel nodes in Project 1 made life significantly easier to avoid dealing with edge cases * Modules as a means of abstraction: the ability to use a piece without understanding how it works based on some specification * Interfaces are an example - HashMap, BSTMap from lab are both Maps and can be used with `get` and `put` for some key-value pairs without understanding the underlying implementation [Previous28.1 Introduction to Software Engineeringchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.1-introduction-to-software-engineering) [Next28.3 Strategic vs Tactical Programmingchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.3-strategic-vs-tactical-programming) Last updated 4 months ago * [Restrictions of Engineering](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.2-complexity#restrictions-of-engineering) * [The Power of Software](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.2-complexity#the-power-of-software) * [A Definition of Complexity](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.2-complexity#a-definition-of-complexity) * [Managing Complexity](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/28.-software-engineering-i/28.2-complexity#managing-complexity) --- # 37.2 The Just-In-Time Compiler | CS61B Textbook Fall 2025 Java’s Just-In-Time Compiler secretly optimizes your code when it runs. * The code you write is not necessarily the code that executes! * As your code runs, the “interpreter” is watching everything that happens. * If some segment of code is called many times, the interpreter actually studies and re-implements your code based on what it learned by watching WHILE ITS RUNNING (!!). * Example: Performing calculations whose results are unused. * See [this videoarrow-up-right](https://www.youtube.com/watch?v=oH4_unx8eJQ) if you’re curious. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-d69b9235b7cb8e406d4b6410fddf556f728cdec6%252Fimage%2520%28143%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=b04d2157&sv=2) [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/37.-sorting-and-data-structures-conclusion/37.2-the-just-in-time-compiler#jit-example) JIT Example --------------------------------------------------------------------------------------------------------------------------------------------------------------------- The code below creates Linked Lists, 1000 at a time. * Repeating this 500 times yields an interesting result. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-0e8f3129918b2af7712c2998ca93f4392f55d388%252Fimage%2520%2867%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=eeae8b22&sv=2) * First optimization: Not sure what it does. * Second optimization: Stops creating linked lists since we’re not actually using them. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-72f251c71385275e10611f98f0ca2fc56b298e97%252Fimage%2520%2895%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=43939b6&sv=2) [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/37.-sorting-and-data-structures-conclusion/37.2-the-just-in-time-compiler#so-which-is-better-msd-or-mergesort) … So Which is Better? MSD or MergeSort? ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- The performance of the merge sort algorithm is highly dependent on the presence of just-in-time (JIT) compilation. Specifically, when JIT is enabled, merge sort is faster in cases where the strings being sorted are equal, but slower when JIT is disabled. This suggests that merge sort is generally more effective for this specific case, given that JIT is typically enabled. However, there are numerous other scenarios to consider, including the sorting of almost equal strings, randomized strings, and real-world data from specific datasets. When assessing the effectiveness of merge sort for these alternative cases, it is important to conduct careful experimentation and profiling to determine which sorting algorithm is most suitable. The lectureCode repository provides code for running these experiments, allowing for more precise assessment of algorithm performance under various conditions. Ultimately, real-world applications will require a thorough analysis of different implementations on actual data in order to select the optimal algorithm for the task at hand. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/37.-sorting-and-data-structures-conclusion/37.2-the-just-in-time-compiler#bottom-line-algorithms-can-be-hard-to-compare) Bottom Line: Algorithms Can Be Hard to Compare ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ Comparing algorithms that have the same order of growth can be a difficult task, as it requires conducting computational experiments to determine their relative performance. However, modern programming environments can introduce additional challenges to this process, as certain optimizations such as just-in-time (JIT) compilation in Java can impact the results of experiments. It is worth noting that even small optimizations to an algorithm can have a significant impact on its performance. For instance, a change to the Quicksort algorithm suggested by Vladimir Yaroslavskiy has been shown to provide notable improvements, as discussed briefly in the Quicksort lecture. Therefore, when comparing algorithms with similar growth rates, it is crucial to remain vigilant of potential optimizations and variations that may influence their performance. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/37.-sorting-and-data-structures-conclusion/37.2-the-just-in-time-compiler#jit-compilers-are-always-evolving) JIT Compilers Are Always Evolving ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- The JIT (just-in-time) compiler is a highly complex and essential component of modern compilers, and is an active area of research and development in this field. However, the older JIT compiler known as C2 has become increasingly difficult to maintain and extend, with no major improvements implemented in recent years. The codebase for C2 is written in a specific dialect of C++, making it challenging for new engineers to understand and work with. As a result, the codebase is being abandoned in favor of newer and more maintainable alternatives. For individuals interested in this area of study, CS164 offers a course on compilers and there are opportunities for involvement in ongoing research. It is worth noting that the reasons for the improved performance of merge sort with the JIT are not yet fully understood, making it an interesting topic for further research. [Previous37.1 Radix vs. Comparison Sortingchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/37.-sorting-and-data-structures-conclusion/37.1-radix-vs.-comparison-sorting) [Next37.3 Radix Sorting Integerschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/37.-sorting-and-data-structures-conclusion/37.3-radix-sorting-integers) Last updated 4 months ago * [JIT Example](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/37.-sorting-and-data-structures-conclusion/37.2-the-just-in-time-compiler#jit-example) * [… So Which is Better? MSD or MergeSort?](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/37.-sorting-and-data-structures-conclusion/37.2-the-just-in-time-compiler#so-which-is-better-msd-or-mergesort) * [Bottom Line: Algorithms Can Be Hard to Compare](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/37.-sorting-and-data-structures-conclusion/37.2-the-just-in-time-compiler#bottom-line-algorithms-can-be-hard-to-compare) * [JIT Compilers Are Always Evolving](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/37.-sorting-and-data-structures-conclusion/37.2-the-just-in-time-compiler#jit-compilers-are-always-evolving) --- # 17.3 Mergesort | CS61B Textbook Fall 2025 In our last example, we'll analyze merge sort, another cool sorting algorithm. Mergesort basics: merging two sorted lists. First, let's remind ourselves of selection sort, which we will initially use as a building block for merge sort. Selection sort works off two basic steps: * Find the smallest item among the unsorted items, move it to the front, and ‘fix’ it in place. * Sort the remaining unsorted/unfixed items using selection sort. If we analyze selection sort, we see that its runtime is Θ(N2)\\Theta(N^2)Θ(N2). **Exercise:** To convince yourself that selection sort has Θ(N2)\\Theta(N^2)Θ(N2) runtime, work through the geometric approach (try drawing out the state of the list at every sort call), or count the operations. Let's introduce one other idea here: **arbitrary units of time**. While the exact time something will take will depend on the machine, on the particular operations, etc., we can get a general sense of time through our arbitrary units (AU). If we run an N\=6N=6N\=6 selection sort, and the runtime is of order N2N^2N2​​, it will take ~36 AU to run. If N\=64N=64N\=64, it'll take ~2048 AU to run. Now we don't know if that's 2048 nanoseconds, or seconds, or years, but we can get a relative sense of the time needed for each size of NNN. Hold onto this thought for later analysis. Now that we have selection sort, let's talk about **merging.** Say we have two **sorted** arrays that we want to combine into a single big sorted array. We could append one to the other, and then re-sort it, but that doesn't make use of the fact that each individual array is already sorted. How can we use this to our advantage? It turns out, we can merge them more quickly using the sorted property. The smallest element must be at the start of one of the two lists. So let's compare those, and put the smallest element at the start of our new list. Now, the next smallest element has to be at the new start of one of the two lists. We can continue comparing the first two elements and moving the smallest into place until one list is empty, then copy the rest of the other list over into the end of the new list. To see an animation of this idea, [go herearrow-up-right](https://docs.google.com/presentation/d/1mdCppuWQfKG5JUBHAMHPgbSv326JtCi5mvjH1-6XcMw/edit#slide=id.g463de7561_042) . What is the runtime of the merge operation? We can use the number of "write" operations to the new list as our cost model, and count the operations. Since we have to write each element of each list only once, the runtime is Θ(N)\\Theta(N)Θ(N). Selection sort is slow, and merging is fast. How do we combine these to make sorting faster? A closer look at Mergesort We noticed earlier that doing selection sort on an N\=64N=64N\=64 list will take ~2048 AU. But if we sort a list half that big, N\=32N=32N\=32, it only takes ~512 AU. That's more than twice as fast! So making the arrays we sort smaller has big time savings. Having two sorted arrays is a good step, but we need to put them together. Luckily, we have merge. Merge, being of linear runtime, only takes ~64 AU. So in total, splitting it in half, sorting, then merging, only takes 512 + 512 + 64 = 1088 AU. Faster than selection sorting the whole array. But how much faster? Now, AUs aren't real units, but they're sometimes easier and more intuitive than looking at the runtime. The runtime for our split-in-half-then-merge-them sort is N+2(N2)2N+2(\\frac{N}{2})^2N+2(2N​)2​​, which is about half of N2N^2N2 for selection sort. However, they are still both Θ(N2)\\Theta(N^2)Θ(N2). What if we halved the arrays again? Will it get better? Yes! If we do two layers of merges, starting with lists of size N4\\frac{N}{4}4N​, the total time will be ~640 AU. **Exercise:** Show why the time is ~640AU by calculating the time to sort each sub-list and then merge them into one array. What if we halved it again? And again? And again? Eventually we'll reach lists of size 1. At that point, we don't even have to use selection sort, because a list with one element is already sorted. This is the essence of **merge sort:** * If the list is size 1, return. Otherwise: * Mergesort the left half * Mergesort the right half * Merge the results So what's the running time of **merge sort**? We know merge itself is order NNN, so we can start by looking at each layer of merging: * To get the top layer: merge ~64 elements = 64 AU * Second layer: merge ~32 elements, twice = 64 AU * Third layer: ~16\*4 = 64 AU * ... Overall runtime in AU is ~64\*k, where kkk is the number of layers. Here, k\=log2(64)\=6k=log\_{2}(64)=6k\=log2​(64)\=6, so the overall cost of mergesort is ~384 AU. Now, we saw earlier that splitting up more layers was faster, but still order N2N^2N2​​. Is merge sort faster than N2N^2N2​​? Yes! Mergesort has worst case runtime Θ(N∗log(N))\\Theta(N\*log(N))Θ(N∗log(N)). * The top level takes ~N AU. * Next level takes ~N/2 + ~N/2 = ~N. * One more level down: ~N/4 + ~N/4 + ~N/4 + ~N/4 = ~N. Thus, total runtime is ~Nk, where kkk is the number of levels. How many levels are there? We split the array until it is length 1, so k\=log2(N)k=log\_{2}(N)k\=log2​(N). Thus the overall runtime is Θ(N∗log(N))\\Theta(N\*log(N))Θ(N∗log(N)). **Exercise:** Use exact counts to argue for Θ(N∗log(N))\\Theta(N\*log(N))Θ(N∗log(N)). Account for cases where we cannot divide the list perfectly in half. So is Θ(N∗log(N))\\Theta(N\*log(N))Θ(N∗log(N)) actually better than Θ(N2)\\Theta(N^2)Θ(N2)? Yes! It turns out Θ(N∗log(N))\\Theta(N\*log(N))Θ(N∗log(N)) is not much slower than linear time. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fassets%2Ftimetable.png&width=768&dpr=3&quality=100&sign=94353876&sv=2) timing\_table\_for\_runtimes [Previous17.2 Binary Searchchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/17.-asymptotics-iii/17.2-binary-search) [Next17.4 B-trees Big Ochevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/17.-asymptotics-iii/17.4-b-trees-big-o) Last updated 4 months ago --- # 20.1.2 A second attempt: DataIndexedWordSet | CS61B Textbook Fall 2025 Our `DataIndexedIntegerSet` only allowed for integers, but now we want to insert the `String` `"cat"` into it. We'll call our data structure that can insert strings `DataIntexedEnglishWordSet` Here's a crazy idea: let's give every string a number. Maybe "cat" can be `1`, "dog" can be `2` and "turtle" can be `3`. (The way this would work is –– if someone wanted to add a "cat" to our data structure, we would 'figure out' that the number for "cat" is 1, and then set `present[1]` to be `true`. If someone wanted to ask us if "cat" is in our data structure, we would 'figure out' that "cat" is 1, and check if `present[1]` is true.) But then if someone tries to insert the word "potatocactus", we don't know what to do. We need to develop a general strategy so that given a string, we can figure out a number representation for it. Here are the two main strategies we chose to use: #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.1-introduction-to-hashing-data-indexed-arrays/20.1.2-a-second-attempt-dataindexedwordset#strategy-1-use-the-first-letter) Strategy 1: Use the first letter. A simple idea is to just use the first character of any given string to convert it to its number representation. However, if someone tried to insert two words with the same first letter, we have a **collision**, which we deal with using the next strategy. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.1-introduction-to-hashing-data-indexed-arrays/20.1.2-a-second-attempt-dataindexedwordset#strategy-1-use-the-first-letter-1) Strategy 2: Avoid Collisions. There are 262626 unique characters in the English lowercase alphabet. We assign each one a number: a\=1,b\=2,...,z\=26a=1, b=2, ...,z=26a\=1,b\=2,...,z\=26. Now, we can write any unique lowercase string in **base 26**. (Note that **base 26** simply means that we will use **26** as the multiplier, much like we used **10** and **2** as examples above.) * ‘‘cat"\=3∗262+1∗261+20∗260\`\`cat"=3\*26^2+1\*26^1+20 \* 26^0‘‘cat"\=3∗262+1∗261+20∗260 **This representation gives a unique integer to every English word containing lowercase letters, much like using base 10 gives a unique representation to every number. We are guaranteed to not have collisions.**\\ [Previous20.1.1 A first attempt: DataIndexedIntegerSetchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.1-introduction-to-hashing-data-indexed-arrays/20.1.1-a-first-attempt-dataindexedintegerset) [Next20.1.3 A third attempt: DataIndexedStringSetchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.1-introduction-to-hashing-data-indexed-arrays/20.1.3-a-third-attempt-dataindexedstringset) Last updated 4 months ago --- # 18.6 Summary | CS61B Textbook Fall 2025 * BSTs have best-case height Θ(log⁡N)\\Theta(\\log N)Θ(logN), and worst-case height Θ(N)\\Theta(N)Θ(N). * Big O is _not_ the same as worst-case! * B-Trees are a modification of the BST that maintain Θ(log⁡N)\\Theta(\\log N)Θ(logN) runtime for `add` and `contains` in the worst case. They maintain perfect balance during insertion. * A B-Tree has a limit LLL on the number of values a node can hold, instead of having one item per node like a BST. * Upon `add` in a B-Tree, we simply append the value to an existing leaf node in the correct location instead of creating a new leaf node. If the node is too full, it splits and pushes a value up. [Previous18.5 B-Tree Performancechevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.5-b-tree-performance) [Next18.7 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.7-exercises) Last updated 4 months ago --- # 29.4 Reductions and Decomposition | CS61B Textbook Fall 2025 Recall in previous section that to solve one problem (longest paths), we created a new graph G' and fed it into a different algorithm and then interpreted the result. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-bfb5e60fec78e903c250182f92464b6e40296ca8%252Fimage%2520%28103%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=55735dbf&sv=2) This process is known as **reduction**. Since DAG-SPT can be used to solve DAG-LPT, we say that "DAG-LPT reduces to DAG-SPT." In other words, the problem of DAG-LPT can be reduced to the problem of DAG-SPT. A problem like DAG-LPT can potentially be reduced to multiple other problems. As a real-world analogy, consider climbing a hill. There are many ways we can solve the problem of "climbing a hill." * "Climbing a hill" reduces to "riding a ski lift" * "Climbing a hill" reduces to "being shot out of a cannon" * "Climbing a hill" reduces to "riding a bike up the hill" Formally, **if any subroutine for task Q can be used to solve P, we say P reduces to Q.** This definition is visualized below: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-49c558bde72caee853c4e498b1a97579aee903ba%252Fimage%2520%28119%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=38b18036&sv=2) Note that this is simply a generalization of the first graphic on this page. P reduces to Q since Q is used to solve P. This works by preprocessing the input �x into �y, running the algorithm Q on �y, and postprocessing the output into a solution for P. This is what we did for reducing DAG-LPT to DAG-SPT. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/29.-reductions-and-decomposition/29.4-reductions-and-decomposition#example) Example Here we'll show how one problem can reduce to a seemingly unrelated different problem. First, the two problems: **Independent Set Problem** An independent set is a set of vertices in which no two vertices are adjacent. The Independent Set Problem: Does there exist an independent set of size k? In other words, can we color k vertices red, such that none touch? ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-3702aee9df88ca0cff2f8a452763cb6dbc639885%252Fimage%2520%2816%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=4a3a25c0&sv=2) Example of independent sets solutions for k=2 and k=4 **3SAT Problem** What values of `x1`, `x2`, `x3`, `x4` satisfy the following boolean formula: `(x1 || x2 || !x3) && (x1 || !x1 || x1) && (x2 || x3 || x4)`? The 3SAT Problem: Given a boolean formula, does there exist a truth value for boolean variables that obeys a set of 3-variable disjunctive constraints? Terminology clarification: * Constraints are True/False values. * **Disjunctive** means separated by OR. 3SAT has a set of "clauses," each made up of 3 literals with each literal separated by an OR. For example, the first clause above is `(x1 || x2 || !x3)`. * In the 3SAT problem we must satisfy the entire set of clauses (combine each clause with AND). **e.g.:** `(x1 || x2 || !x3) && (x1 || !x1 || x1) && (x2 || x3 || x4)` Yes, a solution for x1, x2, x3, x4 exists Solution: x1 = true, x2 = true, x3 = true, x4 = false **Reduction** **CLAIM**: 3SAT reduces to Independent Set * Recall this means we claim we can solve 3SAT by using the Independent Set algorithm! **PROOF**: To prove the reduction, we need to argue that we can: 1. Preprocess a given 3SAT problem 2. Solve it with Independent Set 3. Postprocess the output of part 2 into a solution to the original 3SAT problem. Let's do it! _**Preprocess a given 3SAT problem**_ Given an instance X of 3SAT, preprocess it into a graph G: 1. For each clause in X, create 3 vertices in a triangle 2. Add an edge between each literal and its negation\\ ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-dbe3e20baf5af818f5340eaceb1fbd4d32cd932f%252Fimage%2520%2827%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=67bad0b6&sv=2) _**Solve with Independent Sets**_ On graph G, find an independent set of _size = number of clauses in 3SAT_. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-ca3a6d13310f4beca0f1a265740452b9b4bdbaec%252Fimage%2520%28114%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=4f0a8283&sv=2) _**Postprocess the output**_ Elements in the independent set are considered "True", while elements outside are considered "False." If you can find an independent set of size = number of clauses in 3SAT, then you've successfully solved 3SAT (using independent sets whoo!). In the above example, since `x3`, `!x2`, `x3`, `x4` were picked for the independent set, we consider each of those literals to be True and values for the rest don't matter. Therefore, `x3 = True, x2 = False, x4 = True, x1 = doesn't matter.` **Why this works:** We'll reference the below example when going through the proof. `(x1 || x2 || !x3) && (x1 || !x1 || x1) && (x2 || x3 || x4)` The above 3SAT problem has 3 clauses. To form a satisfying truth assignment we must pick one literal from each clause and give it the value True. Of course, we must be consistent. If we choose `x1` to be True in the first clause, we can't choose `!x1` to be True in the third clause (x1 can't both be True and False!). Representing a clause by a triangle forces us to pick only literal in a clause for the independent set. Repeat this for every clause and and finding an independent set of _size = number of clauses_ means exactly one literal will be picked from each clause (we'll consider a picked node to be True). We also make sure to add an edge from each literal to its negation to prevent us from choosing opposite literals (e.g. both `x1` and `!x1`) in different clauses. This may also have the effect of finding an independent set impossible - in this case, 3SAT is also not solvable. Here's a visualization of the above reduction: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-b543efc0cc98a756fa84cab52e5c54ba5dd89702%252Fimage%2520%28124%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=c7ae7c78&sv=2) Note that reductions are a general concept and apply to many different types of problems (they don't always involve creating graphs!) ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/29.-reductions-and-decomposition/29.4-reductions-and-decomposition#reflection) Reflection One can argue that we have been doing reduction all throughout the course. * Abstract Lists reduce to arrays or linked lists * Percolation reduces to Disjoint Sets * Maze generation reduces to \[your solution here ;)\] However these aren't exactly reductions because you aren't using a single other algorithm to solve your problem. Notably, in the earlier reduction example we used the Independent Sets algorithm as a '[black boxarrow-up-right](https://en.wikipedia.org/wiki/Black_box) ' to solve 3SAT. Perhaps a better term for what we've been accomplishing earlier in the course is _decomposition_ - breaking a complex task into smaller parts. Using abstraction to make problem solving easier. This is the heart of computer science. [Previous29.3 Longest Pathchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/29.-reductions-and-decomposition/29.3-longest-path) [Next29.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/29.-reductions-and-decomposition/29.5-exercises) Last updated 4 months ago --- # 37.4 Summary | CS61B Textbook Fall 2025 **Radix Sort vs. Comparison Sorts.** In lecture, we used the number of characters examined as a cost model to compare radix sort and comparison sort. For MSD Radix Sort, the worst case is that each character is examined once for NM characters examined. For merge sort, MNlogN is the worst case characters examined. Thus, we can see that merge sort is slower by a factor of logN if character comparisons are an appropriate cost model. Using an empirical analysis, however, we saw that this does not hold true because of lots of background reasons such as the cache, optimized methods, extra copy operations, and overall because our cost model does not account for everything happening. **Just-In-Time Compiler.** The “interpreter” studies your code as it runs so that when a sequence of code is run many times, it studies and re-implements based on what it learns while running to optimize it. For example, if a LinkedList is created many times in a loop and left unused, it eventually learns to stop creating the LinkedLists since they are never used. With the Just-In-Time compiler disabled, merge sort, from the previous section, is indeed slower than MSD Radix Sort. **Radix Sorting Integers.** When radix sorting integers, we no longer have a charAt method. There are lots of alternative options are stilizing mods and division to write your own getDigit() method or to make each Integer into a String. However, we don’t actually have to stick to base 10 and can instead treat the numbers as base 16, 256, or even base 65536 numbers. Thus, we can reduce the number of digits, which can reduces the runtime since runtime for radix sort depends on alphabet size. [Previous37.3 Radix Sorting Integerschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/37.-sorting-and-data-structures-conclusion/37.3-radix-sorting-integers) [Next37.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/37.-sorting-and-data-structures-conclusion/37.5-exercises) Last updated 4 months ago --- # 20.7 Exercises | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.7-exercises#id-1.-potpourri) 1\. Potpourri ------------------------------------------------------------------------------------------------------------------------------ (a) True or False: Resizes are triggered if adding a key-value pair causes the load factor to be greater than or equal to the specified maximum load factor. chevron-rightAnswer to Q1(a)[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.7-exercises#answer-to-q1-a) **False**. A resize will be triggered if adding another key value pair would cause the load factor to **exceed** the specified maximum load factor. A resize is not triggered when the load factor is equal to the specified maximum load factor. (b) True or False: The `hashCode()` function can have varied return types. chevron-rightAnswer to Q1(b)[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.7-exercises#answer-to-q1-b) **False**. In Java, the hashCode() function can **only** have an `int` return type to serve as a hash value. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.7-exercises#id-2.-hashcode) 2\. hashCode().... ---------------------------------------------------------------------------------------------------------------------------------- In order for a hash code to be valid, objects that are equivalent to each other (i.e. .equals() returns true) must return equivalent hash codes. If an object does not explicitly override the `hashCode()` method, it will inherit the `hashCode()` method defined in the Object class, which returns the object’s address in memory. Here are four potential implementations of Integer's `hashCode()` function. Assume that `intValue()` returns the value represented by the Integer object. Categorize each `hashCode()` implementation as either a valid or an invalid hash function. If it is valid, point out a flaw or disadvantage. If it is invalid, explain why. Question (a) Copy public int hashCode() { return -1; } chevron-rightAnswer for Q2(a)[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.7-exercises#answer-for-q2-a) Valid. As required, this hash function returns the same hash code for Integers that are .equals() to each other. However, this is a terrible hash code because collisions are extremely frequent and occur 100% of the time. Question (b) chevron-rightAnswer for Q2(b)[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.7-exercises#answer-for-q2-b) Valid. Similar to (a), this hash function returns the same hash code for Integers that are .equals(). However, Integers that share the same absolute values will collide (for example, x = 5 and x = -5 will both return the same hashcode). A better hash function would be to just return intValue() itself. Question (c) chevron-rightAnswer for Q2(c)[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.7-exercises#answer-for-q2-c) Invalid. If we call hashCode() multiple times on the same Integer object, we will get different hash codes returned each time. Question (d) chevron-rightAnswer for Q2(d)[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.7-exercises#answer-for-q2-d) Invalid. This hash function returns some integer corresponding to the Integer object’s location in memory. Different Integer objects will exist in different locations in memory, so even if they represent the same value they will return different hash codes. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.7-exercises#id-3.-hashin-and-resizin) 3\. Hashin' and Resizin' -------------------------------------------------------------------------------------------------------------------------------------------------- Given the provided `hashCode()` implementation, hash the items listed below with external chaining (the first item is already inserted for you). Assume the load factor is 1, and the initial underlying array has size of 4. Use geometric resizing with a resize factor of 2. You may draw more boxes to extend the array when you need to resize. chevron-rightAnswer for Question 3[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.7-exercises#answer-for-question-3) Here is what the hash table should look like after inserting `guava`: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-b631d5a5dd512132559980f790616decbe8b2175%252Fscreenshot%25202023-03-01%2520at%25206.05.07%2520PM.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=73a5c75f&sv=2) Here is what the hash table should look like after inserting `durian`: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-2c01227a190b0bb03295555b2bd1b5b1d005b714%252Fimage%2520%28142%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=49526a1a&sv=2) Here is what the hash table should look like after all insertions have been completed: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-75a6da45d7234cbe99cd3a73cc55263d803400a9%252Fscreenshot%25202023-03-01%2520at%25206.06.50%2520PM.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=7312f66a&sv=2) [Previous20.6 Summarychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.6-summary) [Next21\. Hashing IIchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/21.-hashing-ii) Last updated 4 months ago * [1\. Potpourri](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.7-exercises#id-1.-potpourri) * [2\. hashCode()....](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.7-exercises#id-2.-hashcode) * [3\. Hashin' and Resizin'](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/20.-hashing-i/20.7-exercises#id-3.-hashin-and-resizin) Copy public int hashCode() { return intValue() * intValue(); } Copy public int hashCode() { Random rand = new Random(); return rand.nextInt(); } Copy public int hashCode() { return super.hashCode(); } Copy /** Returns 0 if word begins with ’a’, 1 if it begins with ’b’, etc. */ public int hashCode() { return word.charAt(0) - 'a'; } Copy ["apple", "cherry", "fig", "guava", "durian", "apricot", "banana"] --- # 40.3 Space/Time-Bounded Compression | CS61B Textbook Fall 2025 As described in the previous chapter, it is impossible to write the "perfect" compression algorithm that requires the fewest bits to output some bitstream BBB. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.3-space-time-bounded-compression#space-bounded-compression) Space-Bounded Compression ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- However, what about the problem of space-bounded compression? In this problem, we take in two inputs: a bitstream BBB and a target size SSS. The goal, then, is to find a program of length ≤S\\leq S≤S that outputs BBB. It turns out that such a problem is also uncomputable. If it were, then we could simply binary search on different values of SSS to find the optimal compression program size, which is impossible as shown in te previous section. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.3-space-time-bounded-compression#space-time-bounded-compression) Space-Time-Bounded Compression ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- What if we take our problem from above, and add a constraint that we can run at most TTT lines of bytecode? It might seem unintuitive, but this kind of problem is actually solvable. We will use the following algorithm: Copy for length L = 1....S: for each possible program P of length L: while (P is running && !(B is outputted) && lines_executed < T): run the next line of P The runtime of this algorithm is O(T∗2S)O(T \* 2^S)O(T∗2S), and in the end, it will either output some program `P` that has the correct output and is bounded by TTT and SSS, or return that no such program is possible. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.3-space-time-bounded-compression#efficient-bounded-compression) Efficient Bounded Compression --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- The runtime above is exponential in SSS. Thus, we might ask if it's possible to solve the space-time-bounded compression problem _efficiently_. As we'll see in the next chapter, this depends on our definition of efficiency. [Previous40.2 Optimal Compression, Kolmogorov Complexitychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.2-optimal-compression-kolmogorov-complexity) [Next40.4 P = NPchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.4-p-np) Last updated 4 months ago * [Space-Bounded Compression](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.3-space-time-bounded-compression#space-bounded-compression) * [Space-Time-Bounded Compression](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.3-space-time-bounded-compression#space-time-bounded-compression) * [Efficient Bounded Compression](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.3-space-time-bounded-compression#efficient-bounded-compression) --- # 18.3 B-Tree Operations | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.3-b-tree-operations#height-and-depth) Height and Depth ---------------------------------------------------------------------------------------------------------------------------------------- The average height and depth of a BST are important properties in determining performance. **Height** refers to the depth of the deepest leaf and is a tree-wide property, whereas **depth** refers to the distance from the root of a particular node and is node-specific. The **average depth** of a tree is the mean of the depth of every node. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-577ca416d70980915edf5b87acc94e4eed14f499%252Fimage%2520%28105%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=4aed59d3&sv=2) Heights and depths of a binary tree ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.3-b-tree-operations#bsts-in-practice) BSTs in Practice Height and average depth determine the runtime of BST operations. The height determines the worst-case runtime to fine a node, while the average depth determines the average-case runtime of search operations. The order in which we insert nodes has a major impact on the height and average depth of a BST. For example, consider inserting nodes `1, 2, 3, 4, 5, 6, 7`. This results in a spindly BST with height 6 and average depth 3. If we insert the same nodes in the order `4, 2, 1, 3, 6, 5, 7`, we get a much better height of 2 and an average depth of 1.43. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-35df84b3ec7032cbc5671afa39431a12c13ad440%252Fimage%2520%2878%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=ecd4b544&sv=2) Height and depth variations based on insertion order ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.3-b-tree-operations#real-world-bsts) Real-World BSTs In considering how BSTs operate in real-life applications, we may want to start by considering randomized BSTs. Luckily, on average, randomly generated insertion orders have log⁡N\\log NlogN height and average depth. In fact, you can prove that E\[d\]\=2ln⁡NE\[d\] = 2 \\ln NE\[d\]\=2lnN and E\[h\]\=4.311ln⁡NE\[h\] = 4.311 \\ln NE\[h\]\=4.311lnN (such a proof is beyond the scope of this course). Such properties hold even when considering both insertion and deletion. However, it is not always possible to randomize the order of insertions. For example, if we have real-time data that comes in sequentially, there is no way to shuffle the data since we do not have all the points at once. As such, we need a different way to maintain "bushiness" in our search trees. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.3-b-tree-operations#b-trees) B-Trees ---------------------------------------------------------------------------------------------------------------------- ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.3-b-tree-operations#avoiding-imbalance) Avoiding Imbalance If we could simply avoid adding new leaves in our BST, the height would never increase. Such an idea, however, would be infeasible, since we do need to insert values at some point. One idea that we might approach is that of "overstuffing" the leaf nodes. Instead of adding a new node upon insertion, we simply stack the new value into an existing leaf node at the appropriate location. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-bfcd93ba91d074609050b53a49bc790182523f73%252Fimage%2520%2866%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=73975a0&sv=2) Overstuffing a node upon `insert(17)` However, a clear problem with this approach is that it results in large leaf nodes that basically become a list of values, going back to the problem of linear search. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.3-b-tree-operations#moving-items-up) Moving Items Up To ameliorate the issue of overly stuffed leaf nodes, we may consider "moving up" a value when a leaf node reaches a certain number of values. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-0afc1efacef3f16e8500f4e72a13b3ed694e0829%252Fimage%2520%28159%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=5521c9df&sv=2) Moving `17` from a leaf node to its parent However, this runs into the issue that our binary search property is no longer preserved--`16` is to the right of `17`. As such, we need a second fix: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-ea8aa28edbd188ecabcb364036434327cc7f678c%252Fimage%2520%28109%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=41699315&sv=2) Splitting the children of an overstuffed node Above, we split the children of an overstuffed node into ranges: (−∞,15)(-\\infty, 15)(−∞,15), \[15,17\]\[15, 17\]\[15,17\], and (18,∞)(18, \\infty)(18,∞). A search on this structure would operate exactly the same as a BST, except for a value between 15 and 17, we go to the middle child instead of the left or right. If we set some constant LLL as a limit on our node size, our search time only increases by a constant factor (in other words, there is no asymptotic change). Adding to a node may cause a cascading chain reaction, as shown in the image below where we add `25` and `26`. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-e7bd9fbf22c668c92e6d2878d37a3eb51bdbc97f%252Fimage%2520%2856%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=b8a00db2&sv=2) Adding `25` and `26` causes multiple node splittings In the case when our root is above the limit, we are forced to increase the tree height. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-f36af6b26f3665f98557e201d6098a5fbf6a1bf4%252Fimage%2520%2861%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=2d66b593&sv=2) Root has four items ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-0e19c76f70af58757f1e28d505d6d70ba666314a%252Fimage%2520%2884%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=b3674641&sv=2) Splitting the root ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.3-b-tree-operations#perfect-balance) Perfect Balance Observe that our new splitting-tree data structure has perfect balance. If we split the root, every node is pushed down by one level. If we split a leaf or internal node, the height does not change. There is never a change that results in imbalance. The real name for this data structure is a **B-Tree**. B-Trees with a limit of 3 items per node are also called **2-3-4 trees** or **2-4** trees (a node can have 2, 3, or 4 children). Setting a limit of 2 items per node results in a **2-3 tree.** ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.3-b-tree-operations#b-tree-usage) B-Tree Usage B-Trees are used mostly in two specific contexts: first, with a small L for conceptually balancing search trees, or secondly, with L in the thousands for databases and filesystems with large records. [Previous18.2 Big O vs. Worst Casechevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.2-big-o-vs.-worst-case) [Next18.4 B-Tree Invariantschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.4-b-tree-invariants) Last updated 4 months ago * [Height and Depth](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.3-b-tree-operations#height-and-depth) * [BSTs in Practice](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.3-b-tree-operations#bsts-in-practice) * [Real-World BSTs](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.3-b-tree-operations#real-world-bsts) * [B-Trees](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.3-b-tree-operations#b-trees) * [Avoiding Imbalance](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.3-b-tree-operations#avoiding-imbalance) * [Moving Items Up](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.3-b-tree-operations#moving-items-up) * [Perfect Balance](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.3-b-tree-operations#perfect-balance) * [B-Tree Usage](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.3-b-tree-operations#b-tree-usage) --- # 13.4 Asymptotic Behavior | CS61B Textbook Fall 2025 In most applications, we are most concerned about what happens for very large values of NNN. This is known as the _asymptotic behavior_. We want to learn what types of algorithms are able to handle large amounts of data. Some examples of applications that require highly efficient algorithms are: * Simulating the interactions of billions of particles * Maintaining a social network with billions of users * Encoding billions of bytes of video data Algorithms that scale well have better _asymptotic_ runtime behavior than algorithms that scale poorly. Let us return to the original problem of characterizing the runtimes of our `dup` functions. Recall that we desire characterizations that have the following features: * Simple and mathematically rigorous. * Clearly demonstrate the superiority of dup2 over dup1. We’ve accomplished the second task! We are able to clearly see that `dup2` performed better than `dup1`. However, we didn’t do it in a very simple or mathematically rigorous way. Luckily, we can apply a series of simplifications to solve these issues. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.4-asymptotic-behavior#simplification-1-consider-only-the-worst-case) Simplification 1: Consider Only the Worst Case When comparing algorithms, we often only care about the worst case. The worst case is often where we see the most interesting effects, so we can usually ignore all other cases but the worst case. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.4-asymptotic-behavior#example) Example: Consider the operation counts of some algorithm below. What do you expect will be the order of growth of the runtime for the algorithm? * NNN (linear) * N2N^2N2 (quadratic) * N3N^3N3 (cubic) * N6N^6N6 (sextic) Operation Count less than (<) 100N2+3N100N^2 + 3N100N2+3N greater than (>) N3+1N^3+1N3+1 and (&&) 500050005000 **Answer:** N3N^3N3 (cubic) Intuitively, N3N^3N3 grows faster than N2N^2N2, so it would "dominate." To help further convince you that this is the case, consider the following argument: * Suppose the < operator takes α\\alphaα nanoseconds, the > operator takes β\\betaβ nanoseconds, and the && takes γ\\gammaγ nanoseconds. * The total time is α(100N2+3)+β(2N3+1)+5000γ\\alpha (100N^2 + 3)+\\beta (2N^3+1)+5000\\gammaα(100N2+3)+β(2N3+1)+5000γ nanoseconds. * For very large NNN, the 2βN32\\beta N^32βN3 term is much larger than others. * It can help to think of it in terms of calculus. What happens as NNN approaches infinity? Which term ends up dominating? ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.4-asymptotic-behavior#simplification-2-restrict-attention-to-one-operation) Simplification 2: Restrict Attention to One Operation Pick some representative operation to act as a proxy for overall runtime. From our `dup` example: * Good choice: `increment`, or **less than** or **equals** or **array accesses.** * Bad choice: **assignment of** `j = i + 1`, or `i = 0.` The operation we choose is called the “**cost model**.” ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.4-asymptotic-behavior#simplification-3-eliminate-low-order-terms) Simplification 3: Eliminate Low Order Terms Ignore lower order terms. **Sanity check**: Why does this make sense? (Related to the checkpoint above!) ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.4-asymptotic-behavior#simplification-4-eliminate-multiplicative-constants) **Simplification 4: Eliminate Multiplicative Constants** Ignore multiplicative constants. * Why? No real meaning! * By choosing a single representative operation, we already “threw away” some information. * Some operations had counts of 3N23N^23N2, ​​N2/2N^2/2N2/2, etc. In general, they are all in the family/shape of N2N^2N2. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.4-asymptotic-behavior#checkpoint-exercise) Checkpoint Exercise: Apply our four simplification rules to the `dup2` table. Operation Symbolic Count i = 0 1 j = i+1 0 to NNN < 0 to N−1N-1N−1 \== 1 to N−1N-1N−1 array accesses 2 to 2N−22N-22N−2 **Example answer**: array accesses with order of growth NNN. <, ==, and j=i+1 would be fine answers as well. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.4-asymptotic-behavior#simplification-summary) Simplification Summary * Only consider the worst case. * Pick a representative operation (aka: cost model) * Ignore lower order terms * Ignore multiplicative constants. [Previous13.3 Checkpoint: An Exercisechevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.3-checkpoint-an-exercise) [Next13.5 Simplified Analysis Processchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.5-simplified-analysis-process) Last updated 6 months ago * [Simplification 1: Consider Only the Worst Case](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.4-asymptotic-behavior#simplification-1-consider-only-the-worst-case) * [Simplification 2: Restrict Attention to One Operation](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.4-asymptotic-behavior#simplification-2-restrict-attention-to-one-operation) * [Simplification 3: Eliminate Low Order Terms](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.4-asymptotic-behavior#simplification-3-eliminate-low-order-terms) * [Simplification 4: Eliminate Multiplicative Constants](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.4-asymptotic-behavior#simplification-4-eliminate-multiplicative-constants) * [Checkpoint Exercise:](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.4-asymptotic-behavior#checkpoint-exercise) * [Simplification Summary](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/13.-asymptotics-i/13.4-asymptotic-behavior#simplification-summary) --- # 18.1 BST Performance | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.1-bst-performance#tree-height) Tree Height ---------------------------------------------------------------------------------------------------------------------------- One unforunate feature of BSTs is that they range from a best-case "bushy" tree to a worst-case "spindly" tree. In the best case, our tree will have height Θ(logN)\\Theta(log N)Θ(logN), whereas in the worst case our tree has a height of Θ(N)\\Theta(N)Θ(N), at which point it basically becomes a linked list. For example, `contains` on a "spindly" BST would take linear time. Both trees below have a height H = 3, yet the left tree is able to hold many more items than the left. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-49d8ed4741a87b5014bf9e11411d0e4425f55eef%252Fimage%2520%2865%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=c45bc924&sv=2) [Previous18\. B-Treeschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees) [Next18.2 Big O vs. Worst Casechevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/18.-b-trees/18.2-big-o-vs.-worst-case) Last updated 4 months ago --- # 25.2 Dijkstra's Algorithm | CS61B Textbook Fall 2025 Professor Hug's Lecture on Shortest Paths In [Chapter 23.1arrow-up-right](https://github.com/Berkeley-CS61B/fa25-gitbook/blob/main/23.-graph-traversals-and-implementations/23.1-bfs-and-dfs.md) we have implemented BFS & DFS. We discussed the idea of using BFS for finding the shortest path trees however when the graph edges have weight, BFS will upset us. Consider the following example. You are on campus and playing a game with your friends around Hearst Memorial Mining Building. You start at the location `s` and you want to go to the location `t`. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-b8fde5ff8e7e4182f2e0e36ffc844b3f821e075e%252Fdj.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=590646ae&sv=2) BFS will yield a route of length 330 m instead of therefore we need an algorithm that takes into account edge distances, also known as “edge weights”. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-15061cbfd2cb3fa6d3f753bbfc372cd5a2f94d04%252Fimage1.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=3f0e35be&sv=2) Correct Result ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-a98b5a40a125fa189719093f408d4bfbf3cf41df%252Fimage2.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=526c2ca7&sv=2) BFS result ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.2-dijkstras-algorithm#observations) Observations Note that the shortest path (for a graph whose edges have weights) can have many, many edges. What we care to minimize is the sum of the weights of the edges on the selected path. Secondly, note the fact that the shortest paths tree from a source `s` can be created in the following way: * For every vertex `v` (which is not `s`) in the graph, find the shortest path from `s` to `v`. * "Combine"/"Union" all the edges that you found above. Tada! Thirdly, note that the "Shortest Path Tree" will **always be a tree**. Why? Well, let's think about our original solution, where we maintained an edgeToedgeTo array. For every node, there was exactly one "parent" in the edgeToedgeTo array. (Why does this imply that the "Shortest Path Tree" will be a tree? Hint: A tree has `V-1` edges, where `V` is the number of nodes in the tree.) ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.2-dijkstras-algorithm#dijkstras-algorithm-da-kstr) Dijkstra's Algorithm \[\[/ˈdaɪkstrə/\]\] Dijkstra's algorithm takes in an input vertex �s, and outputs the shortest path tree from �s. How does it work? 1. Create a priority queue. 2. Add `s` to the priority queue with priority 00. Add all other vertices to the priority queue with priority ∞∞. 3. While the priority queue is not empty: pop a vertex out of the priority queue, and **relax** all of the edges going out from the vertex. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.2-dijkstras-algorithm#what-does-it-mean-to-relax) What does it mean to **relax**? Suppose the vertex we just popped from the priority queue was `v`. We'll look at all of `v`'s edges. Say, we're looking at edge (`v`,`w`) (the edge that goes from `v` to `w`). We're going to try and relax this edge. What that means is: Look at your current best distance to `w` from the source, call it **curBestDistToW**. Now, look at your **curBestDistToV**+weight(`v`,`w`) (let's call it **potentialDistToWUsing**). Is **potentialDistToWUsing** **better, i.e., smaller** than **curBestDistToW**? In that case, set **curBestDistToW=potentialDistToWUsingV**, and update the **edgeTo\[**`**w**`**\]** to be `v`. **Important note: we never relax edges that point to already visited vertices.** This whole process of calculating the potential distance, checking if it's better, and potentially updating is called relaxing. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.2-dijkstras-algorithm#pseudocode) Pseudocode Guarantees As long as the edges are all non-negative, Dijkstra's is guaranteed to be optimal. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.2-dijkstras-algorithm#proofs-and-intuitions) Proofs and Intuitions Assume all edges are non-negative. * At start, distTo\[source\] = 0. This is optimal. * After relaxing all edges from source, let vertex v1v\_1v1​ be the vertex with the minimum weight (i.e., the one that's closest to the source.) **Claim: distTo\[**v1v\_1v1​**​​\] is optimal, i.e., whatever the value of distTo\[**v1v\_1v1​**​​\] is at this point is the shortest distance from** sss **to** v1v\_1v1​**​​**. Why? * Let's try to see why this **MUST** be the case. * Suppose that it isn't the case. Then that means that there is some other path from sss to v1v\_1v1​ which is shorter than the direct path (sss,​​ v1v\_1v1​). Ok, so let's consider this hypothetical cool shorter path... it would have to look like (sss, vav\_ava​​​, vbv\_bvb​​​,…, v1v\_1v1​​​). But... (sss, vav\_ava​) is **already** bigger than (sss, v1v\_1v1​). (Note that this is true because v1v\_1v1​ is the vertex that is closest to sss from above.) So how can such a path exist which is actually shorter? It can't! * Now, the next vertex to be popped will be v1v\_1v1​​​. (Why? Note that it currently has the lowest priority in the PQ!) * So now, we can make this same argument for v1v\_1v1​​​ and all the relaxation it does. (This is called "proof by induction". It's kind of like recursion for proofs.) And that's it; we're done. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.2-dijkstras-algorithm#negative-edges) Negative Edges? Things can go pretty badly when negative edges come into the picture. Consider the following image. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-bc8f8cf82a9003e21eb99439629a1101aafc2898%252Fimage.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=8a1c6792&sv=2) Suppose you're at that vertex labeled 34. Now you're going to try to relax all your edges. You have only one outgoing edge from yourself to 33 with weight −67. Ah, but note: vertex 33 is already visited (it's marked with white.) So... we don't relax it. (Recall the pseudocode for the relax method.) Now we go home thinking that the shortest distance to 33 is 82 (marked in pink.) But really, we should have taken the path **through** 34 because that would have given us a distance of 101−67=34. Oops. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-6c5bff1ee70393b1831369fb2b6ca5210977d739%252Fimage%2520%282%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=df69bf29&sv=2) **Dijkstra's algorithm is not guaranteed to be correct for negative edges. It might work... but it isn't guaranteed to work.** Try this out: suppose that your graph has negative edges, but all the negative edges only go out of the source vertex `s` that you were passed in. Does Dijkstra's work? Why / Why not? ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.2-dijkstras-algorithm#a-noteworthy-invariant) A noteworthy invariant Observe that once a vertex is popped off the priority queue, it is never re-added. Its distance is never re-updated. So, in other words, once a vertex is popped from the priority queue, we **know** the true shortest distance to that vertex from the source. One nice consequence of this fact is "short-circuiting". Suppose... that I didn't care about the shortest-paths tree, but just wanted to find the shortest path from some source to some other target. Suppose that you wanted to take, like, the cities of the world on a graph, and find the shortest path from Berkeley to Oakland. Running `dijkstra(Berkeley)` will mean that you can't actually stop this powerful beast of an algorithm... you have to let it run... till it finds the shortest path to LA, and Houston, and New York City, and everywhere possible! Well. Once `Oakland` is popped off the priority queue in the algorithm, we can just stop. We can just return the distance and the path we have at that point, and it will be correct. So **sometimes** `dijkstra` takes in not only a source, but also a target. This is for the purposes of short-circuiting. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.2-dijkstras-algorithm#demo) [Demoarrow-up-right](https://docs.google.com/presentation/d/1_bw2z1ggUkquPdhl7gwdVBoTaoJmaZdpkV6MoAgxlJc/pub?start=false&loop=false&delayms=3000&slide=id.g771336078_0_180) \\ [Previous25.1 Introductionchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.1-introduction) [Next25.3 A\* Algorithmchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.3-a-algorithm) Last updated 4 months ago * [Observations](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.2-dijkstras-algorithm#observations) * [Dijkstra's Algorithm \[\[/ˈdaɪkstrə/\]\]](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.2-dijkstras-algorithm#dijkstras-algorithm-da-kstr) * [What does it mean to relax?](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.2-dijkstras-algorithm#what-does-it-mean-to-relax) * [Pseudocode](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.2-dijkstras-algorithm#pseudocode) * [Proofs and Intuitions](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.2-dijkstras-algorithm#proofs-and-intuitions) * [Negative Edges?](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.2-dijkstras-algorithm#negative-edges) * [A noteworthy invariant](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.2-dijkstras-algorithm#a-noteworthy-invariant) * [Demo](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/25.-shortest-paths/25.2-dijkstras-algorithm#demo) Copy def dijkstras(source): PQ.add(source, 0) For all other vertices, v, PQ.add(v, infinity) while PQ is not empty: p = PQ.removeSmallest() relax(all edges from p) Copy def relax(edge p,q): if q is visited (i.e., q is not in PQ): return if distTo[p] + weight(edge) < distTo[q]: distTo[q] = distTo[p] + w edgeTo[q] = p PQ.changePriority(q, distTo[q]) --- # 22.2 Heaps | CS61B Textbook Fall 2025 Introducing the Heap We previously saw that our known data structures with the best runtime for our PQ operations was the _binary search tree_. Modifying its structure and the constraints, we can further improve the runtime and efficiency of these operations. We will define our binary min-heap as being **complete** and obeying **min-heap** property: * Min-heap: Every node is less than or equal to both of its children * Complete: Missing items only at the bottom level (if any), all nodes are as far left as possible. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2Fjoshhug.gitbooks.io%2Fhug61b%2Fcontent%2Fassets%2Fheap-13.2.1.png&width=768&dpr=3&quality=100&sign=beadbf0d&sv=2) As we can see in the figures above, the green colored heaps are valid and the red ones aren't. The last two aren't because they violate at least one of the properties that we defined above. Now let's consider how this structure lends itself to the abstract data type we described in the previous chapter. We will do this through analyzing our desired operations. **Exercise 13.2.1.** Determine how each method of our Priority Queue interface will be implemented given this heap structure. Don't write actual code, just pseudocode! ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.2-heaps#heap-operations) Heap Operations Heap Operations The three methods we care about for the PriorityQueue ADT are `add`, `getSmallest`, and `removeSmallest`. We will start by conceptually describing how these methods can be implemented given our given schema of a heap. * `add`: Add to the end of heap temporarily. Swim up the hierarchy to the proper place. * Swimming involves swapping nodes if child < parent * `getSmallest`: Return the root of the heap (This is guaranteed to be the minimum by our _min-heap_ property * `removeSmallest`: Swap the last item in the heap into the root. Sink down the hierarchy to the proper place. * Sinking involves swapping nodes if parent > child. Swap with the smallest child to preserve _min-heap_ property. Great! We have determined how we will approach the operations specified by the PriorityQueue interface in an efficient way. But how do we actually code this? **Exercise 13.2.2.** Give the runtime for each of the methods specified above. Worst cases and best cases. ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.2-heaps#tree-representation) Tree Representation Tree representation There are many approaches we can take to representing trees. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.2-heaps#approach-1a-1b-and-1c) Approach 1a, 1b, and 1c Let us consider the most intuitive and previously used representation for trees. We will create mappings between nodes and their children. There are several ways to do this which we will explore right now. * In approach **Tree1A**, we consider creating pointers to our children and storing the value inside of the node object. These are hardwired links that give us fixed-width nodes. We can observe the code: The visualization of this type of structure is shown below. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-697c44def6e9204d379d44f700104dd3a9bc0c27%252Fimage%2520%28128%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=94b666d1&sv=2) 1a: Fixed-Width Nodes (BSTMap used this approach) * Alternatively, in **Tree1B**, we explore the use of arrays as representing the mapping between children and nodes. This would give us variable-width nodes, but also awkward traversals and performance will be worse. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-4728ae817f0941f31752f4675fef9740d08d7d18%252Fimage%2520%2893%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=df17e93b&sv=2) 1b: Variable-Width Nodes * Lastly, we can use the approach for **Tree1C**. This will be slightly different from the usual approaches that we've seen. Instead of only representing a node's children, we say that nodes can also maintain a reference to their siblings. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-ca11fd475210f956149981ae0aba050141d70b8e%252Fimage%2520%2862%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=65ee6639&sv=2) 1c: Sibling Tree In all of these approaches, we store explicit references to who is below us. These explicit references take the form of pointers to the actual Tree objects that are our children. Let's think of more exotic approaches that don't store explicit references to children. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.2-heaps#approach-2) Approach 2 Recall the Disjoint Sets ADT. The way that we represented this Weighted Quick Union structure was through arrays. For representing a tree, we can store the _keys_ array as well as a _parents_ array. The keys array represent which index maps to which key, and the parents array represents which key is a child of another key. Take some time to ensure that the tree on the left corresponds to the representation in the arrays on the right. It's time to make a very important observation! Based on the structure of the tree and the relationship between the array representations and the diagram of the tree, we can see: 1. The tree is **complete**. This is a property we have defined earlier. 2. The parents array has a sort of redundant pattern where elements are just doubled. 3. Reading the level-order of the tree, we see that it matches exactly the order of the keys in the _keys_ array. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-d88305608c6015920db167d6cdbc74d5b2d01ed8%252Fimage%2520%2888%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=46710483&sv=2) Approach 2 What does this all mean? We know the parents array is redundant so we can ignore it and we know that a tree can be represented by level order in an array. #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.2-heaps#approach-3) Approach 3 In this approach, we assume that our tree is complete. This is to ensure that there are no "gaps" inside of our array representation. Thus, we will take this complex 2D structure of the tree and flatten it into an array. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-e4cd6280a82069f324a7cf7a16fb98bd5ba5a249%252Fimage%2520%28117%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=42807039&sv=2) #### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.2-heaps#swim) Swim Given this implementation, we define the following code for the "swim" described in the Heap Operations section. What does the parent method do? It returns the parent of the given k using the representation in Approach 3. **Exercise 13.2.3.** Write the parent method. For extra challenge, try to write the methods for finding the left child and right child of a given item. [Previous22.1 Priority Queueschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.1-priority-queues) [Next22.3 PQ Implementationchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.3-pq-implementation) Last updated 4 months ago * [Heap Operations](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.2-heaps#heap-operations) * [Tree Representation](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/22.-heaps-and-priority-queues/22.2-heaps#tree-representation) Copy public class Tree1A { Key k; Tree1A left; Tree1A middle; Tree1A right; ... } Copy public class Tree1B { Key k; Tree1B[] children; ... Copy public class Tree1C { Key k; Tree1C favoredChild; Tree1C sibling; ... } Copy public class Tree2 { Key[] keys; int[] parents; ... } Copy public class TreeC { Key[] keys; ... } Copy public void swim(int k) { if (keys[parent(k)] ≻ keys[k]) { swap(k, parent(k)); swim(parent(k)); } } --- # 40.4 P = NP | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.4-p-np#reductions) Reductions ----------------------------------------------------------------------------------------------------------------------------------- It turns out that space-time-bounded compression reduces to 3SAT, INDSET, LONGESTPATH, and many other hard problems. (The actual proof of such reductions is incredibly complex and ommitted from this textbook). ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-f9d15b77af8cc642d6903cf6f617b674b145a92b%252Fimage%2520%28155%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=97dbbdda&sv=2) Space-time-bounded compresion can be solved with LONGEST\_PATH. The reason that space-time-compression can be turned into longest paths (or any other problem mentioned above) is that all these problems are part of a **complexity class** known as **NP**. A property of such problem is that any NP problem can be reduced to any NP-complete problem, including longest paths. In subsequent section, we will briefly cover what P, NP, and complexity classes are. However, most of these topics are far beyond the scope of this textbook or course, and would be better served by taking an upper-level algorithms course. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.4-p-np#p-and-np) P and NP ------------------------------------------------------------------------------------------------------------------------------- All yes/no problems can be divided into two main classes: * P: efficiently solvable problems. * NP: problems with solutions that are efficiently verifiable. This means that given an answer to the problem, you can efficiently check whether the answer is correct or not. Examples of problems in P include (note that P is a subset of NP): * Is this array sorted? * This can be solved by sorting the array using any sorting algorithm, and verified by checking that adjacent elements are increasing. * Does this array have duplicates? * This can be solved with a double for-loop, and verified in a similar manner. Examples of problems in NP include: * Is there a solution to this 3SAT problem? * Generating a solution to a 3SAT problem is difficult, but given an assignment of symbols to booleans, you can simply plug in the values and check that the equation is satisfied. * In graph G, does there exist a path from s to t of weight > k? * Genearting a solution to this (essentially longest paths) is difficult, but given a path, you can easily verify if it is a valid path from s to t and that its weight is > k. Examples of problems not in NP include: * Is this the best chest move I can make next? * There is no efficient way to verify that a chess move is indeed optimal, unless you draw out all possibilities for all subsequent moves. * What is the longest path? * This is not a yes-no question. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.4-p-np#np-completeness) NP-Completeness --------------------------------------------------------------------------------------------------------------------------------------------- An unexpected property of NP problems is that every NP problem reduces to every NP-complete problem. This reduction is also "efficient", in that the problem can be transformed (pre-processed and post-processed) in polynomial time. This also means that solving any NP-complete problem essentially solves all problems in NP. As of today, there are tens of thousands of known NP-complete problems, but none of them have been solved yet. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-e5acefc1fe0ff5f20b9fed110d25d55303399717%252Fimage%2520%28149%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=67510713&sv=2) NP-complete problems and reductions. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.4-p-np#p-np) P = NP? -------------------------------------------------------------------------------------------------------------------------- An open question in computer science is whether P = NP; in other words, are all problems with efficiently verifiable problems (NP) also efficiently solvable (P)? One reason to suggest that P = NP might be true is that checking an answer is always efficient. Thus, given the right pruning, could we efficiently zero in on an answer? [Previous40.3 Space/Time-Bounded Compressionchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.3-space-time-bounded-compression) [Next40.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.5-exercises) Last updated 4 months ago * [Reductions](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.4-p-np#reductions) * [P and NP](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.4-p-np#p-and-np) * [NP-Completeness](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.4-p-np#np-completeness) * [P = NP?](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/40.-compression-complexity-p-np/40.4-p-np#p-np) --- # 30. Basic Sorts | CS61B Textbook Fall 2025 [30.1 The Sorting Problemchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.1-the-sorting-problem) [30.2 Selection Sort & Heapsortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.2-selection-sort-and-heapsort) [30.3 Mergesortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.3-mergesort) [30.4 Insertion Sortchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.4-insertion-sort) [30.5 Summarychevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.5-summary) [30.6 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.6-exercises) [Previous29.5 Exerciseschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/29.-reductions-and-decomposition/29.5-exercises) [Next30.1 The Sorting Problemchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/30.-basic-sorts/30.1-the-sorting-problem) Last updated 4 months ago --- # 29.2 Shortest Paths on DAGs | CS61B Textbook Fall 2025 Recall from the previous section that **DAGs** are **directed, acyclic graphs**. If we wanted to find the shortest path on DAGs we could use [Dijkstra'sarrow-up-right](https://github.com/Berkeley-CS61B/fa25-gitbook/blob/main/24.-shortest-paths/24.2-dijkstras-algorithm.md) . However, with DAGs there's a simple shortest path algorithm which also handles negative edge weights! ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/29.-reductions-and-decomposition/29.2-shortest-paths-on-dags#dijkstras-negative-edge-weight-failure) Dijkstra's Negative Edge Weight Failure Recall that Dijkstra's can fail if negative edges exist because it relies on the assumption that once we visit an edge, we've found the shortest path to that edge. But if negative edge weights can exist ahead of where we can see, then this assumption fails. Consider the following example: \\ ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-40c5f82e691d33a40af9d43f16d6819314b3de82%252Fimage%2520%2837%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=b9d5cb93&sv=2) Starting from A, Dijkstra's will visit C first, then B (never even considering the edge _**B→C**_¹ Of course, negative edge weights do not mean Dijkstra's is guaranteed to fail. Dijkstra's succeeds with the following example: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-10d86799bad96e1aaa26751888cf484a02478c91%252Fimage%2520%2840%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=c6f7aa0f&sv=2) > ¹. This technically depends on your implementation of Dijkstra's. If we ensure that the relaxation step only considers neighbors that are still in the queue (haven't been visited yet), then it is true that _**B→C**_ will never be considered. If you don't have that check, then technically when we pop the last node (B) from the queue, we'd consider B's neighbors and update C which gives us the right answer for this specific example. However, in that case one could argue that the graph breaks the Dijkstra invariant and thus Dijkstra has 'failed'. Note, the Dijkstra invariant: _once a node is deleted from the queue (visited) then you've found the shortest path to that node._ [↩arrow-up-right](https://joshhug.gitbooks.io/hug61b/content/chap21/chap212.html#reffn_1) ### [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/29.-reductions-and-decomposition/29.2-shortest-paths-on-dags#shortest-path-algorithm-for-dags) Shortest Path Algorithm for DAGs Visit vertices in topological order: * On each visit, relax all outgoing edges Recall the definition for relaxing an edge _**u→v**_ with weight w: Since we visit vertices in topological order, a vertex is visited only when all possible info about it has been considered. This means that if negative edge weights exist along a path to v, then those have been taken into account by the time we get to �v! Finding a topological sort takes O(V+E) time while relaxation from each vertex also takes O(V+E) time in total. Thus, the overall runtime is O(V+E). Recall that Dijkstra's takes O((V+E)logV) time because of our min-heap operations. What if we want to solve the shortest path problem on graphs that aren't DAGs and also may have negative edges? An extension of Dijkstra's called [Bellman Fordarrow-up-right](https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm) can suit your needs, though it is out of scope for this course. [Previous29.1 Topological Sorts and DAGschevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/29.-reductions-and-decomposition/29.1-topological-sorts-and-dags) [Next29.3 Longest Pathchevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/29.-reductions-and-decomposition/29.3-longest-path) Last updated 4 months ago * [Dijkstra's Negative Edge Weight Failure](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/29.-reductions-and-decomposition/29.2-shortest-paths-on-dags#dijkstras-negative-edge-weight-failure) * [Shortest Path Algorithm for DAGs](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/29.-reductions-and-decomposition/29.2-shortest-paths-on-dags#shortest-path-algorithm-for-dags) Copy if distTo[u] + w < distTo[v]: distTo[v] = distTo[u] + w edgeTo[v] = u --- # 26.5 MST Exercises | CS61B Textbook Fall 2025 [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.5-mst-exercises#factual) Factual --------------------------------------------------------------------------------------------------------------------------------- 1. Select all valid MSTs in the diagram below. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-410e2cde845f7e9eb7b58bf9b7edfb3e6ca6b8ae%252Fimage%2520%285%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=53bd780a&sv=2) 1. **True/False**: It is possible that the only Shortest Path Tree is the only Minimum Spanning Tree. chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.5-mst-exercises#problem-1) Only B. C and D have cycles; A does not span all vertices. chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.5-mst-exercises#problem-2) True. In a tree, there is only one SPT and MST. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.5-mst-exercises#procedural) Procedural --------------------------------------------------------------------------------------------------------------------------------------- 1. Run Prim's from `A` in the graph below. In what order are vertices visited? Break ties alphabetically. 2. Run Kruskal's in the graph below. In what order are edges added to the tree? Break ties alphabetically. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-37882840755e56711d8f035b335749764cd48171%252Fimage%2520%2851%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=8ea5ae7b&sv=2) 1. Design an algorithm to find the min-product spanning tree; ie the spanning tree with the minimum product of its edges. You may assume all edge weights are > 1. chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.5-mst-exercises#problem-1-1) Order: `A B D C F G E`. Prim's repeatedly picks the lightest edge between the current tree and any node not in the tree. chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.5-mst-exercises#problem-2-1) `AB, BD, CF, FG, AC, EG`. Kruskal's keeps adding the next lightest edge as long as it doesn't form a cycle. chevron-rightProblem 3[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.5-mst-exercises#problem-3) Simply take the logarithm of each edge weight, then run any MST algorithm on it. This is guaranteed to work since log⁡a+log⁡b\=log⁡ab\\log a + \\log b = \\log abloga+logb\=logab, and minimizing the logarithm of the product is the same as minimizing the product for positive weight edges. [hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.5-mst-exercises#metacognitive) Metacognitive --------------------------------------------------------------------------------------------------------------------------------------------- 1. If we add 1 to the weight of each edge in an arbitrary graph, will the MST created by Kruskal’s change? 2. **True/False**: Prim’s Algorithm and Kruskal’s algorithm will always return the same result. If this is true, explain why. If this is false, provide a counterexample, breaking ties alphabetically. 3. Prove the following, known as the cycle property: Given any cycle in an edge weighted graph (all edge weights distinct), the edge of maximum weight in the cycle does not belong to the MST of the graph. chevron-rightProblem 1[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.5-mst-exercises#problem-1-2) Adding 1 to each number will not change the order of the edges when we sort them, therefore we will get the same MST. chevron-rightProblem 2[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.5-mst-exercises#problem-2-2) False. A counterexample is the following graph: ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-326c1783879ce2daca7a97cee18ee52aa70fbc08%252Fimage%2520%2859%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=89d881b6&sv=2) Prim’s starting from A will select AD, BD, and CD, whereas Kruskals will select AD, BC, and BD. chevron-rightProblem 3[hashtag](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.5-mst-exercises#problem-3-1) Suppose, for contradiction, the maximum-weight edge `f` in a cycle is present in the MST. ![](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/~gitbook/image?url=https%3A%2F%2F3031105543-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F2aYwKpK43ima4gxrLWSB%252Fuploads%252Fgit-blob-5f99d51e811ec20c11f228cd4a6f99a71647eef1%252Fimage%2520%28150%29.png%3Falt%3Dmedia&width=768&dpr=3&quality=100&sign=83d738a9&sv=2) Removing `f` disconnects our MST `T`. Form a cut with the two sides of the MST after `f` is removed. Since `f` is part of a cycle, there must be also some edge `e` crossing that same cut. However, if we replace `f` with `e`, we now have a spanning tree that has less weight than our MST `T`. This is a contradiction, so the maximum weight edge in a cycle cannot be part of the MST. [Previous26.4 Chapter Summarychevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.4-chapter-summary) [Next27\. Prefix Operations and Trieschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/27.-prefix-operations-and-tries) Last updated 4 months ago * [Factual](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.5-mst-exercises#factual) * [Procedural](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.5-mst-exercises#procedural) * [Metacognitive](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/26.-minimum-spanning-trees/26.5-mst-exercises#metacognitive) --- # 36.4 Summary | CS61B Textbook Fall 2025 **Terminology.** * Radix - just another word for ‘base’ as in the base of a number system. For example, the radix for words written in lowercase English letters is 26. For number written in Arabic numerals it is 10. * Radix sort - a sort that works with one character at a time (by grouping objects that have the same digit in the same position). * Note: I will use ‘character’ and ‘digit’ interchangably in this study guide. **Counting Sort.** Allows you to sort NNN keys that are integers between 000 and R−1R-1R−1 in Θ(N+R)\\Theta(N+R)Θ(N+R)time. Beats linearithmic lower bound by avoiding any binary compares. This is a completely different philosophy for how things should be sorted. This is the most important concept for this lecture. **LSD.** In the LSD algorithm, we sort by each digit, working from right to left. Requires examination of Θ(WN)\\Theta(WN)Θ(WN)digits, where WWWis the length of the longest key. Runtime is Θ(WN+WR)\\Theta(WN+WR)Θ(WN+WR), though we usually think of RRR as a constant and just say Θ(WN)\\Theta(WN)Θ(WN). The Θ(WR)\\Theta(WR)Θ(WR) part of the runtime is due to the creation fo length RRR arrows for counting sort. We usually do LSD sort using counting sort as a subroutine, but it’s worth thinking about whether other sorts might work as well. **LSD vs Comparison Sorting.** Our comparison sorts, despite requiring Θ(N∗logN)\\Theta(N\*logN)Θ(N∗logN) time, can still be faster than LSD sort. For extremely large N, LSD sort will naturally win, but log N is typically pretty small. Know which algorithm is best in the two extreme cases of very long dissimilar strings and very long, nearly equal strings. **MSD.** In MSD sorting, we work from left to right, and solve each resulting subproblem independently. Thus, for each problem, we may have as many as RRR subproblem. Worst case runtime is exactly the same as LSD sort, Θ(WN+WR)\\Theta(WN+WR)Θ(WN+WR), though can be much better. In the very best case, where we only have to look at the top character (only possible for R\>NR>NR\>N), we have a runtime of Θ(N+R)\\Theta(N+R)Θ(N+R). [Previous36.3 MSD Radix Sortchevron-left](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/36.-radix-sorts/36.3-msd-radix-sort) [Next36.5 Exerciseschevron-right](https://cs61b-2.gitbook.io/cs61b-textbook-fall-2025/36.-radix-sorts/36.5-exercises) Last updated 4 months ago --- # 17.2 Binary Search | CS61B Textbook Fall 2025 Binary Search Introduction Getting Familiar with Binary Search Binary search is a nice way of searching a list for a particular item. It requires the list to be in sorted order and uses that fact to find an element quickly. To do a binary search, we start in the middle of the list, and check if that's our desired element. If not, we ask: is this element bigger or smaller than our element? If it's bigger, then we know we only have to look at the half of the list with smaller elements. If it's too small, then we only look at the half with bigger elements. In this way, we can cut in half the number of options we have left at each step, until we find our target element. What's the worst possible case? When the element we want isn't in the list at all. Then we will make comparisons until we've eliminated all regions of the list, and there are no more bigger or smaller halves left. For an animation of binary search, see [these slides.arrow-up-right](https://docs.google.com/presentation/d/1P4HKmsO3Aaugv7_U16jJN0UbfTEJi1uZUdi_WbIIGe0/edit#slide=id.g463de7561_042) What's the intuitive runtime of binary search? Take a minute and use the tools you know to consider this. We start with NNN options, then N/2N/2N/2, then N/4N/4N/4 ... until we have just 1. Each time, we cut the array in half, so in the end we must perform a total of log2(N)log\_{2}(N)log2​(N)operations. Each of the log2(N)log\_{2}(N)log2​(N) operations, eg. finding the middle element and comparing with it, takes constant time. So the overall runtime then is order log2(N)log\_{2}(N)log2​(N). It's important to note, however that each step doesn't cut it _exactly_ in half. If the array is of even length, and there is no 'middle', we have to take either a smaller or a larger portion. But this is a good intuitive approach. We'll do a precise way next. A more precise analysis of Binary Search runtime To precisely calculate the runtime of binary search, we'll count the number of operations, just as we've done previously. First, we define our cost model: let's use the number of recursive binary search calls. Since the number of operations inside each call is constant, the number of calls will be the only thing varying based on the size of the input, so it's a good cost model. Like we've seen before, let's do some example counts for specific NNN. As an exercise, try to fill this table in before continuing: N 1 2 3 4 5 6 7 8 9 10 11 12 13 **Count** Alright, here's the result: N 1 2 3 4 5 6 7 8 9 10 11 12 13 **Count** 1 2 2 3 3 3 3 4 4 4 4 4 4 These seems to support our intuition above of log2(N)log\_{2}(N)log2​(N). We can see that the count seems to increase by one only when NNN hits a power of 2. ...but we can be even more precise: C(N)\=⌊log2(N)⌋+1C(N)=\\lfloor log\_{2}(N)\\rfloor +1C(N)\=⌊log2​(N)⌋+1 (These L-shaped bars are the "floor" function, which is the result of the expression rounded down to the nearest integer.) A couple properties worth knowing (see below for proofs): * ⌊f(N)⌋\=Θ(f(N))\\lfloor f(N) \\rfloor=\\Theta(f(N))⌊f(N)⌋\=Θ(f(N)) * ⌈f(N)⌉\=Θ(f(N))\\lceil f(N) \\rceil=\\Theta(f(N))⌈f(N)⌉\=Θ(f(N)) * logp(N)\=Θ(logq(N))log\_{p}(N) = \\Theta(log\_{q}(N))logp​(N)\=Θ(logq​(N)) The last one essentially states that for logarithmic runtimes, the base of the logarithm doesn't matter at all, because they are all equivalent in terms of Big-O (this can be seen by applying the logarithm change of base). Applying these simplifications, we see that Θ(⌊log2(N)⌋\=Θ(log(N))\\Theta(\\lfloor log\_{2}(N)\\rfloor=\\Theta(log(N))Θ(⌊log2​(N)⌋\=Θ(log(N)) just as we expected from our intuition. **Example Proof:** Prove ⌊f(N)⌋\=Θ(f(N))\\lfloor f(N) \\rfloor=\\Theta(f(N))⌊f(N)⌋\=Θ(f(N)) **Solution:** We start with the following inequality: f(N)−12