Given the head
of a linked list, return the list after sorting it in ascending order.
Example 1:
Input: head = [4,2,1,3] Output: [1,2,3,4]
Example 2:
Input: head = [-1,5,3,4,0] Output: [-1,0,3,4,5]
Example 3:
Input: head = [] Output: []
Constraints:
[0, 5 * 104]
.-105 <= Node.val <= 105
Follow up: Can you sort the linked list in O(n logn)
time and O(1)
memory (i.e. constant space)?
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach to sorting is like trying every single possible order of the items you want to sort. We check each of these orders to see if it is correctly sorted. If not, we move to the next possibility until a sorted order is found.
Here's how the algorithm would work step-by-step:
import itertools
def is_sorted(possible_permutation):
for i in range(len(possible_permutation) - 1):
if possible_permutation[i] > possible_permutation[i + 1]:
return False
return True
def sort_list_brute_force(input_list):
# Generate all possible permutations of the input list.
all_permutations = itertools.permutations(input_list)
for possible_permutation in all_permutations:
# Check if current permutation is sorted.
if is_sorted(possible_permutation):
# If sorted, return the sorted permutation as a list.
return list(possible_permutation)
# If no sorted permutation is found, return the original list (should not happen).
return input_list
To efficiently sort a linked list, we can apply the divide-and-conquer strategy. This means repeatedly splitting the list into smaller sublists, sorting each sublist, and then merging them back together in sorted order. This approach avoids unnecessary comparisons and rearrangements, resulting in faster performance.
Here's how the algorithm would work step-by-step:
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def sort_list(head):
if not head or not head.next:
return head
# Function to find the middle of the linked list
def get_middle(head_node):
slow_pointer = head_node
fast_pointer = head_node.next
while fast_pointer and fast_pointer.next:
slow_pointer = slow_pointer.next
fast_pointer = fast_pointer.next.next
return slow_pointer
# Function to merge two sorted linked lists
def merge(list_one, list_two):
dummy_node = ListNode()
tail = dummy_node
while list_one and list_two:
if list_one.val < list_two.val:
tail.next = list_one
list_one = list_one.next
else:
tail.next = list_two
list_two = list_two.next
tail = tail.next
if list_one:
tail.next = list_one
elif list_two:
tail.next = list_two
return dummy_node.next
middle = get_middle(head)
head_next = middle.next
middle.next = None
# Recursively sort both halves
left_side = sort_list(head)
right_side = sort_list(head_next)
# Merging the two sorted sublists
sorted_list = merge(left_side, right_side)
return sorted_list
#Test
# Create a linked list: 4 -> 2 -> 1 -> 3
head = ListNode(4)
head.next = ListNode(2)
head.next.next = ListNode(1)
head.next.next.next = ListNode(3)
# Sort the linked list
sorted_head = sort_list(head)
# Print the sorted linked list
#while sorted_head:
# print(sorted_head.val, end=" " )
# sorted_head = sorted_head.next
Case | How to Handle |
---|---|
Null or empty list input | Return null or an empty list immediately to avoid NullPointerExceptions or unnecessary processing. |
List with only one element | Return the original list as it is already sorted. |
List with two elements, already sorted | Handle this case correctly by comparing the two elements and returning them in the same order. |
List with two elements, reverse sorted | Swap the elements to achieve correct sorting and return the new list. |
List with all identical elements | Ensure the sorting algorithm handles this without infinite loops or stack overflows; quicksort is known to have quadratic complexity with all equal elements. |
List with a large number of elements exceeding memory limits (consider external sorting) | If the dataset is too large, use an external merge sort that sorts chunks of data and merges the sorted chunks. |
List containing negative numbers, positive numbers and zero | The sorting algorithm should handle mixed signs correctly as integers can be compared directly. |
List with integer overflow if summing during comparison | Use subtraction for comparisons to avoid overflow, e.g., a < b is better expressed as a - b < 0. |