Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Linked List Operations: Swapping Adjacent Nodes, Removal, Intersection, and Cycle Detection

Tech May 14 1

Swapping Adjacent Nodes in a Linked List

The core idea involves using a dummy head node to simplify edge cases and ensure consistent operations on the first node. When swapping node pairs, careful attention must be paid to preserving references before reassigning pointers.

Key considerations:

  • Use a dummy head node to eliminate special treatment for the head
  • Store referecnes to all nodes involved before updating pointers
  • Maintain correct order when updating pointers to avoid losing node references
type ListNode struct {
    Val  int
    Next *ListNode
}

func swapPairs(head *ListNode) *ListNode {
    if head == nil || head.Next == nil {
        return head
    }
    
    dummy := &ListNode{}
    dummy.Next = head
    
    current := dummy
    
    for current.Next != nil && current.Next.Next != nil {
        first := current.Next
        second := current.Next.Next
        
        // Perform the swap
        current.Next = second
        first.Next = second.Next
        second.Next = first
        
        // Move to next pair
        current = current.Next.Next
    }
    
    return dummy.Next
}

Removing the Nth Node from the End

This problem requires finding and removing a node at a specific position from the end. The optimal approach uses a sliding window technique with two pointers.

Key considerations:

  • Use a dummy head to handle edge cases uniformly
  • Create a fixed gap between fast and slow pointers
  • Move fast pointer n+1 steps ahead to locate the predecessor of the target node
  • The value of n ranges from 1 to the list length
func removeNthFromEnd(head *ListNode, n int) *ListNode {
    dummy := &ListNode{}
    dummy.Next = head
    
    fast := dummy
    slow := dummy
    
    // Move fast pointer n+1 steps ahead
    gap := n + 1
    for gap > 0 && fast != nil {
        fast = fast.Next
        gap--
    }
    
    // Move both pointers until fast reaches end
    for fast != nil {
        fast = fast.Next
        slow = slow.Next
    }
    
    // Remove the target node
    slow.Next = slow.Next.Next
    
    return dummy.Next
}

Finding Intersection of Two Linked Lists

Two linked lists intersect when they share the same node reference (not just the same value). If they intersect, they must align at their tails.

Key observations:

  • Compare node references, not values
  • Aligned tails imply intersection
  • Calculate lengths and align the starting positions
func getIntersectionNode(headA, headB *ListNode) *ListNode {
    // Calculate lengths
    ptrA := headA
    ptrB := headB
    lenA, lenB := 0, 0
    
    for ptrA != nil {
        lenA++
        ptrA = ptrA.Next
    }
    
    for ptrB != nil {
        lenB++
        ptrB = ptrB.Next
    }
    
    // Reset pointers
    ptrA = headA
    ptrB = headB
    
    // Ensure ptrA is the longer list
    if lenA < lenB {
        ptrA, ptrB = ptrB, ptrA
        lenA, lenB = lenB, lenA
    }
    
    // Align to same starting position from end
    diff := lenA - lenB
    for diff > 0 {
        ptrA = ptrA.Next
        diff--
    }
    
    // Find intersection point
    for ptrA != nil {
        if ptrA == ptrB {
            return ptrA
        }
        ptrA = ptrA.Next
        ptrB = ptrB.Next
    }
    
    return nil
}

Detecting Cycle Start in Linked List

Using fast and slow pointers, a cycle can be detected when they meet. The mathematical relationship between distances allows finding the cycle entry point.

Mathematical derivation:

  • When fast and slow meet: fast travels twice the distance of slow
  • Let x = distance from head to cycle start
  • Let y = distance from cycle start to meeting point
  • Let z = cycle length
  • Equation: x + y = (n-1)z + z, simplifying to x = (n-1)(y+z) + z
  • Special case when n=1: x = z
func detectCycle(head *ListNode) *ListNode {
    fast := head
    slow := head
    
    // Find meeting point
    for fast != nil && fast.Next != nil {
        fast = fast.Next.Next
        slow = slow.Next
        
        if fast == slow {
            // Cycle detected, find entry point
            start := head
            meet := fast
            
            for start != meet {
                start = start.Next
                meet = meet.Next
            }
            
            return start
        }
    }
    
    return nil
}

Key Takeaways

Linked lists offer efficient insertions and deletions but slower random access compared to arrays. Important considerations:

  • Dummy head nodes simplify operations at the head position
  • In Go, initialization requires explicit nil assignment after declaration
  • Continuous memory allocation is avoided, providing better memory utilization
  • Pointer manipulation requires careful ordering to prevent losing references

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.