Swapping adjacent elements of a linked list algorithm - java

I am practicing linked list programming questions in java, i have a working solution for the following question but cannot understand how it works.
I have commented beside each line what I think should be happening but obviously I haven't grasped how these are working yet, could someone please explain where my comments are wrong and how this solution is correct.(in my comments i use h for head, s for slow etc.)
Given a linked list, swap every two adjacent nodes and return its head.
Example:
Given 1->2->3->4, you should return the list as 2->1->4->3.
public Node s(Node head) {
// 1(h)-2-3-4 passed in
// if only 1 node or null node return it
if (head == null || head.next == null) {
return head;
}
Node slow = head.next; // 1h-2s-3-4
head.next = head.next.next; // 1h-3-4
slow.next = head; // 2s-1h-3-4
head = slow; // 2s/h-1-3-4
Node parent = slow.next; // 1p-3-4
slow = slow.next.next; // 3s-4
while (slow != null && slow.next != null) {
Node temp = slow.next; // 4t-null
slow.next = slow.next.next; // 3s-null
temp.next = slow; // 4t-3s-null
parent.next = temp; // 1p-4-3
parent = parent.next.next; // 3p=null
slow = slow.next; // 4-null, loop ends cause next to slow is null
}
return head; // ( head = slow from earlier) 4-null
}

Let's assume a linked list of A -> B -> C -> D.
I've numbered the lines in your code to make it easier to talk about.
1 public Node s(Node head) {
2 // if only 1 node or null node return it
3 if (head == null || head.next == null) {
4 return head;
5 }
6
7 Node slow = head.next;
8 head.next = head.next.next;
9 slow.next = head;
10 head = slow;
11 Node parent = slow.next;
12 slow = slow.next.next;
13
14 while (slow != null && slow.next != null) {
15 Node temp = slow.next;
16 slow.next = slow.next.next;
17 temp.next = slow;
18 parent.next = temp;
19 parent = parent.next.next;
20 slow = slow.next;
21 }
22 return head;
23 }
At line 7, slow is made to point to node B. head.next is set to B's successor, C on line 8. On line 9, B points to A, and on line 10, head points to B. My comments show what happened.
7 Node slow = head.next; // slow = B
8 head.next = head.next.next; // head.next = C
9 slow.next = head; // B.next = A (because head points to A)
10 head = slow; // head = B
That code swapped the first two nodes. Your list now looks like this:
B -> A -> C -> D
Now the code gets kind of confusing, largely due to poor naming. slow currently points to B.
11 Node parent = slow.next; // parent = A
12 slow = slow.next.next; // slow = C
Remember that slow now points to C. Here's what happens next:
14 while (slow != null && slow.next != null) {
15 Node temp = slow.next; // temp = D
16 slow.next = slow.next.next; // C.next = D.next (which is null)
17 temp.next = slow; // D.next = C
18 parent.next = temp; // A.next = D
At this point, nodes C and D have been swapped, and A points to D, as required. The list now looks like B -> A -> D -> C.
The final two lines in the loop just set things up for next time. Remember, that right now, parent points to A.
19 parent = parent.next.next; // parent = C
20 slow = slow.next; // slow = null
Looping back to the top, we see that slow == null, so the loop exits.
Whereas the code you posted works, it's unnecessarily confusing. There's no need to do a special swap of the first two nodes before going into the loop, and variable names could be more descriptive.
To swap two nodes, you have to make the second point to the first, and the first point to the second's successor. To do that, you have to save the second's successor before you overwrite it. For example, if you have A -> B -> C and you want B -> A -> C, then you have to do this, assuming that head points to A:
firstNode = head // firstNode points to A
secondNode = firstNode.next // secondNode points to B
secondNodeSuccessor = secondNode.next // this points to C
secondNode.next = firstNode // B now points to A
firstNode.next = secondNodeSuccessor // A now points to C
head = secondNode // and head points to B
At this point, secondNodeSuccessor is pointing to C, which is the next firstNode.
With that understanding of how to swap nodes, you can simplify the code quite a bit:
public Node s(Node head) {
// if fewer than 2 nodes, return.
if (head == null || head == null) {
return head;
}
// we know that the new head will be the second node.
Node firstNode = head;
Node parentNode = null;
while (firstNode != null && firstNode.next != null) {
Node secondNode = firstNode.next;
Node secondNodeSuccessor = secondNode.next;
// swap the nodes
secondNode.next = firstNode;
firstNode.next = secondNodeSuccessor;
if (parentNode != null) {
// This links the previous node (the one right before
// the two that we just swapped) to the swapped nodes.
parentNode.next = secondNode;
}
// the new parent node is the last swapped node.
parentNode = firstNode;
firstNode = firstNode.next; // set up for next pair
}
return head.next;
}
Note the improvements here:
I eliminated the special-case swap of the first two nodes, which simplifies things by making every swap the same.
Meaningful variable names make it plain which node I'm referencing.
Eliminating the .next.next construction makes it easier to reason about the code, and also makes it easier to determine whether the code could potentially dereference a null.
Your debugger is a very useful tool for understanding how your code is working. If you were to single-step the code in your debugger, you could examine the variables and see how each line of code affects the state. If you don't know how to use your debugger, you should take the time right now to learn. It will save you hours of debugging, and also greatly increase your understanding of how code works.

In place of swapping nodes, we can swap data only that will be easy and will get the desired output.
public Node s(Node head) {
if (head == null || head.next == null) {
return head;
}
Node temp = head;
/* Traverse only till there are atleast 2 nodes left */
while (temp != null && temp.next != null) {
/* Swap the data */
int k = temp.data;
temp.data = temp.next.data;
temp.next.data = k;
temp = temp.next.next;
}
return head;
}

The other two solutions either don't match your requirements or provide wrong results for some inputs.
I propose an alternative approach that I have tested on LeetCode (if you want to test it yourself, please be sure to rename the Node type into ListNode).
I hope the comments I added to the code are clear enough. When in doubt, I suggest trying to execute this procedure in an interactive debugger.
public ListNode s(Node head) {
// if the list is empty or it's a singleton, no node needs to be swapped
if (head == null || head.next == null) {
return head;
}
// first and second are the first and second node of the current pair to swap
Node first = head;
Node second = head.next;
// parent is the node immediately before the current pair.
// Initially, there is no such pair
Node parent = null;
// the updated list starts from the second node of the first pair
head = second;
// iterate until there is a valid pair to swap
while (first != null && second != null) {
// swap the two nodes of the current pair
first.next = second.next;
second.next = first;
if (parent != null) {
// attach the second element to the updated node of the previous pair
parent.next = second;
}
// keep the invariant of parent valid: parent precedes the new pair to swap,
// if such a pair exists
parent = first;
// advance the pointers of the first and second elements of the new pair to swap
first = first.next;
second = (first == null) ? null : first.next;
}
return head;
}

Related

How does this Linked list Sort solution work?

Im working on some leetcode question and I dont understand this solution and cant find an answer there so I need some clarification please. The quesition is to sort a linked list. This solution uses a merge sort approach. The solution is below
class Solution {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null)
return head;
ListNode mid = getMid(head);
ListNode left = sortList(head);
ListNode right = sortList(mid);
return merge(left, right);
}
ListNode merge(ListNode list1, ListNode list2) {
ListNode dummyHead = new ListNode();
ListNode tail = dummyHead;
while (list1 != null && list2 != null) {
if (list1.val < list2.val) {
tail.next = list1;
list1 = list1.next;
tail = tail.next;
} else {
tail.next = list2;
list2 = list2.next;
tail = tail.next;
}
}
tail.next = (list1 != null) ? list1 : list2;
return dummyHead.next;
}
ListNode getMid(ListNode head) {
ListNode midPrev = null;
while (head != null && head.next != null) {
midPrev = (midPrev == null) ? head : midPrev.next;
head = head.next.next;
}
ListNode mid = midPrev.next;
midPrev.next = null;
return mid;
}
}
So in the sortList method on the line
ListNode left = sortList(head);
how does this assignment work? Head is not incremented so on each recursive call wouldnt it just call the method over and over again on the same value?
I've walked through this by hand and heres where i get stuck.
5 -> 3 -> 4
SortList(5)
head = 5
mid = getMid(5) == 3
getMid(5)
mid prev = null -> 5
head = 5 -> 4
mid = 3
return 3
left = sortList(5)
mid = getMid(5) = 3
left = sortList(5)
mid = sortList(5) = 3
left = sortList(5)
As you can see theres always a call to sortList(5) for every assignment to left. I'm pretty decent and leetcode but this question is really stumping me. All help is much appreciated!
It works because getMid(head) splits the list into two sublists.
After the first call to getMid() you have the two sublists:
head: 5
mid: 3 -> 4
Calling sortList(head) on the first sublist returns immediately (because head.next is null)
Calling sortList(mid) with the second sublist splits that list into two further sublists:
head': 3
mid': 4
Calling sortList on both sublists also returns immediately (because in both sublists head.next is null).
The code then proceeds to merge head' and mid' which results in 3 -> 4.
After that the list head and the result of the previous merge (3 -> 4) are merged together, which gives the final result:
3 -> 4 -> 5
I try to explain in a bit more detail with some pictures.
This is the list on the first call to sortList(head):
ListNode ListNode ListNode
head -> next -> next -> next -> null
val: 5 val: 3 val: 4
getMid(head) changes the list and returns a reference to the middle element:
ListNode
head -> next -> null
val: 5
ListNode ListNode
mid -> next -> next -> null
val: 3 val: 4
That is, although the reference head itself did not change, the list it references changed because head.next changed!
What happens if we call sortList(head) with this reduced list?
The first statement in sortList(head) is
if (head == null || head.next == null)
return head;
Calling sortList(head) (second level) with the reduced list
ListNode
head -> next -> null
val: 5
has head.next == null, so it returns immediately the value of head and in the caller results in left being set to the value of head.
The next line in the first level of the sortList() call is ListNode right = sortList(mid);.
Calling sortList(head) (second level) with the split of list
ListNode ListNode
mid -> next -> next -> null
val: 3 val: 4
What was mid in the first level is now called head (I call it head' to distinguish it from the head in the first level)
ListNode ListNode
head' -> next -> next -> null
val: 3 val: 4
getMid(head) again changes the list and returns a reference to the middle element:
ListNode
head' -> next -> null
val: 3
ListNode
mid' -> next -> null
val: 4

Binary Tree branch sums without recursion

I am trying to calculate a sum of each branch of a binary tree without using recursion. I'm trying to use a stack and can't figure out how to fix my code to get the right sums.
public static List<Integer> branchSums(BinaryTree root) {
LinkedList<BinaryTree> toVisit = new LinkedList<>();
BinaryTree current = root;
List<Integer> sums = new ArrayList<>();
int sum = 0;
while (current != null || !toVisit.isEmpty()) {
while (current != null) {
sum += current.value;
toVisit.push(current);
current = current.left;
}
current = toVisit.pop();
// if found leaf add sum to results and decrement sum by current node
if (current.left == null && current.right == null) {
sums.add(sum);
sum -= current.value;
}
current = current.right;
}
return sums;
}
Example input:
1
/ \
2 3
/ \ / \
4 5 6 7
/ \ /
8 9 10
Example output [15, 16, 18, 10, 11]
Issue with your code is you are not keeping track of the node which
has been last popped from your stack.
Here is the updated code:
public static List<Integer> caculateSum(BinaryTree root) {
List<Integer> sums = new ArrayList<>();
int sum=0;
BinaryTree current = root, popped=null;
Stack<BinaryTree> s = new Stack<BinaryTree>();
while(current!=null ) {
//checking if last node popped from stack is not equal to left or right node of current node
if(popped==null||((current.left!=null && !current.left.equals(popped)) && (current.right!=null && !current.right.equals(popped)))) {
while(current != null) {
sum+=current.value;
s.push(current);
current = current.left;
}
}
current=s.peek();
if(current.right == null) {
//if current node is leaf node
if(current.left == null) {
sums.add(sum);
}
sum-=current.value;
popped = current;
s.pop();
} else if(current.right!=null && current.right.equals(popped)){
//if current node both left and right nodes have been processed
sum-=current.value;
popped = current;
s.pop();
}else {
//if current node right part is not processed
sum+=current.right.value;
s.push(current.right);
}
if(s.isEmpty()) {
break;
}
current=s.peek();
}
return sums;
}
Will explain this by taking an example. Suppose we have given binary tree
1,2,9,3,7,null,8,5
Here in above code apart from old variables a new variable popped is used which keeps track of last element which is popped out from stack.
So, following are the major steps :
Starting from current node first we are checking if current node left is not equal to popped (if it is equal it means that current node left part is already processed so we don't need to process it again). Same we are checking if current node right node is not equal to popped node (if it is equal it means we have already processed right node of current node which indirectly means left node is also processed).
Now for the top node of stack which is current node we check :
If its right node is null If it is true it means either current
node is leaf node or it is an already processed node whose right
node is null (like in our example node with value of 3). If it is
leaf we add it in our sums list. Also, for both cases we remove
this top node and subtract its value from current sum value
(This thing has been done in above code as well) .Along with this we
will keep track of popped element from stack in popped variable.
If its right is not null but its right node is equal to popped
node This happens when in last pass of while loop we had processed
this right node. This means for top node of stack both left and
right node have been processed and hence we pop this node and keep
track of it in popped variable.
Else we push the right node of top element of stack in stack.
At the end for above example , sums variable will store result as [11, 10, 18]
I attempted this for fun and was surprised I didn't see any actual solutions. The following is in Kotlin but can easily be transcribed into Java. The trick was to add state to the Node itself to mark it as consumed before you popped it, otherwise there was no value there to check when going down another branch.
This might be useful in super rare cases to prevent stack overflow? This will still run in O(N) but takes more space with the stacks, and will visit a node twice, once to traverse and once to pop.
open class BinaryTree(value: Int) {
var value = value
var left: BinaryTree? = null
var right: BinaryTree? = null
var consumed: Boolean = false
}
fun branchSums(root: BinaryTree): List<Int> {
var sumList = ArrayList<Int>()
var nodeStack = ArrayList<BinaryTree>()
var valueStack = ArrayList<Int>()
nodeStack.add(root)
while(!nodeStack.isEmpty()) {
val node = nodeStack.get(nodeStack.size-1)
if (node.consumed) {
valueStack.removeAt(valueStack.size - 1)
nodeStack.removeAt(nodeStack.size - 1)
continue
}
valueStack.add(node.value)
if (node.right == null && node.left == null) {
var sum = 0
for (value in valueStack) {
sum += value
}
sumList.add(sum)
}
if (node.right != null) {
nodeStack.add(node.right!!)
}
if (node.left != null) {
nodeStack.add(node.left!!)
}
node.consumed = true
}
return sumList
}
You can have this method:
public static int getBranchSum(Node root){
Queue<Node> q = new LinkedList<>();
q.add(root);
int sum=0;
while (!q.isEmpty()) {
Node curNode = q.poll();
sum+=curNode.data;
if(curNode.left==null || curNode.right==null)
curNode.visited=true;
if(curNode.left != null && curNode.left.visited)
curNode.visited=true;
if(curNode.left!=null && !curNode.left.visited)
q.add(curNode.left);
else if(curNode.right!=null && !curNode.right.visited)
q.add(curNode.right);
}
root.visited=false;
return sum;
}
Then call it below in a while loop as long as the output is not equal the root data.
boolean flag=true;
List<Integer> list = new ArrayList<>();
while(flag){
int result =getBranchSum(root);
if(result == root.data)
flag=false;
else
list.add(result);
}
System.out.println(list);
However the above the working only if we have a visited boolean in the node:
class Node{
Node left,right;
int data;
boolean visited = false;
Node(int data){
this.data=data;
left=right=null;
}
Branch sum without recursion
def branchSums(root):
cs=0
stack=[{"node":root,"cs":cs}]
sums=[]
while(len(stack)>0):
node_info=stack.pop()
node,cs=node_info["node"],node_info["cs"]
if node is None:
continue
cs=cs+node.value
if node.left is None and node.right is None:
sums.append(cs)
print(sums)
stack.append({"node":node.right,"cs":cs})
stack.append({"node":node.left,"cs":cs})
return sums

Circular linked list logic

/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null){
return false;
}
ListNode slow = head;
ListNode fast = head.next;
while((slow != null) && (fast != null) && (slow.next != null) && (fast.next != null)){
if(slow == fast){
return true;
}
slow = slow.next;
fast = fast.next.next;
}
return false;
}
}
For detecting a circular linked list, we use the 2 pointer technique, slow and fast.
My question is, how do I know the pointers must intersect at some point if the list is a circular list?
Look at a watch. That is a circular list of numbers 1 to 12 then circles back to 1.
The big hand is moving fast, the small hand is moving slow, both moving in the same direction, and starting at the same point (top = 12).
Because the list (edge) is circular, the big hand will eventually catch back up to the small hand. How quickly depends on the speed difference, but it will catch up. If it catches up, the list must be circular.
If it doesn't catch up, but gets to end of list, the list is not circular.
Even if the list doesn't circle back to the beginning, e.g. if 12 circled back to 9, the fast hand would just keep circling, until the small hand enters the circle, and then the fast hand will eventually catch up to the small hand.
Ok, for that last part, the image of a watch wasn't good, but I hope you got the point.
The proof is not as obvious as it might seem.
Actually, with a little change that would make the fast pointer even faster, e.g. using fast = fast.next.next.next, the algorithm is no longer guaranteed to work.
What matters is the relative speed of the two pointers.
In the standard case, the relative speed is 2 - 1 = 1, which means that at each step, the fast pointer gets one unit closer to the slow pointer. In this way, it is guaranteed that the fast one will catch up and not jump over the other.
Otherwise, e.g. if the relative speed is 3 - 1 = 2, then it is possible for them to never intersect. This would occur if we start with an odd distance between the pointers, and the cycle length is even. In this case, the distance always will always remain odd (thus it will never be zero).
To make it clear that the pointers may not intersect if not being careful about the speed difference, consider a fast pointer with speed 3, and a slow pointer with speed 1, running in a cycle with 4 nodes, labeled 0, 1, 2, 3, forming a cycle like this 0 -> 1 -> 2 -> 3 -> 0.
Assume that initially, the slow pointer is at node 0 and the fast pointer is at node 1. (note that this is not a strong assumption, and may not be alleviated by a different initialization strategy -- regardless of the initialization method, it might be the case that there are some additional nodes in the graph, not part of the cycle, making it possible for the pointers to be in arbitrary positions once they both reach the cycle).
After k steps, the slow pointer will be at node k mod 4. The fast node will be at node (1 + 3k) mod 4. Assuming there is a k such that the fast and slow pointer are at the same position, it means (1 + 3k) mod 4 = k mod 4, i.e. (1 + 2k) mod 4 = 0. But the left hand side is an odd number, thus it can not be zero. Therefore, the assumption that the pointers can point at the same node lead to a contradiction.
Well, as andreas mentioned look at the watch but if that still doesn't make sense make this could help.
Also, you can simplify your code a bit:
public boolean isCyclic(Node first) {
if(first == null) {
return false;
}
Node fast = first;
Node slow = first;
while(fast.getNext() != null && fast.getNext().getNext() != null) {
slow = slow.getNext();
fast = fast.getNext().getNext();
if(slow == fast) {
return true;
}
}
return false;
}
Code copied from http://codecramp.com/linked-list-loop-detection/
I also think that you should initialize your fast pointer with head instead of head.next
Here is the complete implementation of circular linked list in C:
#include <stdio.h>
#include <stdlib.h>
struct node {
int data;
struct node* next;
};
void insert(struct node** head,int ele){
struct node* temp = (struct node*)malloc(sizeof(struct node));
struct node* ptr;
temp->data = ele;
temp->next = temp;
if(*head == NULL){
*head = temp;
}else{
ptr = *head;
while(ptr->next != *head){
ptr = ptr->next;
}
ptr->next = temp;
temp->next = *head;
}
}
void deleteLastEle(struct node** head){
struct node* current = *head;
struct node* pre = *head;
while(current->next != *head){
pre = current;
current = current->next;
}
pre->next = current->next;
free(current);
}
void deleteAtPos(struct node** head,int pos){
struct node* current = *head;
struct node* pre = *head;
for(int i=0;i<pos-1;i++){
pre = current;
current = current->next;
}
pre->next = current->next;
free(current);
}
void deleteFirst(struct node** head){
struct node* current = *head;
struct node* temp = *head;
while(current->next != *head){
current = current->next;
}
current->next = (*head)->next;
*head = (*head)->next;
free(temp);
}
printCLL(struct node** head){
struct node* ptr = *head;
do{
printf("%d ",ptr->data);
ptr = ptr->next;
}while(ptr != *head);
printf("\n");
}
// main() function.
int main(void) {
struct node* head = NULL;
for(int i=0;i<5;i++){
//function to insert elements in linked list
insert(&head,i);
}
printf("inseted linkedlist: \n");
//print the content of linked list.
printCLL(&head);
//function to delete last element
deleteLastEle(&head);
printf("after deleting last element: \n");
printCLL(&head);
//function to delete element at pos 3.
deleteAtPos(&head,3);
printf("after deleting at pos 3: \n");
printCLL(&head);
//function to delete first element of linkedlust
deleteFirst(&head);
printf("after deleting first element: \n");
printCLL(&head);
return 0;
}
And these are the outputs:
inseted linkedlist:
0 1 2 3 4
after deleting last element:
0 1 2 3
after deleting at pos 3:
0 1 3
after deleting first element:
1 3

Why doesn't he following code works in inserting node at a specific postion in a Linked List?

Please take a look at the following code and let me know what's wrong with it? I'm trying to insert a node at a given specific position in a linked list. We need to return the reference to the head node after inserting it.
Node InsertNth(Node head, int data, int position) {
Node newNode = new Node();
newNode.data = data;
if(head==null){
newNode.next = head;
return newNode;
}
Node first = head;
while(position > 0 && head.next!=null){
head = head.next;
position -= 1;
}
newNode.next = head;
head = newNode;
return first;
}
Yeah, there is something wrong. After the while loop, you are trying to insert newNode BEFORE head, but this does not work. The line head=newNode; is useless.
Either you need another pointer to point to the node before head, so that you can insert newNode between these two pointers, or you need to stop the while loop one step before, and insert newNode AFTER head. Here is the second solution:
while(position > 1 && head.next!=null){ //0 is replaced by 1 here
head = head.next;
position -= 1;
}
newNode.next = head.next;
head.next = newNode;
return first;
[EDIT]
In this solution, you need to handle the special case when position equals 0 by adding the following code just before the while loop:
if(position==0) {
newNode.next = head.next;
return newNode;
}

References to nodes in Java linked list

I need some help understanding how a method works.
I have a basic Node class defined like this:
class Node {
Node next = null;
int data;
public Node(int d){
data = d;
}
}
Now I'm looking at how this deleteDuplicates method is working. I understand that we are passing through each node iteratively and storing its value in a set. If the value is already in the set, I believe we are setting the previous node's next pointer to skip the current node. Here's the method:
public static Node deleteDuplicates(Node head){
Node n = head;
HashSet<Integer> set = new HashSet<Integer>();
Node previous = null;
while(n != null) {
if (set.contains(n.data)){
// skip this node
previous.next = n.next;
}
else {
set.add(n.data);
previous = n;
}
n = n.next;
}
return head;
}
I'm confused about the variables previous and n. When we set previous = n;, isn't that making them reference the same object? If they reference the same object, one change you make to n will be the same in previous. So how does the line previous.next = n.next; work?
Thanks!
read these 2 lines together,
previous = n;
n = n.next;
So, once a node is processed, the pointers for previous and n are moved forward. n is next node after previous, thus previous is set to n, and n is moved to its next node, which is n.next
For the deletion part, hope the diagram below helps

Categories

Resources