0

So I understand how insertion sort works but trying to apply the concept to doubly linked lists fizzles out my brain. Can someone please explain how the algorithm works in this context? I cannot wrap my head around how the node pointers change after each node-by-node comparison, given a pre-existing linked list. I am currently working in Java and referring to this code-example: https://www.geeksforgeeks.org/insertion-sort-doubly-linked-list/

Below are two functions, sortedInsert and insertionSort, where the former is helper function.

What is the else clause doing in sortedInsert? Also why does the author "removing all links so as to create current" in the insertionSort function?

// function to insert a new node in sorted way in
// a sorted doubly linked list
static Node sortedInsert(Node head_ref, Node newNode)
{
 Node current;
 
 // if list is empty
 if (head_ref == null)
 head_ref = newNode;
 
 // if the node is to be inserted at the beginning
 // of the doubly linked list
 else if ((head_ref).data >= newNode.data)
 {
 newNode.next = head_ref;
 newNode.next.prev = newNode;
 head_ref = newNode;
 }
 
 else
 {
 current = head_ref;
 
 // locate the node after which the new node
 // is to be inserted
 while (current.next != null &&
 current.next.data < newNode.data)
 current = current.next;
 
 //Make the appropriate links /
 
 newNode.next = current.next;
 
 // if the new node is not inserted
 // at the end of the list
 if (current.next != null)
 newNode.next.prev = newNode;
 
 current.next = newNode;
 newNode.prev = current;
 }
 return head_ref;
}
 
// function to sort a doubly linked list using insertion sort
static Node insertionSort(Node head_ref)
{
 // Initialize 'sorted' - a sorted doubly linked list
 Node sorted = null;
 
 // Traverse the given doubly linked list and
 // insert every node to 'sorted'
 Node current = head_ref;
 while (current != null)
 {
 
 // Store next for next iteration
 Node next = current.next;
 
 // removing all the links so as to create 'current'
 // as a new node for insertion
 current.prev = current.next = null;
 
 // insert current in 'sorted' doubly linked list
 sorted=sortedInsert(sorted, current);
 
 // Update current
 current = next;
 }
 
 // Update head_ref to point to sorted doubly linked list
 head_ref = sorted;
 
 return head_ref;
}
trincot
356k37 gold badges278 silver badges337 bronze badges
asked Jul 19, 2021 at 5:10
2
  • 1
    Please provide a concrete (small) example, and at which stage you have an issue with the algorithm. Commented Jul 19, 2021 at 6:40
  • Added an example and where I'm struggling Commented Jul 19, 2021 at 7:33

1 Answer 1

1

What is the else clause doing in sortedInsert?

The first two blocks (before the else) deal with two boundary cases:

  • What to do when the list is empty
  • What to do if the new node must be inserted before the first node of the list.

So the else block is dealing with all other cases, i.e. where the new node will not be the new head of the list, but the current head node will remain what it was.

It first finds a node (current) for which the next one holds a value that should come after the new node's value (or alternatively, it has no next node following it). By consequence (and because of the previous boundary case), we then know that the current node's value should come before the new node's value. So if we find such a current, we know that the new node must be inserted between current and current.next.

In short, the while loop finds the spot where to insert newNode. This is a typical phase in any insertion-sort algorithm.

The section that is commented "make the appropriate links" will then make up to 4 rewiring assignments.

Here is an example. Let's say the linked list has 3 values 10, 20 and 30 at the moment sortedInsert is called with a new node that has value 25:

 head_ref
 ↓
┌───────────┐ ┌───────────┐ ┌───────────┐
│data: 10 │ │data: 20 │ │data: 30 │
│next: ────────> │next: ────────> │next: null │
│prev: null │ │prev: ─┐ │ │prev: ─┐ │ 
└───────────┘ └───────│───┘ └───────│───┘
 ^ │ ^ │
 └─────────────┘ └─────────────┘
┌───────────┐ 
│data: 25 │
│next: null │
│prev: null │
└───────────┘
 ↑
 newNode

Because head_ref is not null and head_ref.data < newNode.data, we get in the else block where current is defined. The while loop will iterate once, and make the current reference stop at this point:

 head_ref current
 ↓ ↓
┌───────────┐ ┌───────────┐ ┌───────────┐
│data: 10 │ │data: 20 │ │data: 30 │
│next: ────────> │next: ────────> │next: null │
│prev: null │ │prev: ─┐ │ │prev: ─┐ │ 
└───────────┘ └───────│───┘ └───────│───┘
 ^ │ ^ │
 └─────────────┘ └─────────────┘

Now current.next.data >= newNode.data and so we have found the insertion point for newNode.

The first rewiring is done with newNode.next = current.next, which results in this:

 head_ref current
 ↓ ↓
┌───────────┐ ┌───────────┐ ┌───────────┐
│data: 10 │ │data: 20 │ │data: 30 │
│next: ────────> │next: ────────> │next: null │
│prev: null │ │prev: ─┐ │ │prev: ─┐ │ 
└───────────┘ └───────│───┘ └───────│───┘
 ^ │ ^ │ ^
 └─────────────┘ └─────────────┘ │
 │
 ┌───────────┐ │
 │data: 25 │ │
 │next: ────────────┘
 │prev: null │
 └───────────┘
 ↑
 newNode

The next rewiring only happens when current is not the tail node: newNode.next.prev = newNode, which is our case:

 head_ref current
 ↓ ↓
┌───────────┐ ┌───────────┐ ┌───────────┐
│data: 10 │ │data: 20 │ │data: 30 │
│next: ────────> │next: ────────> │next: null │
│prev: null │ │prev: ─┐ │ │prev: ─┐ │ 
└───────────┘ └───────│───┘ └───────│───┘
 ^ │ │ ^
 └─────────────┘ │ │
 │ │
 ┌───────────┐ │ │
 │data: 25 │ <──┘ │
 │next: ────────────┘
 │prev: null │
 └───────────┘
 ↑
 newNode

At this stage, the new node is correctly linked with the node that should follow it (the one with value 30). Now remain two more assignments to establish the link between the node that should precede the new node (the one with value 20). First assignment is current.next = newNode, which results in:

 head_ref current
 ↓ ↓
┌───────────┐ ┌───────────┐ ┌───────────┐
│data: 10 │ │data: 20 │ │data: 30 │
│next: ────────> │next: ────────┐ │next: null │
│prev: null │ │prev: ─┐ │ │ │prev: ─┐ │ 
└───────────┘ └───────│───┘ │ └───────│───┘
 ^ │ │ │ ^
 └─────────────┘ │ │ │
 v │ │
 ┌───────────┐ │ │
 │data: 25 │ <─┘ │
 │next: ───────────┘
 │prev: null │
 └───────────┘
 ↑
 newNode

And finally the rewiring is completed with newNode.prev = current:

 head_ref current
 ↓ ↓
┌───────────┐ ┌───────────┐ ┌───────────┐
│data: 10 │ │data: 20 │ │data: 30 │
│next: ────────> │next: ────────┐ │next: null │
│prev: null │ │prev: ─┐ │ │ │prev: ─┐ │ 
└───────────┘ └───────│───┘ │ └───────│───┘
 ^ │ ^ │ │ ^
 └─────────────┘ │ │ │ │
 │ v │ │
 │ ┌───────────┐ │ │
 │ │data: 25 │ <─┘ │
 │ │next: ───────────┘
 │ │prev: ─┐ │
 │ └───────│───┘
 └─────────┘
 ↑
 newNode

This is no different then:

 head_ref current newNode
 ↓ ↓ ↓
┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐
│data: 10 │ │data: 20 │ │data: 25 │ │data: 30 │
│next: ────────> │next: ────────> │next: ────────> │next: null │
│prev: null │ │prev: ─┐ │ │prev: ─┐ │ │prev: ─┐ │ 
└───────────┘ └───────│───┘ └───────│───┘ └───────│───┘
 ^ │ ^ │ ^ │ 
 └─────────────┘ └─────────────┘ └─────────────┘

The caller gets back the head reference head_ref, which actually didn't change. It would only change if the new node became the first node in the list, which is what the first two if blocks deal with.

Also why does the author "removing all links so as to create current" in the insertionSort function?

This is just a clean way to extract the node from the input list: it is no longer a part of it, and is ready to become part of the new list (sorted). Technically this is not necessary for the case described above, since there both the prev and next members of newNode get new references assigned anyway, but it remains important for the first two boundary cases, as there we do not assign null values where they are actually needed.

Alternatively, you could assign those null references in sortedInsert.

Hope this clarifies it.

answered Jul 19, 2021 at 8:53

1 Comment

Thank you for the full answer, got it now!

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.