Discussion 7: Linked Lists
In lecture, we introduced several sorting algorithms implemented on arrays, each with its own benefits and drawbacks. However, arrays are not the only linear data structure we may want to sort! Today, we will focus on developing a merge sort implementation for singly-linked lists.
Unlike arrays, linked lists do not provide an \(O(1)\) random access guarantee. This limitation makes sorting algorithms, such as Quicksort, that depend on direct access to specific positions in the structure fairly cumbersome. Merge sort, however, is well-suited to linked lists because of its divide-and-conquer structure. As a reminder, the algorithm consists of three main steps:
- Split the list into two halves.
- Recurse on each half to sort them independently.
- Merge the two sorted halves back together into a single sorted list.
While array-based merge sort requires creating temporary arrays during the merge step, linked lists allow us to split and re-link nodes directly. In this discussion, you’ll develop an algorithm that leverages these properties while strengthening your ability to traverse and manipulate linked structures.
Learning Outcomes
- Draw object (or node) diagrams to visualize linked data structures.
- Implement methods on linked data structures.
- Develop recursive methods in Java given their specifications.
- Determine the number of recursive calls and the maximum depth of the call stack of a recursive method and use these to compute its time and space complexities.
- Compare the performance of List operations implemented with dynamic arrays and linked chains. Determine which is preferable for a given use case.
Reminder: Discussion Guidelines
The work that you complete in discussion serves as a formative assessment tool, meaning it offers the opportunity to assess your understanding of the material and for our course staff to get a “pulse” on how things are going so we can make adjustments in future classes. Your grade in discussion is based entirely on attendance and participation; if you show up and you are actively engaged with the activity (working on the activity on your computer, discussing the activity with your group, asking and answering questions, etc.) for the entire 50-minute section period, you will earn full credit. If you complete the activity early, helping other students is a great way to further your own understanding. You do not need to submit any of the work that you complete during discussion.
Since discussion activities are not graded for correctness, we do not place any restrictions on resources that you may use to complete them, which include notes, books, unrestricted conversations with other students, internet searches, and the use of large language models or other generative AI. We advise you to be pragmatic about your use of these resources and think critically about whether they are enhancing your learning. Discussion activities are intended to serve as “strength training” for programming tasks we will expect on assignments and exams (and that you will encounter in future courses and careers), and their main benefit comes from critically thinking to “puzzle” them out.
Working together in small groups is encouraged during discussion!
split()
Method
split()
helper method.
|
|
|
|
Node
as a parameter, not an IntLinkedList
. Since we are within the IntLinkedList
class, we can operate directly on the underlying linked chain, rather than an encapsulated list. This lets us avoid having to construct many new list objects for the recursive calls, but it also means we lose out on some of the nice CS2110List
methods. In particular, we can't easily (in \(O(1)\) time) determine the size()
of the chain beginning at head
. Instead, we'll locate the midpoint of the list in a clever way:
|
|
|
|
split()
method on the following example list:
while
loop run? To which Node
s will fast
and slow
point at the end of each of these iterations?
merge()
Method
|
|
|
|
head1
and head2
into a single list?
head1
or head2
become the head of the merged list? How do we determine this in general? After we choose the head, how do we decide which node to add next from the two lists as we continue building the merged list?
head1
or head2
run out of nodes first? How can we attach all of the remaining nodes to the merged list?
merge()
method?
merge()
method in IntLinkedList.java
from the starter code. Use the provided tests to check that your implementation is correct.
mergeRecursive()
method. What are the base and recursive cases?
mergeSort()
Method
|
|
|
|
split()
and merge()
helper methods to define the recursive step?
mergeSort()
method and use the provided tests to check that your implementation is correct.
What is the space complexity of merge sort on linked lists, and why is it different from the array-based implementation?
(Hint: What dominates the space complexity in the latter?)